Files
ai-memory/docs/managed-harness-contributions.md
AkitaOnRails 97b8e4cbe1 Merge main into release/2.1: pick up the 2.0.4 fix batch
Forward-merge of the nine PRs that landed on main (the 2.0.4 batch: #642 auth
stale-bearer, #646/#640 LoginLimiter, #644 Cursor attribution, #650/#647
reindex manifest, #638 MCP routing, #652 CI docs, #645 dev-loop/build) into the
2.1 feature train, so release/2.1 carries every fix before 2.1.0 is cut.

Conflicts resolved:
- crates/ai-memory-wiki/src/wiki.rs: 2.1's per-page write lock (page_locks,
  #607) and main's manifested_scopes memo (#650) are independent additions to
  the same struct/imports/constructor — kept both; imports merged to
  {HashMap, HashSet}.
- CHANGELOG.md: [Unreleased] now carries 2.1's ### Added features above main's
  ### Changed + ### Fixed (the 2.0.4 fixes), Keep-a-Changelog order, single
  [2.0.3] section preserved.
- crates/ai-memory-llm/tests/extra_headers_on_the_wire.rs (2.1's #606 test)
  relocated into tests/suite/ and declared in mod.rs to satisfy #645's
  one-test-binary-per-crate harness convention (caught by the repo_layout guard).

fmt, clippy -D warnings, llm harness, and the repo_layout guard all green.
2026-09-05 12:55:11 -03:00

8.3 KiB

Adding a managed harness

Managed-workstream support is narrower than MCP or lifecycle-hook support. This release can manage Claude Code, Codex, OpenCode, OpenCode 2 beta, Pi, Crush, Kimi Code, Command Code, Kiro CLI v2/v3, OMP, Grok Build CLI, and Antigravity CLI. Gemini CLI, Devin CLI, Cursor, and the other integrations in the README support matrix do not become managed merely because ai-memory can capture their hooks.

A managed adapter must preserve a harness's real native session, deliver the portable workstream delta exactly once, and import only visible history without writing the harness's private store. Contributors adding another harness should follow this protocol.

1. Establish the native contract

Start with current upstream documentation and repeatable fixtures. The companion ai-babel research project is useful prior work, but it is not a substitute for verifying the installed CLI version.

Document and test:

  • the default executable and supported host platforms;
  • fresh-session and resume syntax, including selector placement;
  • explicit selectors supplied by the user;
  • print/noninteractive modes and utility subcommands that must pass through;
  • session-store roots, environment overrides, and path options;
  • the stored checkout identifier and timestamp units;
  • complete versus in-progress records, tool-pairing rules, compaction records, and records containing hidden reasoning or credentials;
  • a supported way to inject startup context before the first model turn; and
  • the native dangerous/approval-bypass option, if the harness has one.

If exact session identity, read-only extraction, or pre-turn context delivery cannot be demonstrated, document the limitation instead of adding a partial managed adapter.

2. Register the harness deliberately

Add or reuse the harness's AgentKind. A new kind must also be added to AgentKind::ALL and to a forward SQLite migration that rebuilds the sessions constraint and pairing triggers without losing existing rows. Never edit an already-released migration.

Then wire the explicit managed surface:

  • RunHarnessChoice in ai-memory-cli;
  • ManagedHarness, its executable, and its AgentKind mapping in ai-memory-workstream;
  • the server's managed-harness validation list; and
  • README, install, architecture, design-decision, and changelog references.

Explicit support comes first. Add a harness to bare ai-memory run automatic selection only after checkout-local candidate discovery is reliable. A local file timestamp is a bootstrap hint; the server's current linked harness remains authoritative for an established workstream.

3. Preserve native argv and session ownership

Implement fresh, resume, and explicit-selector behavior in crates/ai-memory-workstream/src/harness.rs. Preserve every user argument and its order except the exact wrapper-owned --yolo and --fresh tokens. An explicit native selector always wins over ai-memory's linked session. Help, version, login, doctor, export, and similar utility commands must not receive session flags.

Generate a session id only when the native CLI officially accepts a caller provided id. Otherwise let the harness create the session, then discover it by exact checkout and launch time. Do not infer a session from "newest globally."

Map --yolo only to a verified native option and avoid duplicates. If the harness has no equivalent, add no flag and document that fact.

Support wrapper --fresh by checking the exact linked id in the native store before injecting a resume selector. A confirmed missing id starts fresh; an unreadable or malformed store remains an error rather than being treated as absence. Reject --fresh when the user also supplied a native resume, session, continue, or fork selector.

4. Discover and export read-only

Implement candidate discovery in crates/ai-memory-workstream/src/transcript.rs. Implement incremental export only when a documented or repeatably observed native format exposes visible conversation records without private state.

The adapter must:

  • restrict candidates to the exact current checkout;
  • honor documented store-root environment and command-line overrides;
  • open SQLite stores read-only and never create, migrate, vacuum, or repair them;
  • when export is supported, tolerate an incomplete final record or in-progress tool call without advancing past it;
  • when export is supported, emit deterministic source record and event ids and resume from a persisted source cursor without duplicates;
  • normalize only visible user/assistant messages, completed tool calls/results, and compaction summaries; and
  • exclude system/developer prompts, hidden reasoning, binary payloads, credentials, provider metadata, and unsupported records.

Extraction gaps should become bounded loss annotations. If the conversation payload is opaque or undocumented, return such an annotation and rely on sanitized lifecycle-hook capture instead of guessing. Never copy a private record "just in case."

5. Deliver context before acknowledging it

The preferred path is a native SessionStart hook. The managed child inherits AI_MEMORY_RUN_ID; the hook links the actual native session, renders the unseen bounded workstream range, makes it model-visible, and only then accepts the delivery cursor.

If the harness has no suitable hook, use a documented native context mechanism. Crush is the reference: the launcher fetches without accepting, writes a private temporary copy of the supported config plus an ephemeral context file, starts the child, and acknowledges only after spawn succeeds. The original config and session store are never written by ai-memory, the harness retains its normal native writes, and the temporary directory is removed after exit.

Fetching must be repeatable until acceptance. A failed spawn, hook, or network ack may redeliver context; it must never silently lose it. Historical tool calls must be labelled completed evidence so another harness cannot interpret them as pending work.

6. Keep workstream invariants intact

One logical workstream has at most one current native session per harness. A new harness joining an established workstream starts a clean native session and receives portable history; it must not adopt an unrelated older local session. First-use adoption is allowed only while authoritative server state has no linked native session or substantive portable event.

Keep leases, delivery cursors, source cursors, immutable sanitized segments, batch limits, and idempotent imports on the existing shared path. Do not add a harness-specific synchronization database or mutate private stores to resolve precedence, directory renames, or conflicts.

7. Required tests

A managed-harness PR should include focused coverage for:

  • fresh launch, linked resume, missing-linked-session recovery, explicit-selector precedence, argv order, utility passthrough, path overrides, --yolo mapping, and wrapper --fresh;
  • candidate ordering, exact-checkout isolation, timestamp handling, read-only access, incremental cursors, stable ids, incomplete records, visible record inclusion, and private record exclusion;
  • first-use adoption and the established-workstream obsolete-session guard;
  • startup context fetch/injection/accept ordering and spawn-failure redelivery;
  • the AgentKind::ALL schema invariant when a kind is added;
  • deterministic fake-process acceptance in scripts/managed-workstream-acceptance.sh; and
  • a manual real-harness pass that switches into the new harness, records successful delivery of its assigned context delta, and resumes its original native session when revisited. Require a new assistant event when the adapter has a readable transcript. A deliberately hook-only adapter must instead prove that a correlated lifecycle observation was persisted and document the bounded transcript-loss annotation. Do not make pass/fail depend on the model quoting packet text: some harnesses externalize large hook results to a file, making recall depend on a model tool-use choice.

The deterministic phase remains credential-free and suitable for frequent local runs. Real model calls stay opt-in and outside CI. Record the tested CLI version and any platform limitation in the PR description.

Run the repository's complete Rust gate before requesting review:

cargo fmt --check
git diff --check
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings