magi-code 0.97.3

Repository-aware CLI coding agent for terminal work
Documentation
# Local-agent discovery and catalog extensions

Normative extension contract for [local-agent stdio](local-agent-protocol.md), protocol version 1. Require each operation explicitly in `initialize.required_capabilities`: `session.discover`, `model.catalog.status`, `model.refresh`, `operation.cancel`. Calling one without negotiation returns `unsupported_capability`. Shared envelopes, strict params, admission, request-ID lifetime, cancellation and cleanup limits still apply. Existing `session.list` and `model.catalog` result schemas and initialization `limits` remain unchanged.

## Incremental session discovery

`session.discover` params and session entries match `session.list`. Result adds `consistency="provisional_until_complete"`, `progress:string` and `recovery:"continue_cursor"|"none"`. It uses a separate child-owned scan. Initialization includes `extensions["session.discover"]` only when negotiated:

```json
{"consistency":"provisional_until_complete","ordering":"directory","step_directory_entries":256,"step_lines":10000,"step_bytes":8388608,"record_bytes":8388608,"step_ms":2000,"page_entries":100,"page_bytes":32768}
```

Each request advances a held directory iterator and at most one held, safe primary-history reader. Work yields at any advertised step/page bound. A record can span requests; memory retains one bounded raw/parsed record (8 MiB raw limit), last recorded cwd, one 8 KiB reader buffer and one bounded page. No global candidate-count or whole-history-size ceiling applies to this operation. Shared 4096-request child lifetime still applies; if exhausted, start a fresh scan in a new child.

`progress` values: `directory_work`, `history_work`, `record_work`, `byte_work`, `inventory_validation`, `deadline`, `page_entries`, `page_bytes`, `complete`. Work limits return resumable progress. Empty pages with a nonnull cursor are expected; continue that cursor rather than refreshing or reducing page size. Order follows directory enumeration. Cursors are single-use, child-scoped and at most 64 ASCII bytes. Using an earlier cursor invalidates scan stickily. Fresh discovery abandons prior scan, never attachment. Cancellation/error abandons scan; restart explicitly.

Only fully read histories become eligible. Every nonempty recorded cwd must match canonical child cwd. One mismatching cwd proves exclusion immediately, without reading an unrelated history's remainder. Legacy empty/missing-cwd histories remain `cwd_unverified`; malformed/unsafe/unreadable histories use `sessions_omitted_invalid`. No over-limit history is silently omitted. Subagent/archive histories and unsaved handles remain excluded. Titles, timestamps, conservative busy hints and exact-open authority match `session.list`.

Pages remain provisional until `complete=true`. Directory identity/permissions/change timestamps, attachment identity and current history signature are checked before/after each step. At enumeration end, a second bounded incremental directory pass compares order-independent signatures of every name, identity, permission, length and modification/change timestamp. Lock renewal timestamps are ignored. Changed inventory returns `stale_revision` and invalidates scan. Mutation of an already emitted history can be detected during final validation, rather than immediately on next page. Discard accumulated pages on failure; never present provisional rows as a completed snapshot. No OS-atomic transaction is promised. `session.open` always revalidates exact ownership, history and scope without switching on failure.

Errors/recovery:

| Code | Meaning and recovery |
| --- | --- |
| `discovery_record_bytes` | One record exceeds 8 MiB; normal backend compaction/export required before retry. |
| `discovery_page_bytes` | Projection cannot fit; report backend failure. |
| `discovery_unavailable` | Unsafe/unreadable root or invalid primary name; inspect backend store. |
| `stale_revision` | Start fresh scan; discard provisional pages. |
| `operation_cancelled` | Restart deliberately. |

Shared startup/admission errors still apply. Codes/progress contain no filenames, paths or content. Neither listing operation repairs metadata, writes history/settings, prunes, acquires writers or executes a turn. Status/shutdown remain on protocol loop.

## Operation cancellation

`operation.cancel` takes exactly `{ "request_id":string }`, using normal ID grammar. It cancels only currently active session/discovery/catalog/refresh/history/selection request with that ID; no turn starts or cancels. Result `{ "state":"cancelling", "request_id":string }`. Missing/completed/different request returns `operation_missing`; invalid params return `invalid_params`. Acknowledgement is not completion or rollback; correlate original request's terminal response separately. Status/shutdown remain usable while cancellation drains.

## Provider-scoped cached status

`model.catalog.status` params, cached choices, paging/revisions and limits match `model.catalog`; no remote effects. Result adds `configuration_reload="restart_child"` and at most 32 `providers` objects, each exactly `{ "provider":string, "catalog_status":string, "authentication":string, "relevant":boolean, "recovery":string }`. Provider IDs use same safe-name policy as choices. Full result, including statuses, remains within catalog page-byte bound.

Catalog status: `available`, `missing_cache`, `empty_cache`, `invalid_format`, `invalid_schema` (schema/provider identity), `incompatible_fingerprint`. Validation order: format, schema/provider, fingerprint, empty entries, entry identities. Cache age alone never expires a catalog. Unsafe/unreadable and oversized caches retain operation errors `catalog_unavailable`/`catalog_limit`, not fabricated format or authentication reasons.

Relevant means provider has an enabled model ID or is currently selected. Authentication is `ready` or `authentication_unavailable` for relevant providers; unused providers report `not_checked`, avoiding irrelevant authentication work. Missing-cache notices for unused providers are suppressed in both catalog methods; their scoped statuses still explain absence. Recovery: `none`, `configure_authentication`, `refresh_catalog`, `enable_models_and_restart`. Valid enabled choices remain selectable when another provider lacks a cache. Both methods share one revision-scoped catalog snapshot; refreshing either replaces prior revision.

## Deliberate provider refresh

`model.refresh` params exactly `{ "provider":string }`: one configured custom or built-in provider, safe name at most 256 bytes. Unknown provider/fields return `invalid_params`. Separate deliberate remote operation, never an automatic side effect of opening/reloading catalogs. Same single session worker and `busy` admission rules as selection; no queued concurrent refresh, prompt replay or tool execution. Status/shutdown remain responsive. Cancel via `operation.cancel` or shutdown.

Result exactly:

```json
{"provider":"<provider>","outcome":"refreshed","reason":null,"catalog_revision":"<revision>","catalog_error":null,"recovery":"reload_catalog","configuration_reload":"restart_child","selection_unchanged":true}
```

`outcome` is `refreshed` or `failed`. `reason` is null on success, otherwise one of `authentication_unavailable`, `authentication_rejected`, `configuration_invalid`, `remote_unavailable`, `remote_timeout`, `rate_limited`, `empty_catalog`, `invalid_catalog`, `catalog_limit`, `cache_write_failed`. `catalog_revision` and `catalog_error` are nullable strings. No raw errors, URLs, response bodies or credentials. Cancellation returns `operation_cancelled`, not an outcome claiming completed refresh.

Reuse backend provider/auth discovery, Codex credential refresh, Claude auth readiness, models.dev enrichment and cache fingerprint validation. Refresh writes only a validated nonempty provider catalog, at most 2048 unique entries and 1 MiB serialized cache, through normal atomic persistence. Failed remote/auth/rate-limit/validation requests preserve previous cache bytes and selection. Uncertain filesystem commit failures require rereading cache. Normal credential refresh may persist renewed credentials. Cache/credential writes are deliberate effects, not preference changes. No enabled list, global/project/session preference, selected model or requested effort changes. Newly discovered disabled models remain unselectable; enable in normal settings and restart child to admit changed configuration. Catalog reload alone never reloads settings.

Every admitted refresh invalidates previous catalog cursors and selection guards, even on remote failure/cancellation. After noncancelled outcome, bounded cache scan returns fresh revision. If scan fails, revision is null and `catalog_error` reports its safe error; prior retained choices remain invalid for mutation. Reload catalog/status before deliberate selection; normal readiness checks remain mandatory. Refresh itself does not reconcile/change selection effort status; subsequent catalog observation/turn admission uses existing rules.

Initialization advertises `extensions["model.refresh"]` only when negotiated:

```json
{"providers_per_request":1,"cache_bytes":1048576,"models":2048,"preferences":"unchanged"}
```

Provider response bodies retain normal 2 MiB bound and at most 32 process-wide catalog HTTP workers; provider HTTP has 10-second connect and 300-second wall timeout. Enrichment retains 8 MiB response and 30-second timeout; authentication/Claude readiness retain normal bounded implementations. Cancellation polls during catalog HTTP and checks between auth, discovery, enrichment and persistence. Already-started blocking auth/enrichment/filesystem calls finish under their own bounds; observed cancellation prevents later cache writes, without promising rollback after atomic commit. Cleanup still waits at most 2000 ms, independent of worker completion.

## Client flow

1. Negotiate needed extensions explicitly; decode their schemas separately from `session.list`/`model.catalog` and their unchanged limits.
2. Browse with `session.discover`; follow single-use cursors, including empty progress pages, until validated completeness. Exact-open chosen ID; never replay input to browse.
3. Read `model.catalog.status`; explain provider-specific cache and authentication reasons. Keep valid enabled choices selectable.
4. Only deliberate user action sends `model.refresh` for named provider. Observe outcome, reload cached catalog and status, then use fresh guards for explicit selection. Never automatically apply a model or effort.
5. Restart child after normal settings changes. Frontend never reads backend JSONL, config, auth or provider APIs.

## Recorded process observations

macOS, locally built debug executable SHA-256 `842364518b3a9dcb6e506b3124bb4b70f803bdd6d84b1c6a99f34716d2878793`, package 0.96.4, implementation prepared for #859. Disposable shell consumers exchanged correlated frames over real Unix pipes; observations recorded without assertions or retained correctness scripts. All backend state and conversation records synthetic, under isolated `MC_HOME` and cwd. No personal history/config/auth accessed or mutated; no turn/tool execution or live authenticated provider requests. Refresh used a controlled loopback HTTP server; successful custom-provider refresh also fetched normal public models.dev enrichment.

| Observation | Actual outcome |
| --- | --- |
| Small workspace with 300 unrelated primary files and another foreign history containing 10,001 parser-valid `SessionEvent` records, each with explicit cwd | Workspace chat and empty legacy row returned; empty validation pages ended with `complete=true`; mixed-cwd history excluded, no invalid-omission notice. |
| 304 eligible histories, using canonical cwd aliases | Pages of 100, 100, 100, 4, then empty validated completion; no global candidate failure. |
| Mutation of already emitted primary's mode | Final inventory validation returned `stale_revision`; same continuation remained stale. No completed partial inventory. |
| Lazy create, exact open, mixed-cwd failure | Lazy handle absent from discovery; exact saved chat opened; mixed-cwd exact open returned `cwd_mismatch`, status retained saved attachment. |
| Unused provider caches missing, empty, invalid JSON, invalid schema or mismatched fingerprint | Scoped reasons distinguished; unused authentication `not_checked`; valid enabled custom choice available and no global unavailable notice. Missing case additionally observed before final executable build. |
| Loopback HTTP 429 / 401 / delayed response | `rate_limited` / `authentication_rejected`; delayed refresh kept status responsive, explicit cancellation acknowledged and original request returned `operation_cancelled`. |
| Loopback success with enabled `demo` and disabled new model | `refreshed`, fresh catalog revision; next cached list offered only enabled `demo`. Normal subsequent catalog observation updated effort metadata/selection revision without selecting another model or changing requested effort. |
| Client negotiating only `session.list` and `model.catalog` | No initialization extensions, `discovery_sessions=256`, unchanged cached result fields; unnegotiated refresh returned `unsupported_capability`. |

Closed-loopback offline refresh and unchanged last-known enabled choice/selection were observed with the earlier implementation build. Source review and formatting, locked check/build, strict all-target/all-feature Clippy and diff checks supplement these observations; compilation does not establish runtime correctness. Whole histories above 8 MiB, more than 4096 directory names, oversized single records, authentication renewal, cache-write faults, every cancellation timing, Linux and native Ma-gi decoding remain unexecuted. Incremental streaming and safe error paths cover those bounds in source; this is not a claim of exhaustive runtime verification.