You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
levain init wires the consent hook for Claude Code and Codex, away mode off. A new install, and an existing one after levain update, runs levain's PermissionRequest hook: a permission the agent would ask you for is answered at once by a standing consent grant or a signed answer the queue verifies, and otherwise left to the agent's own dialog, as before. The hook files a request on the consent queue only when something could answer it: away mode is on, or the cockpit its settings name holds an enrolled key or a standing grant; otherwise (a cockpit made before any key was enrolled included) it files nothing and the dialog shows. Away mode stays off unless you turn it on, so nothing waits: with no grant and no answer the hook prints nothing and the dialog shows at once. With away mode on, a request nobody answers in time also falls back to the agent's own dialog; it is never denied or allowed for you. The hook is a script in the install's activation/hooks/ (consent_claude_code.py or consent_codex.py), wired through the same settings file (Claude Code: the install's .claude/settings.json) or hooks.json (Codex) as levain's other hooks, so levain update keeps it current, a file you edited is staged rather than overwritten, and the hook pin covers it. Init also creates the consent settings file in levain's consent queue directory (~/.levain/consent/claude_code.json or codex.json, or under $LEVAIN_HOME) naming this install's cockpit as the one grants and answers verify against, with away mode off; an existing settings file is never changed. Init and levain doctor warn when the installed agent is not a version the hook was tested with; the hook is wired anyway (it prints a decision only for a verified grant or answer), and levain consent canary --agent claude_code|codex checks that version. levain doctor gains three rows per agent: whether the hook is wired to this install, away mode (for Codex with away mode on, a WARN: Codex shows no dialog while the hook holds, only "Running hook", then its own dialog), and which cockpit grants verify against.
Codex specifics: Codex runs the hook only after you trust it at its "Hooks need review" screen at the next start (levain never writes that trust; until then Codex does not run it and shows its own dialog). The hook is in Codex's machine-wide CODEX_HOME/hooks.json, like levain's other Codex hooks, so it runs in every Codex session on the machine, whatever the directory: once a request is filed (above), every Codex approval files a queue row, the request as Codex filed it, kept in the consent queue directory for good, with secrets inside free text (a shell command) not masked. A grant answers only inside the workspace it was given for.
Known edges, consent hook: (1) one consent settings file per agent per machine (per $LEVAIN_HOME): a second install keeps the first install's cockpit, and init and doctor say so. (2) The hook reads the consent queue of $LEVAIN_HOME as the agent sees it; an agent started with a different $LEVAIN_HOME than init's reads a queue with no settings, so it shows its dialog. (3) Deleting an install, or switching it with init --force from Codex to Claude Code, leaves the machine-wide Codex entry naming a script that is gone; it does nothing (it exits 0 and Codex shows its own dialog) until another levain init --adapter codex replaces it. (4) A grant can verify only once this install's cockpit holds enrolled keys. (5) levain update keeps the recorded choice and has no flag of its own: to turn the hook off on an existing install, run levain init --path <install> --force --consent-hook off (--answers <install>/.levain/answers.json skips the interview). A .levain/config.json that cannot be read, or a consent_hook value other than on or off, reads as off until init records a choice again. (6) levain update creates a missing Codex settings file only while the machine-wide hooks.json runs this install's consent hook; a command there that merely names this install's consent script counts as running it, as levain doctor's wiring rows read it.
The cockpit's Tray can list the items it is holding back. The Tray still opens showing only what is due, with the "N held back — surface date not reached" line; that line now carries a "show" control, and "show" lists the held items under the due ones, each marked "surfaces Oct 22", with the same verbs any Tray item has (edit, schedule, dismiss, so you can reschedule or edit something that is not due yet). The filter box searches them while they are shown. While they are hidden, a filter that matches only held items says how many match and offers "show". Whether they are shown is kept in this browser only, hidden by default. The terminal views and levain's text output are unchanged: they still print the count and never list the held items.
Known edges, held Tray items: (1) on an install that has not enabled the reported class, pulling a held item forward to today or earlier is refused as it was before ("needs the broker"); moving it to a later date works. (2) A Tray with more than 5,000 held items lists the first 5,000 and says so. (3) More than 5,000 held items keep the order the store holds them in, not the soonest-surfacing first, and the partial banner calls the dropped tail "unreadable". (4) The panel's match_label (the ?q= form of the panel read, which the page does not use) counts due rows only: held matches are returned under held but are in neither number. (5) A panel provider other than the Tray that declares held has those rows partitioned out of its rows, and no page draws them (only the Tray sets it). (6) A Tray whose panel kind is not a list or triage list does not show the "held back" line.
Choose how your agent gets the memory wrap procedure: loaded at the start, or read when it wraps. The memory instructions are split in two. seed/memory.md keeps what the agent does every turn (start of session, what to record, the recording cadence, recall before decisions); the new seed/memory_workflow.md holds the wrap procedure (prepare_wrap -> compress -> save_continuity, the pre-compose recall, the contradiction scan, cold-pattern routing, durable facts, what the citation check does and does not catch). By default both load together, as before, so nothing changes until you ask. levain init --memory-load on-demand takes the procedure out of the always-loaded context: the agent's carrier (CLAUDE.md or AGENTS.md) then carries a short summary of the wrap and points at the file, which the agent reads before it wraps. --memory-load claude-code=on-demand (or codex=on-demand) sets one agent, and may be given again; a bare mode sets the install's default. The choice is kept in .levain/config.json, so levain update and init --force keep it; give the flag again to change it, or set memory_load in .levain/config.json and run levain update. ADAPTER=MODE for an agent the install does not carry is refused before anything is written. levain doctor shows one memory workflow row per install, eager or on-demand (summary in the carrier). levain update brings the trimmed seed/memory.md and the new file into an existing install, and now installs a seed file a release adds even when the carrier would import it (it used to hold the carrier back on a file that was not there yet, and nothing wrote the file); a memory.md you edited is staged under .levain/pending/, as every seed file is.
Known edges, memory load: (1) whether an agent in on-demand mode opens seed/memory_workflow.md when you say "wrap" has not been run against a live Claude Code or Codex session; the summary tells it to, and that is all that is known. Eager stays the default for that reason. (2) A bare --memory-load MODE sets the install default and keeps any per-agent entries already recorded; give ADAPTER=MODE to change one. (3) If you edited seed/memory.md before this release, levain update keeps your copy and stages the new one; until you merge it, your copy still holds the whole wrap procedure and the carrier also imports memory_workflow.md, so the agent reads the procedure twice (levain update says which block to remove when you merge). (4) levain update does not print the mode in force; levain doctor does. (5) Doctor's carrier check reads an import or pointer line wherever it appears, so one that sits only inside a code fence or an HTML comment counts as loaded.
levain doctor now also reads your carrier for the seed files your install's memory-load mode expects it to load: an eager file with no import, an on-demand file with no pointer. It fails the CLAUDE.md freshness (or AGENTS.md freshness) row and names the file, so a carrier you edited that predates memory_workflow.md no longer reads as healthy; levain update also says so in one line when it stages your carrier and your copy leaves that file unloaded. levain init --memory-load ... on an OpenHands install is refused before anything is written, since that adapter has no carrier to load a seed file into.