Keep Claude Desktop Chat classified as MCP-only while recording Anthropic’s newer Cowork plugin-hook surface and ai-memory’s current lack of a Cowork integration. References #878.
Drop packaged systemd sandbox changes for a follow-up PR, gate Darwin and
container smoke to schedule/dispatch/nix|full-ci, shrink settings-options
to freeform-friendly keys, escape ExecStart, document llm_headers store
risk and NixOS support-matrix row, and add CODEOWNERS for nix/.
The checklist and `--jail=LIST` emitted only enabled toggles, so the user's
own ai-jail config (e.g. a global `~/.ai-jail` enabling `docker`) could still
mount what an unchecked row or an explicit list left out, and the summary
line misreported it.
- Interactive checklist: `marked_choices` passes every row the user saw,
checked as `--X` and unchecked as `--no-X`.
- `--jail=LIST` (including `none`): after the named entries, every visible
checklist row the list did not name is forced off with `--no-X`. Rows that
are not visible (absent credentials, CLI-only toggles) are never forced.
- Bare `--jail` is unchanged: smart-default rows only, the rest left to the
user's ai-jail config, because no selection was shown.
- `JailToggleChoice::implied` marks rows forced off by omission, so the
summary names the user's own `no-X` entries and says "everything else in
the checklist off" for the rest.
- Tests: an adversarial unit test (unchecked docker row and `--jail=none`
yield `--no-docker` / `--no-*` for every visible row, with bare `--jail`
as the control); parse, checklist, summary and end-to-end expectations
updated, the latter platform-aware for the Linux-only rows.
- Docs: design §5 semantics, cookbook, support matrix, CHANGELOG.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
`ai-memory run` can now decide which ai-jail credentials and capabilities a
jailed session gets, from the CLI or an interactive checklist, with smart
defaults so Enter does the friendly thing.
- ai-memory-workstream/jail.rs: a toggle table (credential mounts github,
aws, kube, gcloud, docker-config; ssh; worktree; docker, gpu, display,
pictures, tailscale; CLI-only audio, x11, host-shm, terminal-passthrough,
update-check, mise, toolchains), support detection from the installed
ai-jail's `--help` (exact `--X` tokens, so `--docker` never matches
`--docker-config`), injected host facts (home, SSH agent, origin URL,
linked worktree, project `.ai-jail` presence), the checklist with smart
defaults, and the `--jail=` list parser (`no-X`, `all`, `none`; reserved
security switches and ai-memory-owned flags refused).
- build_ai_jail_invocation emits the chosen `--X`/`--no-X` and a
`--no-save-config` baseline before the `--`, so ai-jail never writes the
run's transient flags into the repository's `.ai-jail`.
- inspect_repository reports the `origin` URL and whether the cwd is a
linked worktree.
- run.rs: `--jail[=TOGGLES]` / `--no-jail` (also stripped when they land in
the native argv), a pure jail_decision table, an explicit `--jail` re-exec
before the managed run is prepared (failing closed when ai-jail is not
usable), and the line-based checklist after the `--yolo` offer. A project
`.ai-jail` replaces the checklist and the bare-`--jail` defaults.
- Tests: unit coverage for parsing, support detection, defaults, the
decision table, flag stripping, and the checklist grammar; a real
`ai-jail --dry-run` over every toggle the installed ai-jail advertises;
end-to-end runs of the built binary with fake ai-jail/bwrap/claude and a
mock server, including PTY runs of the offer, the checklist, a project
`.ai-jail`, and `--yolo --no-jail`.
- Docs: design §5, cookbook yolo recipe, support matrix, CHANGELOG.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
- frontend-api.md (#986): the list, search, and recent routes return bare
JSON arrays, not `{ "workspaces": … }`-style wrappers (the route tests
assert `as_array()`); a page read returns `body_markdown`, not `body`; a
search hit carries workspace/project/kind and no `id`.
- windows.md (#758): native `ai-memory upgrade` is done (#801/#802), not
in-progress.
- managed-workstreams.md + support matrix (#987): document the Codex shared
daemon handing hooks a stale AI_MEMORY_RUN_ID and the `--no-daemon`
workaround until the server-side fix lands.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
ai-jail integration (`ai-memory run --yolo`):
- The re-exec built `ai-jail <flags> <exe> run …` with no `--`. ai-jail
rejects one of its own flags after the command and `run` shares flag names
with it, so `run claude --yolo --env GH_TOKEN=…` aborted. The invocation now
emits `--` before the wrapped exe (forwarding a colliding flag additionally
needs ai-jail >= 2.4.2, whose guard honors the separator; the cross-tool
test gates on that version).
- The offer only checked for a file named ai-jail: Windows could show it, a
host without bwrap/sandbox-exec was offered a jail that cannot start, and a
~/.local/bin-only install was offered and then not found by the bare
`Command::new("ai-jail")` re-exec after the run was already cancelled.
usable_ai_jail(os, lookup) now returns the exact binary to exec only on
Linux/macOS with the backend present; otherwise no question is asked.
--true-yolo:
- It now implies --yolo (warning, ai-jail offer, harness dangerous mode):
alone it used to apply Claude's bypassPermissions with no warning. It is
interchangeable with --yolo for non-Claude harnesses, and recognized after
native arguments (`run claude --model opus --true-yolo`), where clap leaves
it in the native argv and it was forwarded to Claude as an unknown option.
- The claude_true_yolo config key only upgrades an explicit yolo launch, as
its doc comment stated, instead of bypassing permissions on every run.
- Removed what never worked: three CLAUDE_CODE_DISABLE_*RM* env vars Claude
Code does not read (absent from the 2.1.280 binary and its env reference),
and an empty permissions.ask array that cannot clear ask rules from other
scopes (Claude unions them). Docs now state that Claude honors explicit ask
rules and its command-safety checks in every permission mode.
Relaunch after an interrupted run:
- A launcher killed before releasing its lease (terminal closed, ai-jail
torn down) left the workstream held for up to 90s and the next launch failed
after a 5s retry. An interactive launch now parses the holder and expiry from
the 409, waits for that lease to lapse (bounded by one lease; Ctrl-C aborts),
then proceeds. A holder that renews meanwhile is reported as live and never
displaced; the server's busy check stays the only arbiter (security
inventory row 13b). Non-interactive launches keep the short window.
Tests: usable_ai_jail OS/backend/Windows/exact-path, `--` placement and the
colliding-flag regression (unit + real ai-jail --dry-run), the yolo_modes
table, --true-yolo in both argv positions via real clap parses, the reduced
true-yolo argv, and HTTP-level held-lease wait / renewed-owner / Ctrl-C cases.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
ai-memory run --yolo now, on an interactive TTY only (never in hook/CI/
detached paths):
- warns that --yolo runs every tool call unconfirmed, [Y/n] default-yes;
- offers to re-run inside ai-jail when it is installed, re-execing the
original argv under `ai-jail --network --agent-state --env <NAME>…`
(--network shares the host net namespace so the loopback ai-memory server
stays reachable; only already-set credential/config env is forwarded);
- skips both prompts when already inside ai-jail (Linux hostname ai-sandbox
/ macOS PS1 (jail) ; fails open to showing the warning; Windows never).
Opt-in Claude "true yolo" (--true-yolo / [config] claude_true_yolo,
AI_MEMORY_CLAUDE_TRUE_YOLO) silences the pauses --dangerously-skip-permissions
leaves: sets the CLAUDE_CODE_DISABLE_*_RM_* env vars and injects
--settings bypassPermissions. Claude-only, off by default, sandbox-first.
Detection and argv assembly are pure/OS-explicit (ai-memory-workstream::jail)
with unit tests for every branch; a CLI integration suite asserts the argv
against the real ai-jail via --dry-run (skips cleanly when ai-jail/bwrap are
absent). Design: docs/design-yolo-safety-ai-jail.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
Command Code, Kiro CLI (v2 and v3) and Antigravity CLI have no native
session-end hook, so their sessions stayed open until a manual
`ai-memory finalize-session`. When `ai-memory run` launched one of them, it
now finalizes the run's own session after the harness exits: the one named
on the command line or chosen before the spawn, or the one its child linked
under AI_MEMORY_RUN_ID in this checkout, never a discovered one.
SessionId::from_native moves the hooks router's native-id mapping into core
so hook POSTs and the lookup share one key. finalize_session::run is split
into finalize() and report printing so the run reports on stderr. The
harness's exit code is kept; finalizing is bounded by a timeout, Ctrl-C
skips it, and a failure prints the exact finalize-session command.
The lookup matches the ended session too (as --reopen does), because these
harnesses keep capturing under the same id after a resume; a re-end with
nothing new is a no-op on the server.
Closes#941
Hermes runs a configured hook command through shlex.split with the event JSON on stdin and no shell, so its integration is exec-form (like Zero and ZCode) rather than a .sh bundle: AgentChoice::Hermes (alias hermes-agent) makes install-hooks, setup-agent and finalize-session accept it, and no script subdir is staged.
build_hermes_hooks_yaml emits the ready-to-paste hooks: block for ~/.hermes/config.yaml with the two events ai-memory can act on - pre_tool_call -> pre-tool-use and post_tool_call -> post-tool-use. Their tool_name / tool_input payload is the envelope the router already maps for agent=hermes, which is what gives Hermes sessions tool observations, tool-family titles and capture exclusions.
install-hooks --agent hermes never writes the config file: it is YAML the operator also edits and Hermes gates user hooks behind its own acceptance prompt (hooks_auto_accept), so the block is printed for pasting - the same choice made for Pool. Session lifecycle is deliberately left to the memory-provider plugin, so no hook-driven session-end is installed and a session cannot be closed twice.
Verified against Hermes v0.21.4: the block parses through Hermes own hermes_yaml, _parse_hooks_block registers both events, split_command_line yields an argv that runs the native hook subcommand, and that command spools the event with rc=0. Docs updated in docs/install.md, docs/support-matrix.md and docs/mcp-install.md. Follow-up to #623.
(cherry picked from commit 18155089cd)
OpenCode 2.0.10 replaced the lifecycle events the generated plugin listened
for (session.idle and friends) with session.execution.{succeeded,failed,
interrupted}, and it runs one long-lived service behind every CLI: closing a
terminal is not a session end, so sessions captured through the old binding
rarely produced a summary page or a baton. The plugin also re-ran two `git`
processes per event, lost startup context after the first model request,
dropped content-only tool results, and shared queue and spool state across
the per-location instances OpenCode 2 loads.
Plugin (install_hooks.rs, render_shared.rs, the node host fixture):
- binds session.execution.*, session.text.ended, session.moved,
session.deleted, session.compaction.started and the context/prompt/tool
hooks; every name was checked against the OpenCode 2.0.14 binary;
- claims startup context once per root session and re-injects it on every
model request; child sessions never claim it or publish a baton;
- keeps queue, spool and cleanup state per location instance; spool names
can no longer collide within a millisecond, and a torn-down host cancels
what is still in flight only after its final session-ends had the drain
budget (all generated TypeScript integrations share this runtime now);
- opt-in assistant capture (--capture-assistant) hands the last completed
text to the native hook, which sanitizes and caps it before the spool or
the wire, and falls back to the plain stop hook if that binary cannot run.
Server (router.rs, ops.rs, reader.rs, writer.rs):
- a completed root turn is a turn checkpoint: sessions/<id>.md and the
automatic baton are refreshed deterministically (no LLM) in the session
row's own scope, keeping one open baton per live session, refreshed in
place and audited; a checkpoint that lost the race with the session's end
touches nothing;
- an explicit, keyed session.moved rebinds the live session row to its new
directory on first delivery (compare-and-set on the cwd it left), so the
session's end and checkpoints follow it;
- a SessionEnd whose resolved scope drifted under the same cwd (a
.ai-memory.toml appeared mid-session) ends the session instead of
stranding it open; scope-drifted ordinary events are recorded as upstream
records any other drifted event;
- the latest captured assistant excerpt rides in the automatic baton; it is
not rendered into the git-tracked session page.
Docs: install.md, support-matrix.md, auto-scope.md, SECURITY.md and
DATA_HANDLING.md describe the checkpoint, routing and capture behavior.
Verified: cargo fmt --all -- --check; cargo clippy --workspace --all-targets
-- -D warnings; cargo test --workspace --all-targets (3569 passed, on release/2.5); the node
host fixture runs under cargo test (node >= 22.6) and fails if unload aborts
deliveries before its session-ends. Live on Windows 11 with OpenCode 2.0.14:
after a 64 -> 66 store migration, the running OpenCode service loaded the
regenerated plugin and one real turn through it (a Code Mode call to memory_status)
recorded its prompt, tool events and stop, logged "turn checkpoint written;
native session remains open", left the session open, wrote
sessions/<id>.md and exactly one open baton carrying the captured
assistant excerpt, which the session page does not contain.
The accessory app ships the ai-memory binary and hooks tree, starts the
existing LaunchAgent, and opens /web, status, config, and logs. Durable
data stays in Application Support so replacing the .app is an update.
Same bug as the docs/security.md fix in this branch: links written as
if they resolved from the repo root instead of the linking file's own
directory, 404ing on GitHub (e.g. docs/docs/macos.md, docs/docs/install.md).
Extend the assistant-capture closed table with (Codex, Stop) → last_assistant_message
(verified on codex-cli 0.154.0), behind the existing double opt-in (client
--capture-assistant + server capture_assistant=true). The install gate
capture_assistant_allowed now admits Codex on a native hook platform (it
previously refused it), and the Codex render path threads --capture-assistant
onto the Stop command. The runtime read + sanitize/bound/backstop/store pipeline
is already agent-agnostic and keys off the closed table, so no new capture code.
Only (Codex, Stop) is added — Codex has no SubagentStop.
Tests: closed-table set updated to admit Codex Stop; a positive
client-transform capture test for Codex; the install-flag gate test admits
Codex; a render test asserting --capture-assistant bakes onto the Codex Stop
command (and only Stop) when opted in. Docs: support-matrix + install.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
The MCP-only first increment from #659: install-mcp --client muse writes a
streamable-HTTP entry (with the bearer Authorization header) into Muse's
mcp_servers settings, schema_version-aware and mode:optional. Every field
traced to Meta's published Muse Code docs per the verification asked in #659.
# Conflicts:
# CHANGELOG.md
Register ai-memory with Meta's Muse Code through its documented
streamable-HTTP transport: a snake_case `mcp_servers` map in
~/.config/muse/settings.json carrying `transport`, `url`, `headers`,
`enabled`, and `mode`.
Two documented constraints shape the writer. The settings file must set
"schema_version": 1 or every muse command fails at startup with
`malformed settings file`, so the key is added when absent and an
existing value is never rewritten — a future schema must not be silently
downgraded. And `mode` defaults to `required`, which aborts the whole
Muse run when the server is unreachable; memory is an augmentation, so
the entry sets "optional" explicitly. No `framing` key is emitted: a
non-default value fails Muse's validation on streamable_http.
MCP-only. Muse documents a lifecycle hook surface, but the output
contract of its SessionStart event is not specified, so capture and
managed workstreams are not claimed and no AgentKind variant or
migration is added. Skills need no new code — Muse reads
~/.agents/skills, which install-skills already writes.
Refs #659
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHgUwuTjYeDkb1MVjzxA7Z
Grok, Zero, and MCP-only clients discard SessionStart stdout, so the only
way to see a pending baton was memory_handoff_accept, which claims it.
List is owner-filtered and read-only; accept now takes an optional
handoff_id so the inspected row is claimed exactly once.
Reconciles enrell's #622 onto release/2.1. Additive new-harness feature
(opencode2): new CLI enum arms, wire aliases folded into the existing
AgentKind::OpenCode (no new variant, no migration), a read-only parameterized
SQLite transcript adapter for the beta session_v2/session_message tables, and
an ai-memory-opencode2.ts plugin derived from v1. Sequenced after #625: the
opencode2 plugin inherits #625's resolveToken() auth, so its test assertion is
updated from "Bearer ${TOKEN}" to "Bearer ${token}". CHANGELOG (Added),
mcp-install and support-matrix rows reconciled with the Codex-SessionEnd (#605)
and capture-assistant (#627) wording. Queued for 2.1.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
Forward-merges the 2.0.3 fix batch onto the 2.1 train. CHANGELOG (keep-both
Added/Changed/Fixed) and the Codex support-matrix row (SessionEnd wording +
the #627 capture-assistant clarification) reconciled; code auto-merged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
Clarifies that assistant-final-turn capture is Claude Code + native-platform
only (already enforced in install_hooks + documented in install.md); a matrix
reader could otherwise infer capture is universal because only the Claude Code
row mentioned it. #627 was working-as-designed — the installer correctly
refuses --capture-assistant for Codex; this closes the only real doc gap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
Reconciles lucasliet's #606 onto release/2.1 against 2.0.2's #608/#610
opencode header work (only CHANGELOG conflicted; the header code
auto-merged and is verified below). Targets 2.1.0 — holds on the branch
until release.
Adds AI_MEMORY_LLM_HEADERS (typed ExtraHeaders): operator-supplied headers
on every provider, parsed/validated once at the config boundary, fail-closed
against a 9-entry reserved list (authorization, x-api-key, host, ...),
CRLF-rejecting, values marked sensitive, Debug prints names only. Sends
User-Agent: ai-memory/<version> on every provider (Copilot excepted),
layered under operator override; makes 2.0.2's x-opencode-session/user-agent
operator-overridable; and fixes AI_MEMORY_LLM_BASE_URL for provider=opencode
(OPENCODE_ZEN_BASE_URL deprecated for OPENCODE_GO_BASE_URL, value preserved).
Gate on the merged tree: cargo fmt --check clean; clippy --workspace
--all-targets -D warnings clean; full workspace tests 0 failed. #606's
on-the-wire tests and #610's opencode tests pass together.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
`AI_MEMORY_LLM_BASE_URL` had no effect with
`AI_MEMORY_LLM_PROVIDER=opencode`. `OpenCodeProvider::new` hardcoded the
endpoint and `build_provider`'s `OpenCode` arm never read
`ProviderConfig::base_url` — the override was accepted at the configuration
boundary and then silently dropped, which is worse than rejecting it. Zen's
general catalogue at `https://opencode.ai/zen/v1` was unreachable through
the provider built for OpenCode; the only route was `openai-compat`, which
gives up the `x-opencode-session` default.
Zen and Go are separate products, not two spellings of one: Go serves a
smaller, cost-optimised model set under `zen/go/v1`, Zen the full catalogue
under `zen/v1`. The constant said one and held the other
(`OPENCODE_ZEN_BASE_URL = ".../zen/go/v1"`), and the docs repeated the
conflation as "Zen/Go" throughout, so the gap was invisible from the code.
- `OPENCODE_GO_BASE_URL` names the default endpoint for what it is.
`OPENCODE_ZEN_BASE_URL` is deprecated in favour of it but keeps both its
export and its value, so code compiled against v2.0 is unaffected.
- `OpenCodeProvider::with_base_url` repoints the provider, and the factory
calls it with `ProviderConfig::base_url`, mirroring how the
`anthropic-oauth` arm already handles its own override. Go stays the
default, so existing setups do not move.
- An override keeps the session header and the user agent: both endpoints
correlate requests the same way, so changing where a request goes must not
change how it identifies itself.
- Docs distinguish the two products and state that model ids are per
catalogue, so an override needs an explicit `AI_MEMORY_LLM_MODEL`. The
`opencode-zen` alias selects Go like every other spelling — the endpoint
comes from the base URL, not the alias — and now says so.
Unit tests pin the default, the alias's value, the override, and that the
session header survives it; a wiremock test drives `build_provider` and
asserts the request actually arrives at the overridden host carrying both
headers, since "the field changed" and "the request went elsewhere" are
different claims.
Codex shipped a first-class SessionEnd lifecycle event in Codex CLI
0.145.0 (openai/codex#33895). ai-memory already bundled
hooks/codex/session-end.{sh,ps1} but never listed the event in
CODEX_EVENTS, so finished Codex sessions got no automatic
end-of-session summary or cross-agent handoff.
Add ("SessionEnd", "session-end.sh") to CODEX_EVENTS and stop
merge_codex_payload from unconditionally stripping the key on every
apply -- it now goes through overlay_event_hooks like every other
event, so a user's own third-party SessionEnd hook is preserved and
ai-memory's entry is added alongside it. The .ps1 is picked up by the
existing Windows extension swap. Update the Codex support-matrix row
and add a CHANGELOG entry.
On Codex < 0.145 the event key is inert; finalize-session --agent codex
remains the documented fallback.
Closes#604
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHRmTUMXVVcG1MsyRXTcQp
Every user-facing doc was graded on four axes (what / why / when-to /
real example, including when-NOT-to). Fixes for the highest-impact
gaps:
- managed-workstreams: 'Do you need this?' - hooks + handoffs already
cover the quit-Claude-open-Codex case; managed runs are for native
resume surviving a harness switch. Skip guidance included.
- auto-improvement-loop: a 60-second user-facing top for a feature that
is on by default - what auto-approve means, the explicit
require_approval=true recommendation for shared/team servers, cost
shape, and what a staged proposal sidecar contains. Research prose
retained below the fold.
- okf: opens with the user payoff (hand a bundle to someone without
ai-memory; export-okf one-liner; what the receiving side sees)
before the conformance design.
- temporal: when to reach for as_of (audits/post-mortems; plain
memory_query is right 99% of the time) plus a worked
postgres-then-migrated example with both results.
- typed-edges: the gotcha->fixes->contradicts->lint loop as a concrete
scenario, and 'plain wikilinks remain the default' skip guidance.
- experience: a sample staged cross-session proposal.
- config template: consolidation and slots blocks say why/when, not
just what.
Accuracy bugs found by the same sweep: auto-scope's config snippet
still called mode="single" the default (per_actor since v1.39); ZCode
was listed twice with conflicting statuses in both support matrices
(it has MCP #529 AND hooks #532 - merged to one Supported row); the
README docs index listed ROADMAP-2.0 twice.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm
The README had grown to 1,125 lines and buried the case for the project
under installation detail. Rebuilt it at a third of the size:
- New "Why ai-memory" section up top - the five differentiators the
September 2026 landscape research validated (cross-agent handoffs as a
typed protocol, cross-machine continuity, team sharing with auth and
attribution, plain-markdown source of truth, silent zero-LLM capture),
written for a human reader rather than as a feature list, plus an
honest-ops note with the measured ~700/s write ceiling.
- New "How it works" capture->consolidate->recall->handoff flow.
- Support matrix compacted to two columns; the full 30+-row per-agent
matrix moved verbatim to docs/support-matrix.md.
- Quick start trimmed to AUR + docker + the two claude-code wiring
commands; stale "enable per_session for two sessions" advice replaced
with the v1.39 works-out-of-the-box story.
- Use cases, LLM providers, and security hardening moved verbatim to
docs/use-cases.md, docs/llm-providers.md, docs/security.md, each linked
from a short pointer section and from the Docs index.
Verbatim moves only - no prose was rewritten in the extracted docs.
Packaging tests (36) still pass; README keeps zero mutable-main install
URLs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDbhmszrjG9s5MrPrTuNtm