# AGENT_OPERATOR v1: how an agent operates gbrain gbrain assumes an AI agent is its operator. Every failure, refusal, degraded result and recommendation gbrain produces (MCP tool results, CLI human output, CLI `--json`, doctor, readiness) carries the same machine contract: a stable `code`, a reason, one next step with who acts and whether the user must agree first, and a read-only way to verify it worked. This page is that contract, `contract_version: 1`. Related pages: [error codes](../guides/error-codes.md) (one row per `code`; offline: `gbrain errors `), [exit codes](../guides/exit-codes.md), [`_meta` channels](MCP_META_CHANNELS.md), [memory verbs](MEMORY_VERBS_v1.md). ## Quick contract 1. Read `code` (fall back to `error` on older servers). `message` says what happened, `why` says why. 2. Follow `fix.next`: - `run` → run `fix.argv` (CLI) or call `fix.mcp` (MCP) exactly as given. - `ask_user` → relay `user_message` to the user and stop; run the fix only after they agree. - `tell_user_to_run` → give the user `fix.command`; it needs them or the brain host's operator. - `wait` → retry after the stated delay with the same request. - `report` → tell the user what happened and run `gbrain doctor --json`. 3. Then run `fix.verify` (always read-only) to confirm the fix worked. 4. Treat `[gbrain notice …]` blocks and `[AGENT]` blocks the same way. A degraded result is not proof of "no notes". ## Transcripts These are generated from the frozen wire goldens in `test/fixtures/agent-contract/v1/` by `bun run build:agent-protocol`; the goldens never store `next`, so the generator recomputes it with the same decision table gbrain uses at render time. Docs URLs on the wire are pinned to the installed version; the transcripts show `master`. Transcripts marked "journey" are recorded from real CLI and stdio MCP runs by the deterministic agent journey (`test/agent-journey.serial.test.ts`); machine values (home directory, pids, ids, times) show as fixed examples. ### 1. A caller mistake over MCP: `run` A harness on stdio MCP (surface `full`) lists pages with a sort key that does not exist. ```text list_pages {"sort":"bogus"} ``` gbrain returns (`isError: true`, the envelope shown parsed): ```json { "error": "invalid_params", "code": "invalid_params", "message": "Unknown sort 'bogus'.", "suggestion": "Use one of: updated, created, title. Next: list_pages {\"sort\":\"updated\"}", "docs": "https://github.com/garrytan/gbrain/blob/master/docs/guides/error-codes.md#invalid_params", "fix": { "argv": [ "gbrain", "list", "--sort", "updated", "--brain", "host", "--source", "default" ], "command": "gbrain list --sort updated --brain host --source default", "mcp": { "tool": "list_pages", "arguments": { "sort": "updated" } }, "consent": [], "actor": "agent", "next": "run", "why": "updated is the default sort.", "verify": { "mcp": { "tool": "list_pages", "arguments": { "sort": "updated", "limit": 1 } } }, "requires_exclusive": false }, "docs_cmd": [ "gbrain", "errors", "invalid_params" ], "class": "caller", "retryable": false, "contract_version": 1 } ``` The result is one content block. The agent reads `code: invalid_params` and `fix.next: run`, so it calls `list_pages {"sort":"updated"}` itself, then confirms with the read-only `list_pages {"sort":"updated","limit":1}`. Nothing needs the user. ### 2. Paid work without approval on the CLI: `ask_user`, exit 3 An agent runs remediation from a non-interactive shell without `--yes` or `--max-usd`. ```bash gbrain doctor --remediate --json ``` stdout (`--json`): ```json { "status": "confirmation_required", "error": "confirmation_required", "code": "confirmation_required", "message": "doctor --remediate needs the user's approval before it runs; nothing was changed.", "suggestion": "Ask the user: Fixing your brain's health will cost about $0.40 in embedding calls. OK to proceed? If they agree, run: gbrain doctor --remediate --max-usd 0.60 --yes --brain host", "effects": [ "paid" ], "actor": "agent", "why": "Remediation re-embeds 120 stale pages through the configured provider.", "risk": "Spends up to the cap on embedding calls; no data is deleted.", "est_usd": 0.4, "user_message": "Fixing your brain's health will cost about $0.40 in embedding calls. OK to proceed?", "fix": { "argv": [ "gbrain", "doctor", "--remediate", "--max-usd", "0.60", "--yes", "--brain", "host" ], "command": "gbrain doctor --remediate --max-usd 0.60 --yes --brain host", "consent": [ "paid" ], "actor": "agent", "next": "ask_user", "why": "Remediation re-embeds 120 stale pages through the configured provider.", "user_message": "Fixing your brain's health will cost about $0.40 in embedding calls. OK to proceed?", "requires_exclusive": false, "preview_argv": [ "gbrain", "doctor", "--remediation-plan", "--json", "--brain", "host" ] }, "preview": { "argv": [ "gbrain", "doctor", "--remediation-plan", "--json", "--brain", "host" ], "command": "gbrain doctor --remediation-plan --json --brain host" }, "preapprove_argv": [ "gbrain", "config", "set", "consent.preapprove.paid.max_usd_per_run", "" ], "docs_cmd": [ "gbrain", "errors", "confirmation_required" ], "contract_version": 1 } ``` Without `--json`, the same refusal prints as an `[AGENT]` block: ```text [AGENT] ask: Fixing your brain's health will cost about $0.40 in embedding calls. OK to proceed? why: Remediation re-embeds 120 stale pages through the configured provider. risk: Spends up to the cap on embedding calls; no data is deleted. consent: paid actor: agent next: ask_user if_yes: gbrain doctor --remediate --max-usd 0.60 --yes --brain host — To stop asking for runs under a limit the user picks: gbrain config set consent.preapprove.paid.max_usd_per_run '' if_no: Nothing runs; nothing was changed. To look first (read-only): gbrain doctor --remediation-plan --json --brain host [SHOW USER] Fixing your brain's health will cost about $0.40 in embedding calls. OK to proceed? [/SHOW USER] [/AGENT] ``` Nothing ran and the command exited 3. The agent reads `fix.next: ask_user`, relays `user_message` ("Fixing your brain's health will cost about $0.40 in embedding calls. OK to proceed?") and stops. It may offer the read-only preview `gbrain doctor --remediation-plan --json --brain host`. Only after the user agrees does it run `gbrain doctor --remediate --max-usd 0.60 --yes --brain host`. Passing `--yes` on its own would be a consent violation even though gbrain cannot tell the difference. ### 3. An empty recall with a notice: `tell_user_to_run` A harness on stdio MCP searches a keyless brain and nothing matches. ```text search {"query":"…"} ``` gbrain returns these content blocks: `content[0]`: ```json [] ``` `content[1]`: ```text [gbrain notice empty_retrieval kind=info] why: No pages matched. Search ran keyword-only because embeddings are not configured. fix: gbrain doctor --json --brain host next: tell_user_to_run user_message: I found no notes on that topic. Your brain searches keywords only right now. ``` `content[0]` is still the bare array `[]`; the second block is the notice. The agent does not tell the user "you have no notes on that". It relays `user_message` ("I found no notes on that topic. Your brain searches keywords only right now."). `fix.next` is `tell_user_to_run` because the fix is the user's to run (`gbrain doctor --json --brain host`) in their terminal, so the agent offers it rather than running it. ### 4. First run on a keyless brain: one decision bundle, `ask_user` (journey) An agent installs gbrain for a user with no provider keys, from a non-interactive shell (H1a journey, recorded from the real CLI). ```bash gbrain init --pglite --no-embedding --json ``` stdout (`--json`): ```json { "status": "success", "engine": "pglite", "path": "/home/alice-example/.gbrain/brain.pglite", "pages": 0, "embedding_check": { "ok": true, "skipped": "no_embedding" }, "content": { "version": 1, "brain_id": "7f3c2a10-5b6e-4d1a-9c8b-2e4f6a8d0c11", "source_id": "default", "source_incarnation": "7f3c2a10-5b6e-4d1a-9c8b-2e4f6a8d0c11", "root": "/home/alice-example/.gbrain/content/7f3c2a10-5b6e-4d1a-9c8b-2e4f6a8d0c11/default", "repository_kind": "content_directory", "backup": "not_verified", "status": "ready", "stage": "complete", "pending_actions": [ "Optional: initialize Git explicitly; configure an off-host backup separately." ], "owned_root": true, "fresh_root_activation": true, "root_identity": "(device and inode of the content root)" }, "notices": [ { "code": "first_run_decisions", "kind": "ask", "why": "The brain is ready. These settings were applied with defaults or need the user's choice; none blocks using the brain.", "user_message": "gbrain is installed. Reply 'defaults' to keep the recommended settings (search_mode: conservative; writeback: salient; harness_wiring: skip), or tell me what to change.", "decisions": [ { "id": "search_mode", "question": "Which search mode should this brain use? Estimated downstream input cost of the retrieved payload at 10K queries/month (Haiku 5.5 / Sonnet 5.5 / Opus 5.5): conservative $4 / $80 / $160, balanced $10 / $200 / $400, tokenmax $20 / $400 / $800 per month.", "options": [ { "id": "conservative", "label": "conservative (applied)", "argv": [ "gbrain", "config", "set", "search.mode", "conservative" ] }, { "id": "balanced", "label": "balanced", "argv": [ "gbrain", "config", "set", "search.mode", "balanced" ] }, { "id": "tokenmax", "label": "tokenmax", "argv": [ "gbrain", "config", "set", "search.mode", "tokenmax" ] } ], "default": "conservative", "default_reason": "No chat key (Anthropic/OpenAI/Google) and no ready reranker — conservative keeps a tight 4K budget; balanced's reranker could not run." }, { "id": "writeback", "question": "Should agents save important facts the user states (preferences, decisions, commitments) automatically, with provenance? Saved facts are readable by agents connected to this brain; transient facts expire. Off any time with `gbrain config set memory.auto_writeback off`.", "options": [ { "id": "salient", "label": "Save durable facts the user states directly (recommended)", "argv": [ "gbrain", "config", "set", "memory.auto_writeback", "salient" ] }, { "id": "all", "label": "Every direct factual statement (more low-value facts, more extraction spend)", "argv": [ "gbrain", "config", "set", "memory.auto_writeback", "all" ] }, { "id": "off", "label": "Save only what the user explicitly asks to remember (records the answer)", "argv": [ "gbrain", "config", "set", "memory.auto_writeback", "off" ] } ], "default": "salient", "default_reason": "Recommended for a personal brain: the brain learns what the user tells their agents. It is opt-in, so it applies only when the user accepts the defaults or picks it." }, { "id": "harness_wiring", "question": "Which agent app should get gbrain memory? The install guide has a one-line command for each.", "options": [ { "id": "wire", "label": "Register ` serve --surface full` as a stdio MCP server in your agent host; the install section lists the exact command per harness (Claude Code, Codex, Grok Build, opencode, OpenClaw)." }, { "id": "skip", "label": "Do not register gbrain with an agent harness now." } ], "default": "skip", "default_reason": "No agent harness was detected." } ], "contract_version": 1 } ], "contract_version": 1 } ``` stdout is one document; the brain is ready and nothing blocks using it. The `first_run_decisions` notice is `kind: ask`, so the agent relays its `user_message` ("gbrain is installed. Reply 'defaults' to keep the recommended settings (search_mode: conservative; writeback: salient; harness_wiring: skip), or tell me what to change.") once and stops. The decisions are `search_mode` (default `conservative`), `writeback` (default `salient`), `harness_wiring` (default `skip`); each option carries the exact argv to apply it, so a reply of "defaults" needs no command at all. ### 5. A caller mistake over stdio MCP, recorded: `run` (journey) A harness on `gbrain serve --surface verbs` passes a string where `recall` expects a number (H1a journey, recorded from a real stdio session). ```text recall {"query":"quokka-journey-marker","limit":"abc"} ``` gbrain returns (`isError: true`, the envelope shown parsed): ```json { "error": "invalid_params", "code": "invalid_params", "message": "Parameter \"limit\" must be a number", "suggestion": "Pass `limit` as a number (Per-arm max (default 50, cap 100)). Example: recall {\"limit\": 10}. Next: recall {\"query\":\"quokka-journey-marker\",\"limit\":10}", "docs": "https://github.com/garrytan/gbrain/blob/master/docs/guides/error-codes.md#invalid_params", "protocol_version": 1, "fix": { "mcp": { "tool": "recall", "arguments": { "query": "quokka-journey-marker", "limit": 10 } }, "consent": [], "actor": "agent", "next": "run", "why": "The call failed validation before it ran, so nothing changed. This is the same call with your other arguments kept and `limit` set to 10.", "requires_exclusive": false }, "docs_cmd": [ "gbrain", "errors", "invalid_params" ], "class": "caller", "retryable": false, "contract_version": 1 } ``` One content block, `code: invalid_params`, `class: caller`. Nothing ran, so `fix.next: run`: the agent calls `recall {"query":"quokka-journey-marker","limit":10}` exactly as given (its other arguments kept, the bad one corrected) and gets the recall it wanted. ### 6. A second session on a locked brain: status-only serve, `tell_user_to_run` (journey) A second agent session starts `gbrain serve` while another session's serve owns the PGLite brain. The handshake still completes, with one tool, `gbrain_status` (H1a journey, recorded). ```text gbrain_status {} ``` gbrain returns these content blocks: `content[0]`: ```json { "status": "unavailable", "reason": "lock_held", "why": "This brain (/home/alice-example/.gbrain/brain.pglite) is open in another `gbrain serve` (PID 48213), usually started by another agent session, so this server cannot open it.", "brain_path": "/home/alice-example/.gbrain/brain.pglite", "config_path": "/home/alice-example/.gbrain/config.json", "lock_owner": { "pid": "48213", "transport": "stdio", "serve": true }, "fix": { "mcp": { "tool": "gbrain_status", "arguments": {} }, "consent": [], "actor": "user", "next": "tell_user_to_run", "why": "Close the session that owns the brain (another `gbrain serve` (PID 48213), usually started by another agent session); then call gbrain_status again and this server opens the brain in place (re-checked at most every 5 s). If your client does not refresh its tool list after recovery, restart this MCP server.", "user_message": "Your gbrain brain is already open in another gbrain serve (PID 48213), usually started by another agent session, so this session can't use memory right now. Close that session and I'll reconnect, or I can set up one shared gbrain server so both sessions work at once. Which do you prefer?", "requires_exclusive": false }, "user_message": "Your gbrain brain is already open in another gbrain serve (PID 48213), usually started by another agent session, so this session can't use memory right now. Close that session and I'll reconnect, or I can set up one shared gbrain server so both sessions work at once. Which do you prefer?", "decisions": [ { "id": "lock_recovery", "question": "Two agent sessions want the same brain. Close the other session, or share one HTTP server?", "options": [ { "id": "close_owner", "label": "Close another gbrain serve (PID 48213), usually started by another agent session; this server recovers on the next gbrain_status call." }, { "id": "share_http", "label": "Run one shared `gbrain serve --http` and connect every harness to it (writes harness config, mints tokens); the full plan is share_http_plan.", "argv": [ "kill", "48213" ] } ], "default": "close_owner", "default_reason": "Nothing is installed or reconfigured; the brain comes back as soon as the other session ends." } ], "share_http_plan": { "argv": [ "kill", "48213" ], "command": "kill 48213", "consent": [], "actor": "user", "why": "Stop another `gbrain serve` (PID 48213), usually started by another agent session first (or quit that session); the shared server needs the brain's single-writer lock.", "requires_exclusive": false, "then": { "argv": [ "gbrain", "serve", "--http" ], "command": "gbrain serve --http", "consent": [ "persistent_install" ], "actor": "user", "why": "Run ONE shared HTTP server for every agent session on this machine (keep it running, e.g. under autopilot or a terminal).", "requires_exclusive": true, "then": { "argv": [ "gbrain", "bootstrap", "harness", "--harness", "all", "--yes" ], "command": "gbrain bootstrap harness --harness all --yes", "consent": [ "persistent_install", "credentials" ], "actor": "user", "why": "Mints one bearer token per detected harness through the running HTTP server and rewrites each harness MCP entry to it (each stdio entry keeps working until its harness is rewired).", "user_message": "I can connect your agent apps to the shared gbrain server so every session uses memory at once. That stores an access token in each app's config and pre-approves gbrain's tools. OK?", "verify": { "argv": [ "gbrain", "doctor", "--only", "harness_wiring", "--json" ] }, "docs": "https://github.com/garrytan/gbrain/blob/master/docs/guides/remote-mcp.md", "requires_exclusive": false } } }, "checked_at": "2026-10-03T16:20:00.000Z", "contract_version": 1, "surface": "full", "surface_source": "default" } ``` `reason: lock_held` names the owner (`lock_owner.pid`). `fix.next` is `tell_user_to_run` because only the user can close the other session: the agent relays `user_message` and offers `close_owner` or `share_http`. Calling `gbrain_status` again after the owner exits opens the brain in place and the full tool list arrives through `tools/list_changed`. ## The error envelope One shape for every error: the CLI `--json` document, the single content block of an MCP `isError` result, and HTTP error bodies. | Field | Type | Meaning | |---|---|---| | `error` | string | Legacy wire value, frozen forever under v1. Equals `code` except for the four legacy values listed under **Precedence** below. | | `code` | string | Canonical registry code ([error codes](../guides/error-codes.md)). | | `reason` | string? | Sub-cause where one code covers several (`embeddings_disabled`, `wired_running`, …). | | `message` | string | What happened, one sentence. | | `suggestion` | string | Prose next step. When `fix` exists its rendered command is appended, so the two never disagree. | | `why` | string? | Enough context to explain the failure and weigh tradeoffs. | | `fix` | RenderedAction? | The next step (below). | | `docs` | string? | Absolute URL, pinned to the installed version (`master` for source checkouts, `LLMS_REPO_BASE` for forks). | | `docs_cmd` | string[] | `["gbrain","errors",""]`: the same row, offline. | | `class` | enum | `caller` · `consent` · `retryable` · `unavailable` · `server` · `host_only`. | | `retryable` | boolean | True only when a retry with the same request identity is safe. | | `notices` | RenderedNotice[]? | Notices attached to an error result (see below). | | `contract_version` | `1` | This contract. | Pre-existing keys keep their names and meaning: `detail`, `protocol_version` (memory verbs), `write_request` and `write_error` (write receipts). **Precedence.** Read `code`; fall back to `error` when `code` is absent (a server older than v1). Four surfaces send a legacy `error` value that differs from `code`; the canonical value rides `code`: | `error` (frozen) | `code` (canonical) | |---|---| | `unknown_operation` (HTTP unknown op) | `unknown_tool` | | `invalid_params` (jobs not found) | `not_found` | | `page_identity_changed` (page missing during write replay) | `page_not_found` | | the scope failure's existing value (`permission_denied`, `missing_scope`) | `insufficient_scope` | `gbrain errors --changed` lists these offline. Memory verbs keep their frozen v1 codes and `protocol_version: 1`, and gain `code` with the same value. **Safe recovery.** A mutating call whose outcome is unknown never says "retry". Its `fix` points at the evidence: your own write receipt (`get_write_request {request_id}` over MCP, `gbrain write-request -- ` on the CLI), the op's own status read (`submit_job` → `get_job`), or a host inspection by the brain's operator when the receipt belongs to someone else. `retryable: true` appears only on idempotent ops when the retry reuses the same request identity. **Unknown failures** come back as `code: "internal_error"` naming the op, with `next: report`: tell the user, run `gbrain doctor --json` on the brain host, and report it if it repeats. ## The fix: `Action` ```ts interface RenderedAction { argv?: string[]; // ['gbrain', …]: explicit --brain/--source; positionals after '--' command?: string; // shell-quoted from argv; never hand-built mcp?: { tool: string; arguments: Record }; // present when callable on your connection (or relayed: see below) consent: Effect[]; // [] means no consent needed actor: 'agent' | 'user' | 'host_admin' | 'provider'; next: 'run' | 'ask_user' | 'tell_user_to_run' | 'wait' | 'report'; why: string; user_message?: string; // relay text for the user, verbatim verify?: { argv?: string[]; mcp?: McpCall }; // read-only docs?: string; // absolute URL requires_exclusive: boolean; // needs the brain's single-writer lock inputs?: { name: string; how: string }[]; // values you must obtain first; argv holds `` only for these plan_hash?: string; // destructive/maintenance: binds the approved selection preview_argv?: string[]; // read-only preview of the same plan then?: RenderedAction; // step two of a two-step plan } ``` **Explicit routing.** Every `gbrain` argv in a fix (`argv`, `preview_argv`, `verify.argv`, `then`) names the brain and source the failing call acted on: `--brain ` for commands that open a brain, `--source ` for commands that resolve their source through the ambient chain (shared ops, and CLI commands the command table marks `routes_source`; `gbrain extract`, whose `--source` is the fs|db mode, gets `--source-id `). gbrain appends them when it renders the fix, before any bare `--`, and keeps flags the fix already carries. So you can run the fix later from another directory, or under a different `GBRAIN_BRAIN_ID`, `GBRAIN_SOURCE`, `.gbrain-mount` or `.gbrain-source`, and it still acts on the intended brain. Over HTTP only the source id is pinned, and only where a thin client can send it (an op with a `source_id` scope, or a command the brain host's operator runs): a mount id is host topology, and a thin client refuses `--brain`. Paths are stripped as everywhere else. A thin client pins nothing of its own: it has no local mounts and its remote scopes the source. `next` is computed when gbrain renders the fix for your connection: from the effects, the actor, the transport, which tools you can call and the user's preapprovals. It is never stored, so the same fix can say `run` on the CLI and `tell_user_to_run` over MCP. When a fix has `inputs` (for example a model's per-token price), obtain each value as `how` describes and substitute it for `` before running. ### Effects | Effect | Means | What authorizes it | |---|---|---| | `paid` | spends money with a provider | `--yes` (cap derived from the estimate × 1.5, floor $0.25), `--max-usd`/`--max-cost`, `spend.posture=tokenmax`, or the user's per-run preapproval | | `destructive` | deletes or rewrites data | `--yes --expect ` only; never preapprovable | | `credentials` | creates, stores or sends a credential | `--yes` (or the command's `--apply`/`--trust`) | | `egress` | sends brain content off the machine | `--yes` (or `--apply`/`--trust`) | | `persistent_install` | installs a service, hook, registration or scheduled job | `--yes`, the command's `--apply`, or the user's install preapproval (never covers credentials) | ### Actors | Actor | Who runs the fix | |---|---| | `agent` | you | | `user` | the user, in their own terminal or app (for example a credential prompt, or a CLI-only fix while you are on stdio MCP) | | `host_admin` | the operator of the brain host (a remote brain over HTTP, scope grants, server restarts). Never authorizable from MCP | | `provider` | an outside service you wait on (rate limits, outages) | A fix expected to run past 120 s (`AGENT_TOOL_LIMIT_MS`) is never `agent`; it renders `tell_user_to_run`. ### The `next` decision table First matching row wins. | # | Condition | `next` | |---|---|---| | 1 | the stored fix asks the user (a refusal whose every way out is the user's decision, such as `owner_unavailable` with `detail: unbound_source`) | `ask_user` | | 2 | no runnable step (no `argv`, no callable `mcp`, and not an MCP-only fix on the CLI) | `report`: relay `message`; run `gbrain doctor --json` where you can | | 3 | `actor = provider` | `wait`: retry after the stated delay with the same request identity | | 4 | `actor` is `user` or `host_admin`, or the fix is CLI-only and you are on MCP, or MCP-only and you are on the CLI | `tell_user_to_run` | | 5 | `consent` is non-empty and not covered by a matching preapproval (`destructive` never is) | `ask_user` | | 6 | otherwise | `run` | A CLI-only fix rendered for an MCP caller gets actor `user` on stdio and `host_admin` on HTTP, and `next: tell_user_to_run`. The mirror case: an MCP-only fix rendered for the CLI (a remote brain's fix on a thin client) keeps its `mcp` call as data, gets actor `user`, a `user_message` saying to call that tool over MCP, and `next: tell_user_to_run`; the human line reads `Fix: call over MCP with {…}`. ### Exclusive fixes and two-step plans `requires_exclusive: true` means the fix needs the brain's single-writer lock (`init --force --embedding-model`, `doctor --remediate`, `import`, `reindex-*`, `pglite-repair`, `migrate-embeddings`). Where gbrain can, it routes the work through the running `gbrain serve` that holds the lock and the fix just works. Otherwise the fix is a two-step plan: step one (actor `user`) stops the serve that owns the brain, and `then` carries the command to run once it has stopped. Run the steps in order and run `then.verify` at the end. ## Notices Notices are advice that rides a successful result: a degraded search, a truncated listing, a missing backup, a decision the user should make. ```ts interface RenderedNotice { code: string; // notice code kind: 'safety' | 'degraded' | 'coaching' | 'ask' | 'info'; why: string; fix?: RenderedAction; user_message?: string; decisions?: { id: string; question: string; options: { id: string; label: string; argv?: string[] }[]; default: string; default_reason: string }[]; contract_version: 1; } ``` **MCP success results.** `content[0]` is byte-identical to what it was before notices existed (bare arrays included). Each notice is one extra text block whose first line is the fixed prefix, then the fields that are set: ```text [gbrain notice kind=] why: fix: next: user_message: decision : (default: