Conversation
A completed Prompt now retains a closed account of the permission requests answered while its turn ran, and the REPL can read Agent turns and conversations out of a hostile Journal and filter them from its canonical location. Nothing live runs yet: no Agent provider reaches `xmd repl`, no observer, no pending request, no form and no new command option. The audit is assembled field by named field from the request as it arrives and gains the decision when it is made, so `rawInput`, the Session, provider callbacks and errors cannot cross the durable boundary, and mutating what the caller kept afterwards changes nothing. Members are in arrival order rather than completion order, and a request that raised has no member. The list is closed: an option kind or outcome this build cannot read refuses the whole prompt record. Observing decides nothing — the installed policy still answers. The projection reads `agent_prompt` through core's own record parser, orders the chronology by the sequence each record states rather than by append order, groups conversations by the provider's session key alone, and leaves a turn that reached no provider in the chronology and in no conversation. Every value is copied out of the event graph and frozen, and one turn is one object however it is reached. `ReplRoute` gains `session`, canonically last in the query, and the Sessions-only `+permission` drawer, which carries no key. The resolver's `asking` boolean becomes one closed live-availability value, and a historical prefix ignores all of it. Slice A of #854.
The artifact of run 36417683621, unchanged. This slice adds one test file and changes what several others run, and a weight is only true of the runner that measured it.
4 tasks
taras
added this pull request to stack #858
September 28, 2026 22:24
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A REPL that shows Agent work has to read it from the Journal, and the Journal did
not hold enough: a
<Prompt>recorded what it was asked and what it answered,but not the permission decisions taken on the way. Nothing could show what a turn
was allowed to do, and nothing could group turns into the conversations they
belong to. This is the retained-truth half of #854 — the surface that presents it
comes later.
What changes
Before: a retained Prompt records its input, output and status. Permission
decisions exist only while the turn runs. A REPL location can name an execution,
a scope, a history position and a drawer.
After: a retained Prompt also records the permission decisions it was given, as
safe data — tool call id, title, kind, the offered options and the outcome —
and nothing else. The projector reads retained Prompts into frozen turns with
their owning scope, their chronology and the conversation each belongs to. A
location can name one conversation (
?session=<key>) and a live permissiondrawer (
+permission, on the Sessions surface only).How it works
The observation is installed inside the turn's own scope in
runPrompt(), whichis the only place that can see a decision and still be the turn that owns it.
Audits join the record when the turn settles, in occurrence order rather
than completion order.
Nothing live crosses the durable boundary: the audit is built from copies of the
safe fields, so a provider's
rawInput, its session key and itscwdneverreach the journal — proved by a canary and by mutating every source object the
instant a request settles.
Chronology comes from
PromptRecord.sequence, which is prompt scheduling, notappend order. Conversations are grouped by the provider's own
sessionKey; aturn that never reached a provider has no key, stays out of every group and
appears only in the All list.
Review guide
Start with:
packages/core/src/agent/journal.tsThen review:
packages/core/src/agent/journal.ts— the record's new member, itsserialization, and what it refuses
packages/core/src/agent/function-components.ts— where the observation isinstalled (14 lines inside the turn's scope)
packages/cli/src/repl/model.ts— retained turns, chronology, groupspackages/cli/src/repl/route.ts—?sessionand+permissionspecs/acp-client-spec.mdLook carefully at:
object: the durable value and the serialized bytes are both checked.
permissionsundefined.What must stay true
named safe fields and checked by the
rawInputcanary and themutate-on-settle row.
permissions.sequence, not append order — checked by swapping two records'sequence values and leaving append order alone.
sessionKey, never a guess — checked by oneagent in two conversations, two agents in one, and an unassigned failure that
joins no group.
+permissionis live-only and Sessions-only — checked by refusals at a frozenposition, on the wrong surface, and with no request waiting.
How to verify it
deno task test packages/core/tests/agent-function-components.test.ts \ packages/cli/tests/repl-model.test.ts packages/cli/tests/repl-route.test.tseach refuse the whole record — fails if an audit is dropped or read loosely.
sequence,status,sessionKey, audit or source positionrefuses the projection with no partial model — fails if hostile data is shown.
?sessionround-trips canonically beside the existing query members, and anabsent key refuses and leaves navigation untouched — fails on an ad-hoc session
path or an empty unknown view.
Scope
Included
PromptRecord, and its observation?sessionand the+permissiondrawer in the route grammartest-weights.jsonfrom run 36417683621 ata403cc57Intentionally unchanged
and presents nothing. The rows that draw it are a later slice of Run a Plan and follow its Agent sessions in the REPL #854.
<Prompt>'s own behaviour and the stream it consumes are untouched.Generated or mechanical changes
test-weights.jsonis the artifact of Measure test weights run36417683621 at
a403cc57, committed unchanged. No shard recalibration.packages/cli/src/repl/application.tsis mechanical: the new route and modelmembers on the initial route, the refused view and the empty model.
Risks and limitations
<All>#855 (<All>/<Spawn>) is stacked onthis branch and its PR (✨ Run spawned document work concurrently with <All> (#855) #856) is retargeted here until this merges.
Scope confirmation
Part of #854.