Change how the agent behaves

Goal: change what the agent says, remembers or refuses — without touching the loop.

Work through these in order. The first one that fits is the right one, and the lower you go the more you pay.

Change a tool description or an error message — first choice

Most behaviour problems are tool-surface problems wearing a prompt-shaped disguise. If the agent guesses a path, retries an impossibility, or fails to chain two calls, the fix is almost always in the tool, not in the prompt:

SymptomFix, in the tool
guesses identifierssay in the description where the identifier comes from
retries a refusal foreverthe error must say do not retry
reads a file five timesreturn a hint naming the next call
uses the wrong toolsay in each description what the other one is for

This is cheaper than a prompt rule in a way that compounds: a tool description is paid for once, in the tool list, and it is right next to the thing it describes. A prompt rule is paid for on every turn of every conversation, including the ones where it is irrelevant.

Edit a prompt section — second choice

The system prompt is five files in orchestrator/src/context/sections/, one per section, so editing prose never touches code.

Before adding a rule, check it cannot be an example instead. The three examples in the prompt are its highest-value part: one per behaviour that rules alone do not produce — targeted retrieval, confirmation before an effect, and stopping on a permission refusal. A rule states; an example demonstrates, and models follow demonstrations more reliably.

Do not add: the tool list, argument schemas, or per-tool rules. Those arrive through the protocol or belong in a description.

Change what the model remembers

Facts live in their own store, are read on every later turn, and are wrapped as untrusted data like everything else external.

memory_write is annotated destructive even though it destroys nothing. That is deliberate and should not be "corrected": memory is a persistent instruction channel, so a fact written without the user seeing it would turn a one-shot injection into a permanent backdoor. → why

To purge everything the agent attributed to itself, filter on the recorded source rather than deleting the store — user-supplied facts are not the problem.

Change when the conversation is compacted

Two rungs, both inside the same hook so there is one seam rather than two. If you touch them, three constraints are load-bearing and each fails in its own way:

  1. Cutting between an assistant message and its tool result returns a 400 from the provider.
  2. The summary is reinjected as a user message inside an untrusted envelope, never as a system message.
  3. Never compact between an approval request and its answer — the pending call vanishes and the confirmation lands on nothing.

Which tools' results are considered heavy is injected from the tool registry. The pruner hardcodes no tool name, and keeping it that way is what stops "add a tool" from becoming "edit the context layer".

Change the model

DEFAULT_MODEL in the environment, in the gateway's creator/model form. A provider key set directly takes precedence over the gateway for that provider.

Gateway model ids drift — one disappeared between two releases — so if every turn fails at once, list what the gateway actually offers before debugging anything in this codebase.

What you should not change

The loop. It is about thirty-five lines and should never need to move again. If a task needs a fixed sequence, that sequence is a tool that happens to contain a loop, not a new layer. → Workflows are tools

Where approval comes from. It is derived from tool annotations and fails closed. Three separate ways of "simplifying" it have silently disabled it in the past. → Verified facts

Verify

cd orchestrator && npm test

Then have a real conversation and read what it recorded — a behaviour change that does not show up in the failure kinds probably did not happen. → Follow one message