Session Goals
/goal gives a session a success condition. After each agent run, an evaluator judges whether the condition is satisfied. If it isn’t, the agent is automatically prompted to keep going — so you can state an outcome once instead of typing “continue” a dozen times.
/goal "all tests pass and the README is updated" --max-turns 20 --max-cost 5The Loop
Section titled “The Loop”- You set a goal. The condition is sent to the agent immediately as its first directive.
- The agent works — reading, editing, running commands — for as many tool calls as it needs.
- When the agent finishes a run, the evaluator checks the condition.
- Not met → the evaluator’s reasoning is fed back and a new run starts automatically.
- Met, or a budget is exhausted → the goal deactivates and control returns to you.
The loop pauses itself when other work is in flight: if background subagents are running or messages are queued, /goal waits rather than talking over them.
Evaluators
Section titled “Evaluators”| Evaluator | How it decides | Cost |
|---|---|---|
llm (default) | Appends a judge question to the session’s own conversation and asks the session’s model for a yes/no with a reason | One short call, with the conversation served from the provider’s prompt cache |
keyword | Checks whether any --keyword appears in the run’s output | Free, local |
The LLM evaluator is told to judge on evidence — command output, test results, file contents — rather than the agent’s claim that it finished. Ending a run with real output (“42 passed”) resolves a goal; ending it with “the tests should pass now” does not.
How the LLM evaluator reuses your session
Section titled “How the LLM evaluator reuses your session”Rather than assembling a separate transcript, the evaluator re-sends the exact context your session just sent — same system prompt, same tools, same messages — with the judge question appended as one final user message. Providers cache on an exact prefix, so that conversation is served from cache and only the appended question is billed as new input. It also means the judge reads the real conversation at full fidelity, and nothing is shared with a provider your session wasn’t already using.
/goal "docker image builds" --evaluator keyword --keyword "Successfully built"An uncertain verdict — evaluator model unreachable, no credentials, unparseable reply — never auto-continues. The loop stops and hands control back to you, so a broken evaluator can’t spin a session.
Budgets
Section titled “Budgets”Budgets are guardrails, not targets. The first one to trip deactivates the goal.
| Flag | Counts |
|---|---|
--max-turns N | Completed agent runs |
--max-tokens N | Cumulative tokens across every LLM round-trip, including evaluator calls |
--max-cost N | Cumulative USD across every LLM round-trip, including evaluator calls |
Budgets are enforced mid-run too, so a runaway tool loop can’t blow past a token or cost ceiling while waiting for a run to end.
| Flag | Default | Purpose |
|---|---|---|
--max-turns N | none | Stop after N completed agent runs |
--max-tokens N | none | Stop after N cumulative tokens |
--max-cost N | none | Stop after N cumulative USD |
--evaluator llm|keyword | llm | Which evaluator decides |
--keyword "text" | none | Success string for the keyword evaluator (repeatable, required by it) |
--every N | 1 | Evaluate once every N completed runs |
--min-turns N | 1 | Wait N completed runs before the first evaluation |
Budget values must be greater than zero. Text that isn’t a recognized flag is treated as part of the condition, so /goal "fix the --dry-run handling" works as written.
Sub-commands
Section titled “Sub-commands”| Command | Effect |
|---|---|
/goal or /goal status | Show the active goal, counters, and budget |
/goal clear | Cancel the active goal (aliases: stop, off, cancel, reset, none) |
An active goal appears in the TUI status bar and as a badge in the web UI session header, showing the completed run count (against --max-turns if set) and the latest evaluator reason.
Configuration
Section titled “Configuration”Defaults for every goal live under goal in ~/.pizzapi/config.json, or in the web UI under Runner Settings → Fast Model:
{ "goal": { "evaluatorModel": "anthropic:claude-3-5-haiku-20241022", "evaluatorMaxTokens": 512, "evaluateEveryNTurns": 1, "minTurnsBeforeEvaluate": 1 }}There is deliberately no evaluator model picker. The evaluator runs on your session’s model so it can reuse the cached conversation; choosing a different one would silently give that up.
evaluatorModel remains as an advanced escape hatch for the rare case that needs it — pinning a judge (for example a local, fully private model) opts out of cache reuse and sends a standalone, truncated transcript instead. If a pinned model has no working credentials, the evaluator falls back to the cheapest authenticated one rather than silently switching itself off.
Evaluator spend is tracked separately and shows up in the Usage dashboard alongside normal session usage.
See Configuration for the full option reference.
Privacy
Section titled “Privacy”By default the LLM evaluator sends your conversation to the same provider and model the session is already using, so evaluation introduces no new data exposure.