Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
464 commits
Select commit Hold shift + click to select a range
c064b28
Merge remote-tracking branch 'origin/brennanb2025/acp-c5-tool-interru…
brennanb2025 Oct 5, 2026
4edf2b2
Merge remote-tracking branch 'origin/brennanb2025/acp-d2-translation'…
brennanb2025 Oct 5, 2026
b7279d1
Merge remote-tracking branch 'origin/main' (d17351401d7) into brennan…
brennanb2025 Oct 5, 2026
57a7a29
refactor(native-chat): Grok's registration declares where it runs; AC…
brennanb2025 Oct 5, 2026
0fc5a72
fix(native-chat): Grok follows the ACP runtime's request contract and…
brennanb2025 Oct 5, 2026
1108280
fix(native-chat): a Grok chat Orca already holds resumes without writ…
brennanb2025 Oct 5, 2026
d2764bc
refactor(native-chat): a failed Grok reattach needs no window close o…
brennanb2025 Oct 5, 2026
cba2dfa
test(native-chat): D3's merged tests use the shipped declarations and…
brennanb2025 Oct 5, 2026
da34dda
fix(native-chat): typecheck fallout of the base merges; any agent's e…
brennanb2025 Oct 5, 2026
4b4febc
fix(native-chat): a Grok Stop ends the process once Grok settles its …
brennanb2025 Oct 5, 2026
f6f9d66
test(native-chat): a Grok Stop ends the process only after Grok answe…
brennanb2025 Oct 5, 2026
2245710
fix(native-chat): Steer on a Grok card cancels the running prompt, th…
brennanb2025 Oct 5, 2026
bca0c77
refactor(native-chat): a close or Stop cancels a start through the ac…
brennanb2025 Oct 5, 2026
a30a89e
fix(native-chat): a Grok crash that closes stdout before its exit sti…
brennanb2025 Oct 5, 2026
5d68d91
fix(native-chat): nothing Grok sends while a held chat reattaches is …
brennanb2025 Oct 5, 2026
586e6a6
test(native-chat): a Stop after a resume finds no turn an unmarked ol…
brennanb2025 Oct 5, 2026
651ab47
test(native-chat): a resumed Grok chat keeps its last context reading…
brennanb2025 Oct 5, 2026
e8d1e11
test(native-chat): a Grok background task a Stop ended reads as stopp…
brennanb2025 Oct 5, 2026
294fd0d
refactor(native-chat): quit's stop of each start answers through one …
brennanb2025 Oct 5, 2026
cd9a138
fix(native-chat): quit aborts every start the host has in flight befo…
brennanb2025 Oct 5, 2026
2c2731d
fix(native-chat): a Grok start's abort stops reaching its child once …
brennanb2025 Oct 5, 2026
418aa57
fix(native-chat): a close or Stop during any attach phase stops the s…
brennanb2025 Oct 5, 2026
a0c5bba
test(native-chat): a close during the attach's owner probe asks no ad…
brennanb2025 Oct 5, 2026
b63c9b5
test(native-chat): a close's re-ask closes a Claude or Codex child a …
brennanb2025 Oct 5, 2026
7a4ac2f
fix(native-chat): Steer during a turn Grok began itself cancels it an…
brennanb2025 Oct 5, 2026
0ff2585
test(native-chat): a steer whose cancel Grok never answers ends Grok …
brennanb2025 Oct 5, 2026
0baae44
fix(native-chat): a Grok crash stays a crash when a stop lands before…
brennanb2025 Oct 5, 2026
b0f1e33
test(native-chat): fixtures this PR's registered Grok and desktop cap…
brennanb2025 Oct 5, 2026
9931665
fix(native-chat): a start quit stops is not the queued message's star…
brennanb2025 Oct 5, 2026
ff08986
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-c1a-…
brennanb2025 Oct 5, 2026
06e664b
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-c1-t…
brennanb2025 Oct 5, 2026
98e7403
Merge origin/main into acp-d2-translation
brennanb2025 Oct 5, 2026
7346f41
fix(native-chat): a message sent after a Stop or close aborted a star…
brennanb2025 Oct 5, 2026
ad8ebbd
Merge remote-tracking branch 'origin/brennanb2025/acp-c1a-timeline-tr…
brennanb2025 Oct 5, 2026
77c16ec
test(native-chat): the message sent after an aborted start is answere…
brennanb2025 Oct 5, 2026
c4a96e1
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-c5-t…
brennanb2025 Oct 5, 2026
16eef12
Merge remote-tracking branch 'origin/brennanb2025/acp-c1-timeline-ass…
brennanb2025 Oct 5, 2026
c1f0c7a
Merge acp-c1-timeline-assembler (main merged) into acp-d2-translation
brennanb2025 Oct 5, 2026
75231a4
fix(native-chat): a Grok model pick Grok never answers no longer hold…
brennanb2025 Oct 5, 2026
27a9db0
fix(agent-launch): a phone's launch opens a terminal for an agent who…
brennanb2025 Oct 5, 2026
d2e32d1
fix(acp): strip every agent hook variable from the ACP child, from th…
brennanb2025 Oct 5, 2026
2776c80
refactor(native-chat): the mutation context carries the provider-wait…
brennanb2025 Oct 5, 2026
a24cd11
fix(agent-launch): agent.launch.v2 still vouches for Claude and Codex…
brennanb2025 Oct 5, 2026
879f74a
refactor(native-chat): drop saved-history adoption from the timeline …
brennanb2025 Oct 5, 2026
d012bc2
Merge remote-tracking branch 'origin/brennanb2025/acp-c1-timeline-ass…
brennanb2025 Oct 5, 2026
0f4be81
refactor(acp): drop session/load history adoption from the translator
brennanb2025 Oct 5, 2026
58c3dd5
refactor(native-chat): a pending input is only Orca's send now
brennanb2025 Oct 5, 2026
d40fd03
Merge remote-tracking branch 'origin/brennanb2025/acp-c1-timeline-ass…
brennanb2025 Oct 5, 2026
2802460
test(acp): keep the task-result status table on live frames
brennanb2025 Oct 5, 2026
d613623
test(acp): a frame helper for a shell command Grok is running
brennanb2025 Oct 5, 2026
03ca1d9
fix(acp): a Grok crash settles through the host's provider-exit batch…
brennanb2025 Oct 5, 2026
8854fe8
test(acp): a crash seen first leaves the host no Grok turn to revise
brennanb2025 Oct 5, 2026
459b8cb
refactor(native-chat): what a gone generation left unfinished gets it…
brennanb2025 Oct 5, 2026
ed63665
refactor(native-chat): a watched exit revises what its child left unv…
brennanb2025 Oct 5, 2026
ed9fadc
test(native-chat): drop the duplicate itemFence on the fake that alre…
brennanb2025 Oct 5, 2026
16e84d0
test(claude, codex): an exit whose stdout ended first still reports a…
brennanb2025 Oct 5, 2026
374e740
Merge origin/main into acp-a1-opaque-handle
brennanb2025 Oct 5, 2026
75ddbbe
fix(acp): reopen a chat with session/load, as the common pattern does
brennanb2025 Oct 5, 2026
22d3d85
Merge acp-a1-opaque-handle (with origin/main) into acp-a2-agent-registry
brennanb2025 Oct 5, 2026
ce62189
fix(acp): drop the 60 s handshake bound; an abort fails the start's w…
brennanb2025 Oct 5, 2026
275455d
fix(acp): a Stop naming an ended turn follows Claude's rule
brennanb2025 Oct 5, 2026
ace8822
Merge acp-a2-agent-registry (with origin/main) into acp-a3-wire
brennanb2025 Oct 5, 2026
c832af5
fix(native-chat): a close no longer re-asks a failed start's unproven…
brennanb2025 Oct 5, 2026
c2bfd63
Merge origin/main into acp-a6-provider-lifecycle
brennanb2025 Oct 5, 2026
fe8108c
fix(acp): a message sent during a turn the agent began itself goes at…
brennanb2025 Oct 5, 2026
6143fb4
test(native-chat): dismiss-all through a remote client's audience kee…
brennanb2025 Oct 5, 2026
d717abf
fix(acp): launch Grok as `grok agent stdio`, without the update and l…
brennanb2025 Oct 5, 2026
e996bb1
Merge origin/main into acp-c1a-timeline-transitions
brennanb2025 Oct 5, 2026
aa80392
fix(acp): an agent that ends its stdout, or answers unreadably, is no…
brennanb2025 Oct 5, 2026
fa8e969
fix(native-chat): a person's close joining a failed one still binds t…
brennanb2025 Oct 5, 2026
a59276f
Merge remote-tracking branch 'origin/brennanb2025/acp-c1a-timeline-tr…
brennanb2025 Oct 5, 2026
5c4a89f
Merge remote-tracking branch 'origin/brennanb2025/acp-c1-timeline-ass…
brennanb2025 Oct 5, 2026
a08bbef
test(native-chat): Grok opens as a chat only behind the structured-ch…
brennanb2025 Oct 5, 2026
557a602
docs(acp): generic ACP comments say what holds for every agent, not Grok
brennanb2025 Oct 5, 2026
3b7d680
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-d1-p…
brennanb2025 Oct 5, 2026
f5b6658
test(claude, codex): type the EOF-before-exit test's streams; the sup…
brennanb2025 Oct 5, 2026
76a91ab
feat(acp): a steer's cancel asks once and never ends the agent
brennanb2025 Oct 5, 2026
2a1cef2
chore(native-chat): keep the record store and recovery capsule under …
brennanb2025 Oct 5, 2026
58bb525
fix(acp): a repeated steer shares the cancel in flight; say what the …
brennanb2025 Oct 5, 2026
3b0c7ab
Merge origin/main into acp-a2-agent-registry (main #24862)
brennanb2025 Oct 5, 2026
42c2e2f
Merge acp-a2-agent-registry (main #24862) into acp-a3-wire
brennanb2025 Oct 5, 2026
636f6f1
Merge origin/main into acp-a6-provider-lifecycle (main #24862 landed)
brennanb2025 Oct 5, 2026
f77b590
Merge acp-c1-timeline-assembler into acp-c5-tool-interrupted
brennanb2025 Oct 5, 2026
105ba89
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-c5-t…
brennanb2025 Oct 5, 2026
162e973
Merge A3 #25159 (42c2e2fb532) into D3
brennanb2025 Oct 5, 2026
d3342e3
Merge A6 #25204 (636f6f1bdc4) into D3
brennanb2025 Oct 5, 2026
13f9ad8
Merge C5 #25181 (105ba896929) into D3
brennanb2025 Oct 5, 2026
75f36ac
Merge D2 #25090 (5c4a89f0613) into D3
brennanb2025 Oct 5, 2026
c862fe7
Merge D1 #24990 (58bb525f828) into D3
brennanb2025 Oct 5, 2026
3bc888b
Merge origin/main (e9ed75a5de1) into D3
brennanb2025 Oct 5, 2026
804c2bb
fix(native-chat): drop the stopDelivery the A3 merge doubled
brennanb2025 Oct 5, 2026
964db92
fix(acp): a steer's cancel asks Grok once and never ends it
brennanb2025 Oct 5, 2026
28b5036
fix(acp): a permission Grok asks with no prompt of Orca's running is …
brennanb2025 Oct 5, 2026
a6bd840
fix(acp): a Grok that dies while starting is reported with its own la…
brennanb2025 Oct 5, 2026
212d0d2
test: a Stop after a steer sends its own cancel; drop the import the …
brennanb2025 Oct 5, 2026
10673e9
test(native-chat): main's Stop-note test builds its turn context with…
brennanb2025 Oct 5, 2026
d47e26e
test(claude): say why the close test's fake child cast is safe
brennanb2025 Oct 5, 2026
bb082ea
Merge origin/main (a3bd6a6a1e7) into D3
brennanb2025 Oct 5, 2026
fc120f7
Merge origin/main into acp-a2-agent-registry (main squash of A1 #2499…
brennanb2025 Oct 5, 2026
fb8bcb8
test(native-chat): build the Stop-opened-turn test's identity and tur…
brennanb2025 Oct 5, 2026
7c4112f
Merge acp-a2-agent-registry (with origin/main d791421568c) into acp-a…
brennanb2025 Oct 5, 2026
f1e98c3
Merge origin/main into acp-a6-provider-lifecycle (main squash of #249…
brennanb2025 Oct 5, 2026
5c6a834
test(native-chat): build the Stop-opened-turn test's identity with th…
brennanb2025 Oct 5, 2026
e35bf18
test(ratchet): require src/main/provider-process now that it has landed
brennanb2025 Oct 5, 2026
0bb3919
Merge origin/main into acp-a2-agent-registry (main #25706, #25073)
brennanb2025 Oct 5, 2026
5f8deca
Merge brennanb2025/ratchet-provider-process-required (#25710) into ac…
brennanb2025 Oct 5, 2026
5b75928
test(native-chat): keep main's opaque-handle import in the Stop-opene…
brennanb2025 Oct 5, 2026
4e53819
Merge origin/main into acp-a6-provider-lifecycle (main #25706, #25073)
brennanb2025 Oct 5, 2026
1a5c70e
test(native-chat): keep main's opaque-handle import in the Stop-opene…
brennanb2025 Oct 5, 2026
ab066e6
Merge brennanb2025/ratchet-provider-process-required (#25710) into ac…
brennanb2025 Oct 5, 2026
a21812b
Merge acp-a2-agent-registry (main 9def4b9ba19 + ratchet fix #25710) i…
brennanb2025 Oct 5, 2026
1717828
Merge origin/main into acp-c5-tool-interrupted (main #25073 and the A…
brennanb2025 Oct 5, 2026
433c302
Merge brennanb2025/ratchet-provider-process-required (#25710) into ac…
brennanb2025 Oct 5, 2026
9dbd818
Merge origin/main into acp-a2-agent-registry (main's submission-posit…
brennanb2025 Oct 5, 2026
8e35d9a
Merge origin/main into acp-c5-tool-interrupted (main's submission-pos…
brennanb2025 Oct 5, 2026
5d011d7
Merge origin/main into acp-a6-provider-lifecycle (main's submission-p…
brennanb2025 Oct 5, 2026
547158c
Merge acp-a2-agent-registry (main e65bbc95cec) into acp-a3-wire
brennanb2025 Oct 5, 2026
1f7f7fa
feat(native-chat): record a fresh provider conversation that replaced…
brennanb2025 Oct 5, 2026
54c9f21
test(native-chat): build this stack's journal identities with main's …
brennanb2025 Oct 5, 2026
a6923e4
docs(acp): every reattach drops the agent's replay, not only for a ch…
brennanb2025 Oct 5, 2026
6312c30
Merge origin/main into acp-a2-agent-registry (main's duplicate-import…
brennanb2025 Oct 6, 2026
8e3331c
Merge acp-a2-agent-registry (main c6827fe02a5) into acp-a3-wire
brennanb2025 Oct 6, 2026
a26ef64
Merge origin/main into acp-a6-provider-lifecycle (main's duplicate-im…
brennanb2025 Oct 6, 2026
6130495
Merge origin/main into acp-c5-tool-interrupted (main's duplicate-impo…
brennanb2025 Oct 6, 2026
ab992ed
refactor(native-chat): store a replaced conversation flat; refuse it …
brennanb2025 Oct 6, 2026
ed9996e
Merge A3 #25159 (8e3331cd0e8) into D3
brennanb2025 Oct 6, 2026
528f256
Merge A6 #25204 (a26ef64fd9b) into D3
brennanb2025 Oct 6, 2026
c6ce409
Merge C5 #25181 (6130495f57b) into D3
brennanb2025 Oct 6, 2026
c95cc45
Merge origin/main (9756daeced4) into D3
brennanb2025 Oct 6, 2026
a31b528
refactor(native-chat): read hosts' structured agents from the app-she…
brennanb2025 Oct 6, 2026
2a57650
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-hand…
brennanb2025 Oct 6, 2026
5e84b09
Merge current main into agent registry branch
brennanb2025 Oct 6, 2026
6b1ed63
Merge 5e84b09c0d94 into acp-a3-wire
brennanb2025 Oct 6, 2026
e21865a
Merge 8127d9478218 into acp-a6-provider-lifecycle
brennanb2025 Oct 6, 2026
4dfc78f
Merge 8127d9478218 into acp-c5-tool-interrupted
brennanb2025 Oct 6, 2026
503e483
Merge 8127d9478218 into acp-c1a-timeline-transitions
brennanb2025 Oct 6, 2026
eba27a3
Use current provider handles in transition tests
brennanb2025 Oct 6, 2026
bbd96e1
Merge eba27a3c784e into acp-c1-timeline-assembler
brennanb2025 Oct 6, 2026
a7d8bcb
Use current provider handles in timeline fixtures
brennanb2025 Oct 6, 2026
63f79a9
Merge a7d8bcbb64fb into acp-d2-translation
brennanb2025 Oct 6, 2026
3b064e8
test(native-chat): prove replacement rows survive downgrade and re-up…
brennanb2025 Oct 6, 2026
503ebe4
Require the ACP directory in the runtime import check
brennanb2025 Oct 6, 2026
db71a89
Merge 08a970a3f117 into acp-a2-agent-registry
brennanb2025 Oct 6, 2026
a94f7ec
Merge db71a89d7438 into acp-a3-wire
brennanb2025 Oct 6, 2026
5df4db2
Merge 08a970a3f117 into acp-a6-provider-lifecycle
brennanb2025 Oct 6, 2026
85d1b66
Merge 08a970a3f117 into acp-c5-tool-interrupted
brennanb2025 Oct 6, 2026
383b0dc
Merge 08a970a3f117 into acp-c1a-timeline-transitions
brennanb2025 Oct 6, 2026
b5b0068
Merge 383b0dc2c420 into acp-c1-timeline-assembler
brennanb2025 Oct 6, 2026
bb93479
Merge b5b0068f885c into acp-d2-translation
brennanb2025 Oct 6, 2026
e7c0c5d
Merge origin/main (1b52be62550) into D3
brennanb2025 Oct 6, 2026
754fe5b
test(ratchet): require src/main/acp now that this PR lands it
brennanb2025 Oct 6, 2026
ba22513
Merge #25747 (3b064e88821) into D3
brennanb2025 Oct 6, 2026
541ecbd
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-hand…
brennanb2025 Oct 6, 2026
b6f57c3
feat(acp): a saved session the agent cannot reopen continues in a new…
brennanb2025 Oct 6, 2026
df8468e
Merge #25747 (541ecbd6835) into D3
brennanb2025 Oct 6, 2026
dbeef45
chore(acp): rewrap the acquire header comment
brennanb2025 Oct 6, 2026
bcff300
test(acp): a start closed while the agent reopens fails without openi…
brennanb2025 Oct 6, 2026
93ec93f
Merge A3 #25159 (a94f7ecd33e) into D3
brennanb2025 Oct 6, 2026
42b61b1
Merge A6 #25204 (5df4db2900c) into D3
brennanb2025 Oct 6, 2026
bbd9694
Merge C5 #25181 (85d1b669941) into D3
brennanb2025 Oct 6, 2026
2ac670f
Merge D2 #25090 (bb93479f1a2) into D3
brennanb2025 Oct 6, 2026
cc22251
Merge origin/main (5f7c8483e9c) into D3
brennanb2025 Oct 6, 2026
67e12c3
Merge current main and landed protocol client into translation branch
brennanb2025 Oct 6, 2026
82baab3
Merge origin/main (4e64fa9940f) into D3
brennanb2025 Oct 6, 2026
798fc3b
Let ACP connections own their supervised agent process
brennanb2025 Oct 6, 2026
613ca21
Preserve ACP cleanup evidence and isolate exit observers
brennanb2025 Oct 6, 2026
20f7f8a
Expose ACP cleanup observations and type the permission fixture
brennanb2025 Oct 6, 2026
9bd27f9
Merge landed timeline assembler and current main into ACP translation…
brennanb2025 Oct 6, 2026
e7212b2
Merge origin/main (1de3aa405f7) into D3
brennanb2025 Oct 6, 2026
0ef6d21
refactor(native-chat): the registered-agents capability lives in its …
brennanb2025 Oct 6, 2026
2de0157
Merge origin/main (9905765e3b8) into D3
brennanb2025 Oct 6, 2026
aebc700
Merge ACP-ALIGN #25810 (20f7f8a9531) into D3
brennanb2025 Oct 6, 2026
eae097e
Merge D2 #25090 (9bd27f9d16b) into D3
brennanb2025 Oct 6, 2026
03584f0
refactor(acp): one connection owns the Grok process and its protocol
brennanb2025 Oct 6, 2026
9fea20e
Add chat-owned OpenCode server transport foundation
brennanb2025 Oct 6, 2026
c23d1ce
Merge origin/main (4de9f9f85a8) into D3
brennanb2025 Oct 6, 2026
50e2578
Combine server fixture type imports for CI lint
brennanb2025 Oct 6, 2026
fa957dc
fix(acp): Grok signs in on its own machine with its API key or cached…
brennanb2025 Oct 6, 2026
16a1875
Bound request serialization and correct transport fixture types
brennanb2025 Oct 6, 2026
bb32fe8
fix(acp): the adapter decides which of Grok's requests reach the person
brennanb2025 Oct 6, 2026
9c7c2eb
fix(acp): a plan Grok proposes shows as a plan, with no approval card
brennanb2025 Oct 6, 2026
7b8bdaa
fix(orchestration): worker-start opens a Grok worker in a terminal, a…
brennanb2025 Oct 6, 2026
ef67dde
Bound OpenCode event consumers and isolate chat caller identity
brennanb2025 Oct 6, 2026
606d1f6
fix(acp): a prompt answer Orca can't read ends the turn instead of ha…
brennanb2025 Oct 6, 2026
a652f15
fix(acp): send Grok's prompt-identity extension only to agents that e…
brennanb2025 Oct 6, 2026
06c7314
Merge origin/main (3ec38b8c6d1) into D3
brennanb2025 Oct 6, 2026
2f7da77
refactor(native-chat): the registered-agents capability lives in prot…
brennanb2025 Oct 6, 2026
b972838
test(acp): read the sign-in method with a schema, not a type assertion
brennanb2025 Oct 6, 2026
6aadfde
refactor(native-chat): composer transport and Stop control in their o…
brennanb2025 Oct 6, 2026
1c3f726
Merge origin/main (b75213100b5) into D3
brennanb2025 Oct 6, 2026
06dd7ea
test(native-chat): read registered agents by id, as main's structured…
brennanb2025 Oct 6, 2026
ccc2a8d
fix(acp): a first reopen warns when Grok forgets a chat that exchange…
brennanb2025 Oct 6, 2026
0687496
Add setting-gated OpenCode server native chat integration
brennanb2025 Oct 6, 2026
1d5b4e6
Use checked native event ordinals in timeline identities
brennanb2025 Oct 6, 2026
4b5d2db
Validate nested account tags and preserve bounded restore failures
brennanb2025 Oct 6, 2026
f3cb22d
Keep native history restoration within its session module
brennanb2025 Oct 6, 2026
cec2b35
Check native protocol records and support registered-agent drafts
brennanb2025 Oct 6, 2026
b194994
fix(acp): a start writes the warning row an earlier attach failure dr…
brennanb2025 Oct 6, 2026
c43cf4c
fix(acp): a question during a turn the agent began itself gets its ca…
brennanb2025 Oct 6, 2026
bc33e91
Merge remote-tracking branch 'origin/main' into brennanb2025/acp-d3-grok
brennanb2025 Oct 6, 2026
a135592
Update launch and status tests for registered structured agents
brennanb2025 Oct 6, 2026
b48ccbf
Merge current main into OpenCode structured chat integration
brennanb2025 Oct 6, 2026
99a36ef
Merge commit '842c6c667a86d66d55b8cc3b2486ea6be5c56fcc' into brennanb…
brennanb2025 Oct 6, 2026
940e05a
test(native-chat): import the unfinished-work capture from the module…
brennanb2025 Oct 6, 2026
b4f19ec
Run OpenCode structured chat over ACP
brennanb2025 Oct 6, 2026
b81a595
Merge remote-tracking branch 'origin/brennanb2025/acp-d3-grok' into b…
brennanb2025 Oct 6, 2026
c568299
Replace the OpenCode server integration with OpenCode over ACP
brennanb2025 Oct 6, 2026
40aca28
OpenCode 2 over ACP only for its default account
brennanb2025 Oct 6, 2026
4b15f90
Test OpenCode over ACP against its recorded traffic
brennanb2025 Oct 6, 2026
a6d5f70
Name the normalized tool update for what it is
brennanb2025 Oct 6, 2026
d0d3400
OpenCode over ACP: 1.x only, honest grants, sound recovery
brennanb2025 Oct 6, 2026
1daf4da
Merge commit '61c9a3aeab1f46035731da4e781cfc3ab2f71512' into brennanb…
brennanb2025 Oct 6, 2026
dfe9fda
Round 2: recovery reads the chat's own database, honest grant label
brennanb2025 Oct 6, 2026
92b03e0
Merge commit 'b922342199bb4c816072deb42cc58cf755420488' into brennanb…
brennanb2025 Oct 6, 2026
9fd32b9
Merge commit '81a1968bc656c0faa3731859a64f29f3adbb0ed1' into brennanb…
brennanb2025 Oct 6, 2026
6544fc7
refactor(native-chat): keep the session host within its line budget a…
brennanb2025 Oct 6, 2026
65b7212
Merge remote-tracking branch 'origin/brennanb2025/acp-d3-grok' into b…
brennanb2025 Oct 6, 2026
7a712ac
test(codex): move the stdout-before-exit test into its own file
brennanb2025 Oct 6, 2026
6f81271
Merge remote-tracking branch 'origin/brennanb2025/acp-d3-grok' into b…
brennanb2025 Oct 6, 2026
d8e585d
Keep the record's path bound, which the launch directory now uses
brennanb2025 Oct 6, 2026
06e8a81
Narrow the refusal in the prompt content test instead of casting it
brennanb2025 Oct 6, 2026
90db537
Type the replayed error answer as the protocol's error
brennanb2025 Oct 6, 2026
31a29e9
Read the replayed message through the replay's own schema
brennanb2025 Oct 6, 2026
c8f5a2f
Merge origin/main (Grok structured chat over ACP landed) into OpenCod…
brennanb2025 Oct 7, 2026
53cfa99
Match recovered OpenCode messages through main's send fingerprint
brennanb2025 Oct 7, 2026
6ac1ba1
Merge origin/main (main's lint fix #26079) into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
21c6606
Keep an `opencode` that is 2.x on the terminal chat
brennanb2025 Oct 7, 2026
c112f0e
Type the mocked login-shell environment instead of asserting it
brennanb2025 Oct 7, 2026
56d5457
Run the OpenCode the Command setting names, and say why a version che…
brennanb2025 Oct 7, 2026
23ecf99
Merge origin/main into brennanb2025/native-chat-opencode-serve
brennanb2025 Oct 7, 2026
c7628a4
Say why a new OpenCode tab opened a terminal, and ask the host again …
brennanb2025 Oct 7, 2026
0b6c3df
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
0109478
Type the mocked login-shell environment in the OpenCode probe tests i…
brennanb2025 Oct 7, 2026
53479f8
Pass the OpenCode Command and environment through the route test's st…
brennanb2025 Oct 7, 2026
8c3d733
Open OpenCode's chat on the first new tab, and learn the host's agent…
brennanb2025 Oct 7, 2026
4a1fce5
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
f39f753
Type the new-tab route request as a launch request, which carries the…
brennanb2025 Oct 7, 2026
8387321
Declare that agentSession.agents answers from the registrations in th…
brennanb2025 Oct 7, 2026
290b6ea
Wait for this computer's runtime too before routing a new tab during …
brennanb2025 Oct 7, 2026
864064d
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
66d11df
Wait for the probe that lands the runtime capabilities, not one that …
brennanb2025 Oct 7, 2026
592ae2f
Answer an OpenCode account Orca cannot pin at create support, not as …
brennanb2025 Oct 7, 2026
804b050
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
6d13791
fix(opencode): start the chat with the user's OpenCode environment as-is
brennanb2025 Oct 7, 2026
67a4cc6
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
7c518bc
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
4aa7e63
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
35b4829
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
a8c3aee
test(cross-version): build each method's params in their own file, un…
brennanb2025 Oct 7, 2026
e00b9f6
Merge remote-tracking branch 'origin/main' into brennanb2025/native-c…
brennanb2025 Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/main/acp/acp-account-binding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// How an ACP agent's chats pin their account: what a new chat records, and how a launch points the
// agent at the account its record pinned. Both run on the machine that runs the agent.

import {
isLegacyAgentSessionAccountHome,
type AgentSessionAccountHome
} from '../../shared/agent-session-account-home'
import { resolveStructuredEnvAccountHomePath } from '../runtime/structured-agent-account-home'

type AccountPin = { accountHomeVariable: string } | { accountLocatorKind: 'opencode' }

export type AcpAccountBinding = {
/** What the agent's definition says its records pin. */
pin: AccountPin
/** The account a new chat pins: the current selection, read without side effects. */
resolve(input: { launchEnv: NodeJS.ProcessEnv }): Promise<AgentSessionAccountHome>
/** `env` pointed at the pinned account; throws for an account of another kind. */
environment(home: AgentSessionAccountHome, env: Record<string, string>): Record<string, string>
}

/** An agent whose account is one config directory named by `variable`, with a default under the
* user's home. */
export function directoryAccountBinding(
variable: string,
defaultHome: (homePath: string) => string
): AcpAccountBinding {
return {
pin: { accountHomeVariable: variable },
resolve: async ({ launchEnv }) => ({
variable,
path: resolveStructuredEnvAccountHomePath({ launchEnv, variable, defaultPath: defaultHome })
}),
environment: (home, env) => {
if (!isLegacyAgentSessionAccountHome(home) || home.variable !== variable) {
throw new Error(`sessions of this agent pin ${variable}`)
}
return { ...env, [variable]: home.path }
}
}
}
6 changes: 5 additions & 1 deletion src/main/acp/acp-dialects/acp-dialect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { AgentSessionPromptResponse } from '../../../shared/agent-session-q
import type { NativeChatBackgroundTaskBlock } from '../../../shared/native-chat-types'
import type { ProviderTimelineRequestBody } from '../../native-chat/agent-session-timeline/provider-timeline-event'
import type { AcpAgentError } from '../acp-errors'
import type { ToolCallUpdate } from '../generated/acp-protocol.generated'
import type { PermissionOption, ToolCallUpdate } from '../generated/acp-protocol.generated'

export type AcpRequestPresentation = {
body: ProviderTimelineRequestBody
Expand Down Expand Up @@ -41,6 +41,10 @@ export type AcpDialectNotification =
/** Hooks interpret extensions; lifecycle and row identity stay shared. */
export type AcpDialect = {
injectedPromptIdentity?: true
/** A tool update in the shared shape (`rawOutput.stdout`, `rawOutput.exitCode`), read first. */
normalizeToolUpdate?(update: ToolCallUpdate): ToolCallUpdate
/** The words for a permission option whose own name misstates what it grants. */
permissionOptionLabel?(option: PermissionOption): string | undefined
toolName?(update: ToolCallUpdate): string | undefined
toolBackgroundTasks?(
update: ToolCallUpdate,
Expand Down
49 changes: 49 additions & 0 deletions src/main/acp/acp-dialects/opencode-dialect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { z } from 'zod'
import type { ToolCallUpdate } from '../generated/acp-protocol.generated'
import type { AcpDialect } from './acp-dialect'

// OpenCode reports a command's output and exit code under `rawOutput.metadata`; the shared shape
// is `stdout` and `exitCode`.
const commandRawOutputSchema = z.looseObject({
output: z.unknown().optional(),
metadata: z
.looseObject({
exit: z.number().int().nullable().optional(),
output: z.string().optional()
})
.optional()
})

function normalizeToolUpdate(update: ToolCallUpdate): ToolCallUpdate {
const parsed = commandRawOutputSchema.safeParse(update.rawOutput)
if (!parsed.success) {
return update
}
const rawOutput = parsed.data
const output =
typeof rawOutput.output === 'string' ? rawOutput.output : rawOutput.metadata?.output
const hasSharedOutput = ['stdout', 'stderr', 'output_for_prompt'].some(
(key) => rawOutput[key] !== undefined
)
const hasSharedExitCode = rawOutput.exitCode !== undefined || rawOutput.exit_code !== undefined
const exitCode = rawOutput.metadata?.exit ?? undefined
if ((output === undefined || hasSharedOutput) && (exitCode === undefined || hasSharedExitCode)) {
return update
}
return {
...update,
rawOutput: {
...rawOutput,
...(output === undefined || hasSharedOutput ? {} : { stdout: output }),
...(exitCode === undefined || hasSharedExitCode ? {} : { exitCode })
}
}
}

export const OPENCODE_ACP_DIALECT: AcpDialect = {
normalizeToolUpdate,
// OpenCode 1.x keeps an "always" grant in its own process, which is this chat's alone and ends
// whenever Orca ends it (a Stop, quitting, or stopping an idle chat's agent): not "always".
permissionOptionLabel: (option) =>
option.kind === 'allow_always' ? 'Allow until OpenCode restarts' : undefined
}
61 changes: 49 additions & 12 deletions src/main/acp/acp-launch-specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,26 @@ import {
import { AGENT_HOOK_RUNTIME_ENV_KEYS } from '../ipc/pty/host-env/spawn-env-keys'
import type { AcpDialect } from './acp-dialects/acp-dialect'
import { GROK_ACP_DIALECT } from './acp-dialects/grok-dialect'
import { OPENCODE_ACP_DIALECT } from './acp-dialects/opencode-dialect'
import { directoryAccountBinding, type AcpAccountBinding } from './acp-account-binding'
import { openCodeAcpAccountBinding } from '../opencode/opencode-structured-account-home'
import { scrubOpenCodeAcpEnvironment } from '../opencode/opencode-acp-environment'
import { openCodeStoredUserMessagesReader } from '../opencode/opencode-acp-stored-messages'
import type { AcpStoredUserMessagesReader } from './acp-recovery-history'
import { isStableCliVersionOnLine } from '../agent-cli-version-probe'
import type { TuiAgent } from '../../shared/tui-agent'

export type AcpLaunchSpec = {
/** The Orca agent id (a `TuiAgent`), which names the agent's records and its catalog label. */
agent: string
/** The Orca agent id, which names the agent's records, its catalog label and its settings. */
agent: TuiAgent
command: string
/** Built per launch: `fullAccess` is the Agent Permissions setting's bypass posture. */
args(input: { fullAccess: boolean }): string[]
/** Overlaid on the child's environment. */
/** Laid over the child's environment last, after the account and the user's own variables. */
env: Readonly<Record<string, string>>
/** Rewrites what the child would inherit from Orca's own plumbing; returns the keys it must not
* inherit at all. `inherited` is the environment the child process starts from. */
scrubEnvironment?(env: Record<string, string>, inherited: NodeJS.ProcessEnv): string[]
dialect: AcpDialect
/** The agent's own sign-in command, for a person to run when it reports auth required. */
loginCommand: readonly string[]
Expand All @@ -27,12 +38,17 @@ export type AcpLaunchSpec = {
advertised: readonly string[]
env: Readonly<Record<string, string>>
}): string | undefined
/** Variable naming the agent's config directory, pinned as each record's account home. */
accountHomeVariable: string
/** The config directory the agent uses when the variable is unset, under the user's home. */
defaultAccountHome(homePath: string): string
/** The account each chat pins, and how a launch points the agent at it. */
account: AcpAccountBinding
/** The `--version` releases a structured chat runs on, asked before a create and again at every
* launch; any other release keeps the terminal chat. Absent runs whatever is installed. */
supportsVersion?(version: string): boolean
/** Where the agent installs its own binary, searched after PATH. */
installDirectories(accountHomePath: string): string[]
installDirectories(input: { env: Readonly<Record<string, string>>; homePath: string }): string[]
/** Images go to the agent when it also advertises them; off sends text prompts only. */
imagePrompts?: true
/** The agent's own store of a session's user messages, read for restart recovery only. */
readStoredUserMessages?: AcpStoredUserMessagesReader
}

const GROK_LAUNCH_SPEC: AcpLaunchSpec = {
Expand All @@ -50,12 +66,33 @@ const GROK_LAUNCH_SPEC: AcpLaunchSpec = {
: advertised.includes('cached_token')
? 'cached_token'
: undefined,
accountHomeVariable: 'GROK_HOME',
defaultAccountHome: (homePath) => join(homePath, '.grok'),
installDirectories: (accountHomePath) => [join(accountHomePath, 'bin')]
account: directoryAccountBinding('GROK_HOME', (homePath) => join(homePath, '.grok')),
installDirectories: ({ env }) => (env.GROK_HOME ? [join(env.GROK_HOME, 'bin')] : [])
}

export const ACP_LAUNCH_SPECS: readonly AcpLaunchSpec[] = [GROK_LAUNCH_SPEC]
// OpenCode 1.x serves ACP in-process through `opencode acp`. OpenCode 2 (`opencode2`, and any
// `opencode` that is 2.x) is not: its `acp` runs inside the user's own background service, which a
// chat's environment and account pin do not reach, so it keeps its terminal-backed chat.
const OPENCODE_LAUNCH_SPEC: AcpLaunchSpec = {
agent: 'opencode',
command: 'opencode',
// OpenCode has no bypass flag: full access answers each permission request yes.
args: () => ['acp'],
// Applied last: the client name ACP sessions report, and no question tool, which ACP cannot
// answer (it would wait forever).
env: { OPENCODE_CLIENT: 'acp', OPENCODE_ENABLE_QUESTION_TOOL: 'false' },
scrubEnvironment: scrubOpenCodeAcpEnvironment,
dialect: OPENCODE_ACP_DIALECT,
loginCommand: ['opencode', 'auth', 'login'],
account: openCodeAcpAccountBinding(),
installDirectories: ({ homePath }) => [join(homePath, '.opencode', 'bin')],
// Stable 1.x from 1.18.31, the release the recorded sessions capture.
supportsVersion: (version) => isStableCliVersionOnLine(version, { major: 1, floor: '1.18.31' }),
imagePrompts: true,
readStoredUserMessages: openCodeStoredUserMessagesReader()
}

export const ACP_LAUNCH_SPECS: readonly AcpLaunchSpec[] = [GROK_LAUNCH_SPEC, OPENCODE_LAUNCH_SPEC]

export function acpLaunchSpecFor(agent: string): AcpLaunchSpec | null {
return ACP_LAUNCH_SPECS.find((spec) => spec.agent === agent) ?? null
Expand Down
109 changes: 109 additions & 0 deletions src/main/acp/acp-prompt-content.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { describe, expect, it } from 'vitest'
import type { AgentJournalMessageItem } from '../../shared/agent-session-journal-types'
import type { NativeChatBlock } from '../../shared/native-chat-types'
import {
ACP_PROMPT_IMAGE_MAX_BYTES,
AcpPromptContentError,
acpPromptBlocks
} from './acp-prompt-content'

const PNG = Buffer.from('89504e470d0a1a0a', 'hex')
const dir = mkdtempSync(join(tmpdir(), 'acp-prompt-content-'))
const pngPath = join(dir, 'shot.png')
writeFileSync(pngPath, PNG)

function message(...blocks: NativeChatBlock[]): AgentJournalMessageItem {
return { kind: 'message', role: 'user', blocks }
}

async function refusal(body: AgentJournalMessageItem, images = true) {
const error = await acpPromptBlocks(body, images).catch((caught: unknown) => caught)
if (!(error instanceof AcpPromptContentError)) {
throw new Error(`expected a refusal, got ${String(error)}`)
}
return error.failure
}

describe('ACP prompt content', () => {
it('sends text as text blocks', async () => {
await expect(acpPromptBlocks(message({ type: 'text', text: 'hi' }), false)).resolves.toEqual([
{ type: 'text', text: 'hi' }
])
})

it('sends a file or data image as an ACP image block, in order', async () => {
const data = PNG.toString('base64')
await expect(
acpPromptBlocks(
message(
{ type: 'image-ref', path: pngPath },
{ type: 'text', text: 'what is this?' },
{ type: 'image-ref', url: pathToFileURL(pngPath).href },
{ type: 'image-ref', url: `data:image/png;base64,${data}` }
),
true
)
).resolves.toEqual([
{ type: 'image', mimeType: 'image/png', data },
{ type: 'text', text: 'what is this?' },
{ type: 'image', mimeType: 'image/png', data },
{ type: 'image', mimeType: 'image/png', data }
])
})

it('refuses an image when the agent takes none', async () => {
expect(await refusal(message({ type: 'image-ref', path: pngPath }), false)).toMatchObject({
kind: 'attachmentInvalid',
attachment: { reason: 'unsupportedType' }
})
})

it('refuses what it cannot read or send before anything leaves Orca', async () => {
expect(await refusal(message({ type: 'image-ref', path: 'relative.png' }))).toMatchObject({
attachment: { reason: 'noSource' }
})
expect(await refusal(message({ type: 'image-ref', path: join(dir, 'a.bmp') }))).toMatchObject({
attachment: { reason: 'unsupportedType' }
})
expect(await refusal(message({ type: 'image-ref', path: `${dir}.png` }))).toMatchObject({
kind: 'attachmentUnreadable'
})
expect(
await refusal(message({ type: 'image-ref', url: 'data:image/svg+xml;base64,PHN2Zz4=' }))
).toMatchObject({ attachment: { reason: 'unsupportedType' } })
})

it('keeps a message with images within one protocol line', async () => {
// The images fit their raw limit, but with this much text the prompt would pass the line.
const text = 'x'.repeat(8 * 1024 * 1024)
const image = {
type: 'image-ref' as const,
url: `data:image/png;base64,${PNG.toString('base64')}`
}
expect(
await refusal(message({ type: 'text', text }, { type: 'text', text }, image))
).toMatchObject({
attachment: { reason: 'totalTooLarge' }
})
expect(ACP_PROMPT_IMAGE_MAX_BYTES * (4 / 3)).toBeLessThan(16 * 1024 * 1024)
})

it('leaves a text-only message to the agent, however long', async () => {
const text = 'x'.repeat(9 * 1024 * 1024)
await expect(
acpPromptBlocks(message({ type: 'text', text }, { type: 'text', text }), true)
).resolves.toHaveLength(2)
})

it("bounds one message's images together", async () => {
const big = Buffer.alloc(ACP_PROMPT_IMAGE_MAX_BYTES / 2 + 1).toString('base64')
const image = { type: 'image-ref' as const, url: `data:image/png;base64,${big}` }
expect(await refusal(message(image, image))).toMatchObject({
attachment: { reason: 'totalTooLarge', limit: ACP_PROMPT_IMAGE_MAX_BYTES }
})
})
})
Loading
Loading