Agent runtime: majordomo in-process, Ollama Cloud config, chat endpoint (#56)
Everything below the run loop already existed. This is the thing that runs a model. The build tag is gone, deliberately. internal/agent's doc comment promised two separations — cmd/pansy not importing the package, and the tool wiring behind //go:build majordomo — and both have been rewritten rather than left as a stale aspiration. A tag that keeps the agent out of the binary only earns its keep if you would ever ship a build without the agent, and the agent is the point; keeping it meant an untagged CI that never compiled the code that matters. majordomo is a real dependency now, resolved from the Gitea instance as a pseudo-version with no replace directive, so the Docker build (which has no sibling checkout) resolves it the same way this machine does. It is stdlib-first and pure Go, so CGO_ENABLED=0 and the single static binary survive. A TURN IS ONE CHANGE SET. That is the whole reason acting without a confirmation prompt is defensible: "empty the garlic bed and plant cucumbers" is one object edit and a dozen planting inserts, and it has to undo as one action rather than thirteen. The scope is opened even for a turn that turns out to be a question, because a change set with no revisions is never written — so asking costs nothing and history isn't littered with empty entries. The model spec goes to majordomo.Parse verbatim. That grammar, including comma-separated failover chains, is majordomo's; re-implementing any of it here would only mean two places to update when it grows. The key needs a bridge though: majordomo's ollama-cloud preset reads OLLAMA_API_KEY while pansy (like gadfly) is configured with OLLAMA_CLOUD_API_KEY, so the provider is registered explicitly on a private registry rather than depending on ambient environment. Runs are bounded by a step cap, a timeout and majordomo's loop guards. This is loop safety, not cost control — pansy is a personal tool and spend caps are explicitly not a v2 concern. A capped run does NOT fail: it kept whatever it managed to do, that work is recorded and undoable, and the reply says it stopped early rather than going silent. The chat endpoint streams. A turn that clears a bed and replants it makes a dozen tool calls over tens of seconds, and without streaming that is a long silence followed by everything at once — which reads as a hang, and defeats a design that rests on watching the canvas change as it happens. Conversations persist per (user, garden). Client-held history would be lost on a refresh, which is exactly when someone reloads to check whether the agent's change landed. Only the user/assistant TEXT is stored, not the model's full transcript: continuity needs what was said and what came back, and replaying a stored tool call would replay a decision made against a garden that has since moved on. It also keeps majordomo's message shape out of the schema. An instance with no key starts, serves the app, and doesn't advertise the agent — the routes aren't registered at all, the same shape as OIDC 404ing when unconfigured. A configured-but-unresolvable model logs and disables the assistant rather than refusing to boot: a garden planner that won't start because of a chat feature is worse than one without chat. Tool refusals reach the model as tool results it can explain, not 500s. The ACL story only works if it can narrate the refusal. Closes #56 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -63,12 +63,15 @@ All configuration is via environment variables; every value has a default, so `.
|
||||
| `PANSY_OIDC_BUTTON_LABEL` | `Sign in with Authentik` | Label for the OIDC button on the login page. |
|
||||
| `PANSY_TRUSTED_PROXIES` | *(none)* | Comma-separated proxy CIDRs/IPs to trust for client-IP resolution. |
|
||||
|
||||
The garden assistant reads two more. Both are **read only once the assistant lands** ([#56](https://gitea.stevedudenhoeffer.com/steve/pansy/issues/56)); setting them earlier is harmless and setting neither leaves the assistant off.
|
||||
The garden assistant reads three more. Setting none of them leaves the assistant off; the app runs exactly as it does without it.
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ----------------------- | ----------------------------- | --------------------------------------------------------------------------- |
|
||||
| `OLLAMA_CLOUD_API_KEY` | *(empty)* | Ollama Cloud API key. Without it the assistant is disabled, not broken. |
|
||||
| `PANSY_AGENT_MODEL` | `ollama-cloud/glm-5.2:cloud` | Model spec, passed verbatim to `majordomo.Parse` — a comma-separated list is a failover chain. |
|
||||
| Variable | Default | Description |
|
||||
| ----------------------- | ------------------------------ | --------------------------------------------------------------------------- |
|
||||
| `OLLAMA_CLOUD_API_KEY` | *(empty)* | Ollama Cloud API key. Without it the assistant is disabled, not broken — the chat routes simply aren't registered. |
|
||||
| `PANSY_AGENT_MODEL` | `ollama-cloud/glm-5.2:cloud` | Model spec, passed verbatim to `majordomo.Parse` — a comma-separated list is a failover chain, e.g. `ollama-cloud/glm-5.2:cloud,ollama-cloud/kimi-k2.6:cloud`. |
|
||||
| `PANSY_AGENT_ENABLED` | on when a key is present | Turns the assistant off without removing the key. |
|
||||
|
||||
The assistant acts without asking first, which is only reasonable because every turn is one undoable change set — see the History panel in the editor.
|
||||
|
||||
Local email/password auth is live (`POST /api/v1/auth/register`, `/auth/login`, `/auth/logout`, `GET /auth/me`, `GET /auth/providers`); the session is an HttpOnly cookie (`Secure` when `PANSY_BASE_URL` is https). The first account registered becomes admin, and it may register even when `PANSY_REGISTRATION=closed` to bootstrap the instance.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user