@agentex/agent 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +484 -1
- package/README.md +23 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/claude/parse.d.ts +3 -0
- package/dist/providers/claude/parse.d.ts.map +1 -1
- package/dist/providers/claude/parse.js +23 -1
- package/dist/providers/claude/parse.js.map +1 -1
- package/dist/providers/claude/session.d.ts +174 -4
- package/dist/providers/claude/session.d.ts.map +1 -1
- package/dist/providers/claude/session.js +541 -29
- package/dist/providers/claude/session.js.map +1 -1
- package/dist/providers/codex/execute.d.ts.map +1 -1
- package/dist/providers/codex/execute.js +3 -0
- package/dist/providers/codex/execute.js.map +1 -1
- package/dist/providers/codex/parse.d.ts +12 -1
- package/dist/providers/codex/parse.d.ts.map +1 -1
- package/dist/providers/codex/parse.js +21 -0
- package/dist/providers/codex/parse.js.map +1 -1
- package/dist/providers/codex/session.d.ts.map +1 -1
- package/dist/providers/codex/session.js +21 -1
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/providers/opencode/event-parse.d.ts +8 -2
- package/dist/providers/opencode/event-parse.d.ts.map +1 -1
- package/dist/providers/opencode/event-parse.js +22 -11
- package/dist/providers/opencode/event-parse.js.map +1 -1
- package/dist/providers/opencode/http-session.js +13 -0
- package/dist/providers/opencode/http-session.js.map +1 -1
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/providers/claude/parse.ts +23 -1
- package/src/providers/claude/session.ts +556 -30
- package/src/providers/codex/execute.ts +3 -0
- package/src/providers/codex/parse.ts +25 -0
- package/src/providers/codex/session.ts +24 -1
- package/src/providers/opencode/event-parse.ts +27 -13
- package/src/providers/opencode/http-session.ts +13 -0
- package/src/types.ts +97 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,245 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.37 — Turn liveness and background-task identity (Claude)
|
|
4
|
+
|
|
5
|
+
Claude Code starts turns by itself. When a background task finishes, the CLI
|
|
6
|
+
enqueues the notification as user input, which opens a fresh turn with no host
|
|
7
|
+
involvement. A host that tracks "is the agent working" from its own `send()`
|
|
8
|
+
cannot see those turns — `send()` already resolved — so the session reads as
|
|
9
|
+
finished while the agent is visibly working. Verified against Claude Code
|
|
10
|
+
2.1.241, where one user message produced two `result` events with a
|
|
11
|
+
self-started turn between them.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`turn_start` StreamEvent.** Pairs with `result`, which closes a turn.
|
|
16
|
+
Carries `trigger: "send" | "resume"` — `resume` meaning the provider opened
|
|
17
|
+
the turn on its own. Emitted for host-initiated turns too, so `turn_start` →
|
|
18
|
+
`result` describes turn liveness straight off the stream rather than by
|
|
19
|
+
inference from dispatch.
|
|
20
|
+
|
|
21
|
+
It deliberately does not name the background task behind a `resume`. Claude
|
|
22
|
+
delivers a task's result and opens the turn as two unlinked records, and with
|
|
23
|
+
several tasks in flight the pairing is not recoverable from the wire; every
|
|
24
|
+
attempt to infer it produced a plausible id that was sometimes wrong.
|
|
25
|
+
Correlate through `background_task.report` and `toolUseId`, which the
|
|
26
|
+
provider does state.
|
|
27
|
+
|
|
28
|
+
Attribution is exact, not inferred. A host message carries a uuid and a
|
|
29
|
+
provider-initiated continuation does not: every dequeued input with a uuid
|
|
30
|
+
emits `command_lifecycle` naming it, while the task-notification continuation
|
|
31
|
+
is enqueued without one. So a turn opened by a `started` naming an
|
|
32
|
+
outstanding message *is* that message's turn. A build that has never emitted
|
|
33
|
+
`command_lifecycle` falls back to the oldest unclaimed send; once one has
|
|
34
|
+
been seen, the fallback is disabled, because guessing there would mislabel
|
|
35
|
+
twice.
|
|
36
|
+
|
|
37
|
+
- **`turn_end` StreamEvent.** Every `turn_start` is followed by exactly one,
|
|
38
|
+
carrying the same `turnId` and a `reason`. `result` cannot serve as the close
|
|
39
|
+
signal on its own: a message the CLI cancels, discards, or refuses opens a
|
|
40
|
+
turn and produces no result, so a host pairing `turn_start` with `result`
|
|
41
|
+
would stay busy forever on those paths. `result` remains the outcome payload
|
|
42
|
+
and is ordered before the `turn_end` that follows it.
|
|
43
|
+
|
|
44
|
+
- **`background_task.report`.** The task's delivered output (`summary`,
|
|
45
|
+
`outputFile`, `usage`), present only on the event that hands the result
|
|
46
|
+
back. Claude emits *both* `task_updated` and `task_notification` for a single
|
|
47
|
+
completion; they are different records, not duplicates, and only the
|
|
48
|
+
notification carries the result. Collapsing them into one indistinguishable
|
|
49
|
+
`phase: "completed"` event made hosts render every finished task twice, once
|
|
50
|
+
with its report and once empty. A task that was stopped or killed delivered
|
|
51
|
+
nothing and carries no report. Codex applies the identical rule at every
|
|
52
|
+
emitter, so "one row per delivered result" holds across providers.
|
|
53
|
+
|
|
54
|
+
- **`background_task.toolUseId`.** The tool call that launched the task — the
|
|
55
|
+
same id Claude writes into a subagent's `meta.json`, and the only structured
|
|
56
|
+
task-to-tool_call link the wire provides. It was being discarded.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
61
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
62
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
63
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
64
|
+
have its sends resolved by that handler — the failure that made two
|
|
65
|
+
back-to-back results resolve both callers with the first result.
|
|
66
|
+
|
|
67
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
68
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
69
|
+
Modelling one command per turn left every coalesced message permanently
|
|
70
|
+
unsettled.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- A background task's `taskType` and `description` survive its whole lifetime.
|
|
75
|
+
`task_started` is the only record that names them; the patches and the
|
|
76
|
+
completion notification that follow identify the task by id alone, so every
|
|
77
|
+
finished subagent normalized to `taskType: "unknown"` and hosts rendered
|
|
78
|
+
"Background task completed" for what was plainly a subagent. A completion for
|
|
79
|
+
a task the session never saw start still reports `unknown` rather than
|
|
80
|
+
guessing. The cache is bounded at 512 entries, oldest evicted.
|
|
81
|
+
|
|
82
|
+
- A detached subagent's own output no longer drives the parent session. Claude
|
|
83
|
+
streams a child's assistant text, thinking, and tool calls onto the parent
|
|
84
|
+
stream while the root turn is already over. Anything carrying
|
|
85
|
+
`parent_tool_use_id` is the child working, not the session: it does not open
|
|
86
|
+
a turn, does not move `session.state`, and a child's permission prompt no
|
|
87
|
+
longer parks the parent in `waiting_for_approval` with nothing able to clear
|
|
88
|
+
it.
|
|
89
|
+
|
|
90
|
+
- `session.state` and `turn_start`/`result` agree. A host turn took `thinking`
|
|
91
|
+
from `send()`; a provider-initiated one had nothing to set it, so `state`
|
|
92
|
+
read `idle` for the whole head of every resume turn while `turn_start` had
|
|
93
|
+
already fired.
|
|
94
|
+
|
|
95
|
+
- Turn settling is synchronous with the `result` line. The CLI can flush a
|
|
96
|
+
result and the next turn's opening line in one chunk; deferring the close
|
|
97
|
+
until the event chain drained swallowed the following `turn_start`, and
|
|
98
|
+
deferring the turn's send-resolvers with it let the next turn mistake them
|
|
99
|
+
for its own. The resolvers are appended to a settling list, never assigned
|
|
100
|
+
over — overwriting discarded the earlier turn's resolvers outright, hanging
|
|
101
|
+
that caller's `send()` and deadlocking `drain()` behind it.
|
|
102
|
+
|
|
103
|
+
- Background-task state is tracked from the wire, not from the delivery path.
|
|
104
|
+
It was maintained inside event dispatch, which only runs when a host
|
|
105
|
+
subscribed, so `session.state`, `drain()`, and turn attribution silently
|
|
106
|
+
degraded for a host that reads the session without an `onEvent` handler.
|
|
107
|
+
|
|
108
|
+
- A send resolves with its own turn. `_pendingResults` was a flat queue drained
|
|
109
|
+
by whatever `result` landed next, so a follow-up the CLI had merely queued
|
|
110
|
+
resolved against a turn that never contained it — the host then read
|
|
111
|
+
"finished" for a message still waiting to run. Each entry now carries its
|
|
112
|
+
command uuid and settles only when the turn that claimed it completes. A
|
|
113
|
+
terminal `command_lifecycle` settles a message the CLI retires without
|
|
114
|
+
running, which previously had nothing to settle it at all.
|
|
115
|
+
|
|
116
|
+
- `drain()` waits for provider-initiated turns and for running subagents.
|
|
117
|
+
`_inFlight` only tracks turns the host dispatched, so draining during the gap
|
|
118
|
+
between a root result and the resume turn that follows it — measured at 8-11
|
|
119
|
+
seconds live — SIGTERM'd the CLI and killed the child outright. Background
|
|
120
|
+
*processes* are deliberately excluded: a dev server started with
|
|
121
|
+
`run_in_background` may never exit, and the contract is "let the agent's work
|
|
122
|
+
settle", not "outlive whatever it launched". Bounded by a deadline so a
|
|
123
|
+
wedged turn cannot hang the drain.
|
|
124
|
+
|
|
125
|
+
It also holds across the gap between a task's result being delivered and the
|
|
126
|
+
resume turn it triggers — 24ms for a subagent, 71ms for a background process.
|
|
127
|
+
The task is no longer live by then, so waiting on live tasks alone left
|
|
128
|
+
`drain()` landing in that window and killing the very turn it was extended to
|
|
129
|
+
protect. The delivery record is the provider stating a turn is coming, so it
|
|
130
|
+
is used as one; the wait is released by the next turn to open or close.
|
|
131
|
+
|
|
132
|
+
- Turns opened by a command that ends in `refused`, `discarded`, or `cancelled`
|
|
133
|
+
are closed by that record. Those states never produce a `result`, so nothing
|
|
134
|
+
else would ever close them and the session pinned as working with no path
|
|
135
|
+
back.
|
|
136
|
+
|
|
137
|
+
- Wire lines arriving after `close()` are ignored, and `close()` rejects sends
|
|
138
|
+
still waiting on a turn instead of stranding the caller forever.
|
|
139
|
+
|
|
140
|
+
- `turn_start` carries the `eventId` of the line that opened it, and a minimal
|
|
141
|
+
`raw`. A resume turn is headed by `system/init`, whose payload runs to ~5KB
|
|
142
|
+
of tools, skills, plugins, and MCP config; echoing it whole made hosts that
|
|
143
|
+
persist `raw` pay that for every resume, twice.
|
|
144
|
+
|
|
145
|
+
### Compatibility
|
|
146
|
+
|
|
147
|
+
- Additive at the type level. `turn_start` is a new event type — consumers with
|
|
148
|
+
exhaustive `switch` statements over `StreamEvent` will need a case or a
|
|
149
|
+
default. `report` and `toolUseId` are new required fields on
|
|
150
|
+
`background_task`; every in-tree provider sets them, and both are `null`
|
|
151
|
+
where the provider reports nothing.
|
|
152
|
+
- No behavior change for OpenCode, Cursor, or any other provider. Codex gains
|
|
153
|
+
`report`/`toolUseId` and is otherwise untouched; only the Claude provider
|
|
154
|
+
emits `turn_start`.
|
|
155
|
+
|
|
156
|
+
## 0.0.36 — OpenCode empty-turn follow-ups
|
|
157
|
+
|
|
158
|
+
Follow-ups to the OpenCode empty-turn handling in 0.0.35.
|
|
159
|
+
|
|
160
|
+
### Changed
|
|
161
|
+
|
|
162
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
163
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
164
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
165
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
166
|
+
have its sends resolved by that handler — the failure that made two
|
|
167
|
+
back-to-back results resolve both callers with the first result.
|
|
168
|
+
|
|
169
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
170
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
171
|
+
Modelling one command per turn left every coalesced message permanently
|
|
172
|
+
unsettled.
|
|
173
|
+
|
|
174
|
+
### Fixed
|
|
175
|
+
|
|
176
|
+
- The empty-turn classifier's "finished" guard now lives in `terminalOutcome`,
|
|
177
|
+
so the live and reconcile paths agree. The live path called it unconditionally
|
|
178
|
+
on the `/message` response, while the reconcile path gated on the message
|
|
179
|
+
having a finish reason or an error first. With `info` absent (an empty or
|
|
180
|
+
malformed body) `terminalOutcome` walked to `failed`/`incomplete_turn` but
|
|
181
|
+
suppressed the note — which needs `info` — producing a failed turn with
|
|
182
|
+
nothing in the transcript explaining it, the exact silent stall the 0.0.35
|
|
183
|
+
change removes, reached from the other side. A message with neither a finish
|
|
184
|
+
reason nor an error now stays `completed`.
|
|
185
|
+
- `_messageRoles` (the map that suppresses the prompt echo) is now cleared at
|
|
186
|
+
turn end instead of never. It sat next to the per-turn dedup maps but was
|
|
187
|
+
omitted from their turn-start reset, and could not join it: a user message's
|
|
188
|
+
role has to survive from its `message.updated` frame into the part stream that
|
|
189
|
+
follows within the same turn, so clearing at the start would reintroduce the
|
|
190
|
+
echo. Clearing at turn end bounds the map to one turn's messages instead of
|
|
191
|
+
growing for the session's whole life.
|
|
192
|
+
- A user interrupt performed through OpenCode's own UI now reports as `aborted`,
|
|
193
|
+
not `failed`. When OpenCode records a `MessageAbortedError`, the classifier
|
|
194
|
+
maps it to `status: "aborted"` with a plain message, matching self-aborts and
|
|
195
|
+
Codex's handling of the same action, rather than `agent_error` with a
|
|
196
|
+
JSON-stringified error object.
|
|
197
|
+
|
|
198
|
+
- `session.state` no longer follows a detached child. The state machine set
|
|
199
|
+
`thinking` on any assistant line, including a subagent's, so `state` and
|
|
200
|
+
`turn_start`/`result` contradicted each other for the child's entire run —
|
|
201
|
+
measured at 7.6s live — with nothing to clear it if the child was stopped or
|
|
202
|
+
killed.
|
|
203
|
+
- `stream_event` opens a turn. Under `includePartialMessages` the whole
|
|
204
|
+
streamed reply arrived before `turn_start`, which reintroduced the reported
|
|
205
|
+
bug one layer down: output visible while the session still read as finished.
|
|
206
|
+
- A `send()` issued while a turn is settling is no longer classified as
|
|
207
|
+
`resume`. The trigger reads a counter of host messages still awaiting a
|
|
208
|
+
turn; the resolver list cannot answer that, because a turn's resolvers are
|
|
209
|
+
moved off it the moment its `result` is read.
|
|
210
|
+
- `_pendingResumeTaskId` is consumed only by the resume turn it explains. It
|
|
211
|
+
was cleared on every turn open, so a host send landing between a task's
|
|
212
|
+
delivery and its resume turn wiped the attribution.
|
|
213
|
+
- Turn state, the task-fact cache, and the unclaimed-send counter are all
|
|
214
|
+
reset when a session's pending work is rejected (exit, crash, close). A
|
|
215
|
+
`_turnOpen` left set would also suppress the `idle` fallback in
|
|
216
|
+
`handleResult` and pin a dead session as working with no path back.
|
|
217
|
+
|
|
218
|
+
### Compatibility
|
|
219
|
+
|
|
220
|
+
- The incomplete-turn note is emitted as `type: "assistant"` (the only surface a
|
|
221
|
+
host renders) and tagged `raw.synthetic: "incomplete_turn"`. It is
|
|
222
|
+
library-authored prose, not model output: a host that replays transcript
|
|
223
|
+
history back into a model (context rebuilding, summarization) should filter
|
|
224
|
+
events carrying `raw.synthetic` so the note is never fed back as assistant turn
|
|
225
|
+
content.
|
|
226
|
+
|
|
3
227
|
## 0.0.35 — Codex turn-boundary correctness and model discovery
|
|
4
228
|
|
|
229
|
+
### Changed
|
|
230
|
+
|
|
231
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
232
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
233
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
234
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
235
|
+
have its sends resolved by that handler — the failure that made two
|
|
236
|
+
back-to-back results resolve both callers with the first result.
|
|
237
|
+
|
|
238
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
239
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
240
|
+
Modelling one command per turn left every coalesced message permanently
|
|
241
|
+
unsettled.
|
|
242
|
+
|
|
5
243
|
### Fixed
|
|
6
244
|
|
|
7
245
|
- A Codex `send()` issued while the previous turn's result was still being
|
|
@@ -120,7 +358,12 @@
|
|
|
120
358
|
- OpenCode's live terminal `result` event now uses the same `${messageId}:result`
|
|
121
359
|
id as the reconcile path. The two paths previously produced different ids for
|
|
122
360
|
the same turn terminus, so a reconcile after a live turn appended a duplicate
|
|
123
|
-
terminal marker instead of deduping against the one already written.
|
|
361
|
+
terminal marker instead of deduping against the one already written. Migration:
|
|
362
|
+
a host that already persisted a terminal row under the old bare `msg_x` id
|
|
363
|
+
won't dedup it against the incoming `msg_x:result`, so the first catch-up after
|
|
364
|
+
upgrading appends one duplicate terminal marker per previously-recorded live
|
|
365
|
+
turn. One-time and bounded; terminal `result` rows are analytics-only and not
|
|
366
|
+
rendered.
|
|
124
367
|
|
|
125
368
|
### Added
|
|
126
369
|
|
|
@@ -141,6 +384,26 @@
|
|
|
141
384
|
contract test now checks both directions so a provider cannot declare
|
|
142
385
|
discovery it does not implement, or implement discovery it does not declare.
|
|
143
386
|
|
|
387
|
+
- `session.state` no longer follows a detached child. The state machine set
|
|
388
|
+
`thinking` on any assistant line, including a subagent's, so `state` and
|
|
389
|
+
`turn_start`/`result` contradicted each other for the child's entire run —
|
|
390
|
+
measured at 7.6s live — with nothing to clear it if the child was stopped or
|
|
391
|
+
killed.
|
|
392
|
+
- `stream_event` opens a turn. Under `includePartialMessages` the whole
|
|
393
|
+
streamed reply arrived before `turn_start`, which reintroduced the reported
|
|
394
|
+
bug one layer down: output visible while the session still read as finished.
|
|
395
|
+
- A `send()` issued while a turn is settling is no longer classified as
|
|
396
|
+
`resume`. The trigger reads a counter of host messages still awaiting a
|
|
397
|
+
turn; the resolver list cannot answer that, because a turn's resolvers are
|
|
398
|
+
moved off it the moment its `result` is read.
|
|
399
|
+
- `_pendingResumeTaskId` is consumed only by the resume turn it explains. It
|
|
400
|
+
was cleared on every turn open, so a host send landing between a task's
|
|
401
|
+
delivery and its resume turn wiped the attribution.
|
|
402
|
+
- Turn state, the task-fact cache, and the unclaimed-send counter are all
|
|
403
|
+
reset when a session's pending work is rejected (exit, crash, close). A
|
|
404
|
+
`_turnOpen` left set would also suppress the `idle` fallback in
|
|
405
|
+
`handleResult` and pin a dead session as working with no path back.
|
|
406
|
+
|
|
144
407
|
### Compatibility
|
|
145
408
|
|
|
146
409
|
- Discovery is curated-list-plus-validation for Claude, not enumeration. A tier
|
|
@@ -187,6 +450,20 @@
|
|
|
187
450
|
|
|
188
451
|
## 0.0.34 - Codex collaboration task lifecycle
|
|
189
452
|
|
|
453
|
+
### Changed
|
|
454
|
+
|
|
455
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
456
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
457
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
458
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
459
|
+
have its sends resolved by that handler — the failure that made two
|
|
460
|
+
back-to-back results resolve both callers with the first result.
|
|
461
|
+
|
|
462
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
463
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
464
|
+
Modelling one command per turn left every coalesced message permanently
|
|
465
|
+
unsettled.
|
|
466
|
+
|
|
190
467
|
### Fixed
|
|
191
468
|
|
|
192
469
|
- Codex 0.144 collaboration tool calls now register spawned child threads as
|
|
@@ -199,6 +476,26 @@
|
|
|
199
476
|
child-thread, and reconciliation signals. Session shutdown also closes any
|
|
200
477
|
still-active child lifecycle instead of leaving a permanent running state.
|
|
201
478
|
|
|
479
|
+
- `session.state` no longer follows a detached child. The state machine set
|
|
480
|
+
`thinking` on any assistant line, including a subagent's, so `state` and
|
|
481
|
+
`turn_start`/`result` contradicted each other for the child's entire run —
|
|
482
|
+
measured at 7.6s live — with nothing to clear it if the child was stopped or
|
|
483
|
+
killed.
|
|
484
|
+
- `stream_event` opens a turn. Under `includePartialMessages` the whole
|
|
485
|
+
streamed reply arrived before `turn_start`, which reintroduced the reported
|
|
486
|
+
bug one layer down: output visible while the session still read as finished.
|
|
487
|
+
- A `send()` issued while a turn is settling is no longer classified as
|
|
488
|
+
`resume`. The trigger reads a counter of host messages still awaiting a
|
|
489
|
+
turn; the resolver list cannot answer that, because a turn's resolvers are
|
|
490
|
+
moved off it the moment its `result` is read.
|
|
491
|
+
- `_pendingResumeTaskId` is consumed only by the resume turn it explains. It
|
|
492
|
+
was cleared on every turn open, so a host send landing between a task's
|
|
493
|
+
delivery and its resume turn wiped the attribution.
|
|
494
|
+
- Turn state, the task-fact cache, and the unclaimed-send counter are all
|
|
495
|
+
reset when a session's pending work is rejected (exit, crash, close). A
|
|
496
|
+
`_turnOpen` left set would also suppress the `idle` fallback in
|
|
497
|
+
`handleResult` and pin a dead session as working with no path back.
|
|
498
|
+
|
|
202
499
|
### Compatibility
|
|
203
500
|
|
|
204
501
|
- The change is isolated to Codex collaboration events. Claude and the other
|
|
@@ -222,6 +519,20 @@
|
|
|
222
519
|
`getClaudeTaskDetails()` remains available for Claude-native fields and
|
|
223
520
|
accepts legacy `unknown` task events from older agentex versions.
|
|
224
521
|
|
|
522
|
+
### Changed
|
|
523
|
+
|
|
524
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
525
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
526
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
527
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
528
|
+
have its sends resolved by that handler — the failure that made two
|
|
529
|
+
back-to-back results resolve both callers with the first result.
|
|
530
|
+
|
|
531
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
532
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
533
|
+
Modelling one command per turn left every coalesced message permanently
|
|
534
|
+
unsettled.
|
|
535
|
+
|
|
225
536
|
### Fixed
|
|
226
537
|
|
|
227
538
|
- Codex live sessions now track the active root turn and interrupt it with the
|
|
@@ -235,6 +546,26 @@
|
|
|
235
546
|
as `aborted`. Interrupt RPC errors propagate to the caller so hosts can show a
|
|
236
547
|
failed Stop action instead of reporting false success.
|
|
237
548
|
|
|
549
|
+
- `session.state` no longer follows a detached child. The state machine set
|
|
550
|
+
`thinking` on any assistant line, including a subagent's, so `state` and
|
|
551
|
+
`turn_start`/`result` contradicted each other for the child's entire run —
|
|
552
|
+
measured at 7.6s live — with nothing to clear it if the child was stopped or
|
|
553
|
+
killed.
|
|
554
|
+
- `stream_event` opens a turn. Under `includePartialMessages` the whole
|
|
555
|
+
streamed reply arrived before `turn_start`, which reintroduced the reported
|
|
556
|
+
bug one layer down: output visible while the session still read as finished.
|
|
557
|
+
- A `send()` issued while a turn is settling is no longer classified as
|
|
558
|
+
`resume`. The trigger reads a counter of host messages still awaiting a
|
|
559
|
+
turn; the resolver list cannot answer that, because a turn's resolvers are
|
|
560
|
+
moved off it the moment its `result` is read.
|
|
561
|
+
- `_pendingResumeTaskId` is consumed only by the resume turn it explains. It
|
|
562
|
+
was cleared on every turn open, so a host send landing between a task's
|
|
563
|
+
delivery and its resume turn wiped the attribution.
|
|
564
|
+
- Turn state, the task-fact cache, and the unclaimed-send counter are all
|
|
565
|
+
reset when a session's pending work is rejected (exit, crash, close). A
|
|
566
|
+
`_turnOpen` left set would also suppress the `idle` fallback in
|
|
567
|
+
`handleResult` and pin a dead session as working with no path back.
|
|
568
|
+
|
|
238
569
|
### Compatibility
|
|
239
570
|
|
|
240
571
|
- Timeout and AbortSignal cancellation remain best-effort and preserve their
|
|
@@ -244,6 +575,20 @@
|
|
|
244
575
|
|
|
245
576
|
## 0.0.32 — Codex root-thread completion isolation
|
|
246
577
|
|
|
578
|
+
### Changed
|
|
579
|
+
|
|
580
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
581
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
582
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
583
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
584
|
+
have its sends resolved by that handler — the failure that made two
|
|
585
|
+
back-to-back results resolve both callers with the first result.
|
|
586
|
+
|
|
587
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
588
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
589
|
+
Modelling one command per turn left every coalesced message permanently
|
|
590
|
+
unsettled.
|
|
591
|
+
|
|
247
592
|
### Fixed
|
|
248
593
|
|
|
249
594
|
- Codex live sessions now pin their root thread and ignore notifications from
|
|
@@ -260,6 +605,26 @@
|
|
|
260
605
|
- Live Codex event identity prefers the event's own thread scope while retaining
|
|
261
606
|
the pinned root id as a compatibility fallback for older unscoped events.
|
|
262
607
|
|
|
608
|
+
- `session.state` no longer follows a detached child. The state machine set
|
|
609
|
+
`thinking` on any assistant line, including a subagent's, so `state` and
|
|
610
|
+
`turn_start`/`result` contradicted each other for the child's entire run —
|
|
611
|
+
measured at 7.6s live — with nothing to clear it if the child was stopped or
|
|
612
|
+
killed.
|
|
613
|
+
- `stream_event` opens a turn. Under `includePartialMessages` the whole
|
|
614
|
+
streamed reply arrived before `turn_start`, which reintroduced the reported
|
|
615
|
+
bug one layer down: output visible while the session still read as finished.
|
|
616
|
+
- A `send()` issued while a turn is settling is no longer classified as
|
|
617
|
+
`resume`. The trigger reads a counter of host messages still awaiting a
|
|
618
|
+
turn; the resolver list cannot answer that, because a turn's resolvers are
|
|
619
|
+
moved off it the moment its `result` is read.
|
|
620
|
+
- `_pendingResumeTaskId` is consumed only by the resume turn it explains. It
|
|
621
|
+
was cleared on every turn open, so a host send landing between a task's
|
|
622
|
+
delivery and its resume turn wiped the attribution.
|
|
623
|
+
- Turn state, the task-fact cache, and the unclaimed-send counter are all
|
|
624
|
+
reset when a session's pending work is rejected (exit, crash, close). A
|
|
625
|
+
`_turnOpen` left set would also suppress the `idle` fallback in
|
|
626
|
+
`handleResult` and pin a dead session as working with no path back.
|
|
627
|
+
|
|
263
628
|
### Compatibility
|
|
264
629
|
|
|
265
630
|
- Unscoped global Codex notifications continue to flow through. The documented
|
|
@@ -313,6 +678,20 @@
|
|
|
313
678
|
|
|
314
679
|
## 0.0.30 — Complete Claude and Codex host capabilities
|
|
315
680
|
|
|
681
|
+
### Changed
|
|
682
|
+
|
|
683
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
684
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
685
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
686
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
687
|
+
have its sends resolved by that handler — the failure that made two
|
|
688
|
+
back-to-back results resolve both callers with the first result.
|
|
689
|
+
|
|
690
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
691
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
692
|
+
Modelling one command per turn left every coalesced message permanently
|
|
693
|
+
unsettled.
|
|
694
|
+
|
|
316
695
|
### Fixed
|
|
317
696
|
|
|
318
697
|
- Claude and Codex now declare the resumability, permission, question, and
|
|
@@ -447,6 +826,26 @@ remain source-compatible.
|
|
|
447
826
|
- Codex endpoint header names are emitted as quoted TOML path segments, so
|
|
448
827
|
valid names containing dots cannot become nested configuration keys.
|
|
449
828
|
|
|
829
|
+
- `session.state` no longer follows a detached child. The state machine set
|
|
830
|
+
`thinking` on any assistant line, including a subagent's, so `state` and
|
|
831
|
+
`turn_start`/`result` contradicted each other for the child's entire run —
|
|
832
|
+
measured at 7.6s live — with nothing to clear it if the child was stopped or
|
|
833
|
+
killed.
|
|
834
|
+
- `stream_event` opens a turn. Under `includePartialMessages` the whole
|
|
835
|
+
streamed reply arrived before `turn_start`, which reintroduced the reported
|
|
836
|
+
bug one layer down: output visible while the session still read as finished.
|
|
837
|
+
- A `send()` issued while a turn is settling is no longer classified as
|
|
838
|
+
`resume`. The trigger reads a counter of host messages still awaiting a
|
|
839
|
+
turn; the resolver list cannot answer that, because a turn's resolvers are
|
|
840
|
+
moved off it the moment its `result` is read.
|
|
841
|
+
- `_pendingResumeTaskId` is consumed only by the resume turn it explains. It
|
|
842
|
+
was cleared on every turn open, so a host send landing between a task's
|
|
843
|
+
delivery and its resume turn wiped the attribution.
|
|
844
|
+
- Turn state, the task-fact cache, and the unclaimed-send counter are all
|
|
845
|
+
reset when a session's pending work is rejected (exit, crash, close). A
|
|
846
|
+
`_turnOpen` left set would also suppress the `idle` fallback in
|
|
847
|
+
`handleResult` and pin a dead session as working with no path back.
|
|
848
|
+
|
|
450
849
|
### Compatibility and limits
|
|
451
850
|
|
|
452
851
|
- OpenCode 1.3.2 is the release-tested server schema. Safe disconnect uses
|
|
@@ -463,6 +862,20 @@ remain source-compatible.
|
|
|
463
862
|
|
|
464
863
|
## 0.0.27 — Codex session reasoning effort
|
|
465
864
|
|
|
865
|
+
### Changed
|
|
866
|
+
|
|
867
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
868
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
869
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
870
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
871
|
+
have its sends resolved by that handler — the failure that made two
|
|
872
|
+
back-to-back results resolve both callers with the first result.
|
|
873
|
+
|
|
874
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
875
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
876
|
+
Modelling one command per turn left every coalesced message permanently
|
|
877
|
+
unsettled.
|
|
878
|
+
|
|
466
879
|
### Fixed
|
|
467
880
|
|
|
468
881
|
- **Codex session reasoning effort.** Multi-turn Codex sessions now forward
|
|
@@ -588,6 +1001,20 @@ invisible to callers. `getProvider` stays synchronous; only heavy modules
|
|
|
588
1001
|
The loader defaults to the built-in `acpProvider`; `registerAcpFactory` stays
|
|
589
1002
|
exported and honored as an override hook.
|
|
590
1003
|
|
|
1004
|
+
### Changed
|
|
1005
|
+
|
|
1006
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
1007
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
1008
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
1009
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
1010
|
+
have its sends resolved by that handler — the failure that made two
|
|
1011
|
+
back-to-back results resolve both callers with the first result.
|
|
1012
|
+
|
|
1013
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
1014
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
1015
|
+
Modelling one command per turn left every coalesced message permanently
|
|
1016
|
+
unsettled.
|
|
1017
|
+
|
|
591
1018
|
### Fixed
|
|
592
1019
|
|
|
593
1020
|
- **TDZ crash on direct provider import.** `import("@agentex/agent/providers/gemini")`
|
|
@@ -651,6 +1078,20 @@ A session-scoped **goal** primitive. Attach a durable objective and the library
|
|
|
651
1078
|
- **`GoalController` + reconstruction helpers**, exported for hosts: `goalStateFromEvent`, `latestGoalFromEvents`, `normalizeClaudeGoalAttachment`, `normalizeCodexGoalStatus`, `normalizeCodexGoalRecord`, `createDefaultSentinel`, `parseAssessment`, `isTerminalGoalStatus`, `EMULATED_GOAL_CAPABILITY`, `GOAL_OBJECTIVE_MAX`, `CODEX_GOAL_TOOLS`, plus the `GoalState` / `GoalStatus` / `GoalOptions` / `GoalSentinel` / `SetGoalResult` / `ClearGoalResult` types.
|
|
652
1079
|
- **Native observability + resume.** Claude writes `goal_status` only to the on-disk transcript (never live stdout), so a native goal session tails its transcript to surface `active`→`met` and restores an unmet goal on `--resume`. Codex rehydrates a durable goal on resume via `thread/goal/get`. Both confirmed live.
|
|
653
1080
|
|
|
1081
|
+
### Changed
|
|
1082
|
+
|
|
1083
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
1084
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
1085
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
1086
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
1087
|
+
have its sends resolved by that handler — the failure that made two
|
|
1088
|
+
back-to-back results resolve both callers with the first result.
|
|
1089
|
+
|
|
1090
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
1091
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
1092
|
+
Modelling one command per turn left every coalesced message permanently
|
|
1093
|
+
unsettled.
|
|
1094
|
+
|
|
654
1095
|
### Fixed
|
|
655
1096
|
|
|
656
1097
|
- **Codex failed turns are no longer reported as success.** codex 0.130 signals failure via `turn/completed` with `turn.status: "failed"` (carrying `turn.error.message`), not only `turn/failed` — agentex hardcoded `isError:false`, so a failed turn (e.g. a 4xx from the model API) looked `completed`. The parser + session now detect the failed status and the trailing `error` notification, reporting `status:"failed"` with the error text. Verified live.
|
|
@@ -682,6 +1123,20 @@ Additive. The instruction-file twin of `installSkills`: install an orientation b
|
|
|
682
1123
|
|
|
683
1124
|
Driven by consumer feedback from an embedding host wiring an orchestrator onto agentex sessions. All additive — except the MCP fix, which replaces behavior that never worked in any published version.
|
|
684
1125
|
|
|
1126
|
+
### Changed
|
|
1127
|
+
|
|
1128
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
1129
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
1130
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
1131
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
1132
|
+
have its sends resolved by that handler — the failure that made two
|
|
1133
|
+
back-to-back results resolve both callers with the first result.
|
|
1134
|
+
|
|
1135
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
1136
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
1137
|
+
Modelling one command per turn left every coalesced message permanently
|
|
1138
|
+
unsettled.
|
|
1139
|
+
|
|
685
1140
|
### Fixed
|
|
686
1141
|
|
|
687
1142
|
- **`config.mcpServers` actually attaches MCP servers now.** It previously emitted `--mcp-server <name> -- <command>…` — a flag that does not exist in Claude Code 2.x — so any run/session setting the field died instantly with `error: unknown option '--mcp-server'` (verified against claude 2.1.165; the field has never worked in any published version, so there is no behavior to migrate from). The config is now staged as a **mode-0600 JSON file** in a temp dir and passed via the real `--mcp-config <path>`, cleaned up with the run/session (including spawn-failure paths). Secrets never touch argv — http `headers` (bearer tokens) live only in the 0600 file; argv is world-readable via `ps`.
|
|
@@ -717,6 +1172,20 @@ A three-tier provider architecture (deep-native · ACP · bespoke). The ACP tier
|
|
|
717
1172
|
- **Codex collaboration modes.** `codexProvider.listModes()` discovers Codex's collaboration modes via `collaborationMode/list`; `config.modeId` applies a chosen mode to a fresh `thread/start` (a resumed thread keeps its original mode). `capabilities.modes` is now `true` for Codex.
|
|
718
1173
|
- **Codex structured questions.** The app-server `requestUserInput` (and legacy `tool/requestUserInput`) server→client request is now bridged to `onUserInputRequest` as an `AskUserQuestion`, with answers mapped back into Codex's `{ answers: { [id]: { answers: [] } } }` shape — Codex sessions can answer questions headlessly.
|
|
719
1174
|
|
|
1175
|
+
### Changed
|
|
1176
|
+
|
|
1177
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
1178
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
1179
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
1180
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
1181
|
+
have its sends resolved by that handler — the failure that made two
|
|
1182
|
+
back-to-back results resolve both callers with the first result.
|
|
1183
|
+
|
|
1184
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
1185
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
1186
|
+
Modelling one command per turn left every coalesced message permanently
|
|
1187
|
+
unsettled.
|
|
1188
|
+
|
|
720
1189
|
### Fixed
|
|
721
1190
|
|
|
722
1191
|
- **Codex tool-approval response shape.** Command/file approval requests are now answered with `{ decision: "accept" | "decline" }` (Codex's actual app-server contract) instead of `{ approved: boolean }`, which the app-server did not honor. Tool-permission gating in Codex sessions now works headlessly.
|
|
@@ -754,6 +1223,20 @@ Scheduled / fire-and-forget session runs needed three things the SDK pushed onto
|
|
|
754
1223
|
|
|
755
1224
|
- **`SendHandle`, `SendOptions`, `CancelResult`** are now exported from the package entry point (previously only the `AgentSession` interface was).
|
|
756
1225
|
|
|
1226
|
+
### Changed
|
|
1227
|
+
|
|
1228
|
+
- Turn, command, and task state is now reduced from the ordered wire in one
|
|
1229
|
+
place, rather than held in mutable fields updated across `await` boundaries.
|
|
1230
|
+
Each turn's settlement batch is captured synchronously before any suspension
|
|
1231
|
+
point, so a turn that opens while an earlier handler is still draining cannot
|
|
1232
|
+
have its sends resolved by that handler — the failure that made two
|
|
1233
|
+
back-to-back results resolve both callers with the first result.
|
|
1234
|
+
|
|
1235
|
+
- A turn owns a *set* of command uuids. The CLI coalesces: a host message
|
|
1236
|
+
dequeued while a turn is running joins that turn instead of starting its own.
|
|
1237
|
+
Modelling one command per turn left every coalesced message permanently
|
|
1238
|
+
unsettled.
|
|
1239
|
+
|
|
757
1240
|
### Fixed
|
|
758
1241
|
|
|
759
1242
|
- **Session `close()` now honors `ProviderConfig.graceSec`** for the SIGTERM → SIGKILL window (was hardcoded to 5s, ignoring the config field that `execute()` already respected). `drain()` uses the same configurable grace.
|
package/README.md
CHANGED
|
@@ -382,8 +382,10 @@ Variants:
|
|
|
382
382
|
- `tool_result` — Tool returned a result (`toolCallId: string | null`, `toolName: string | null`, `content`, `isError`, `exitCode: number | null`). `toolName` mirrors the matching `tool_call.name` (correlated for you), so you don't need your own `toolCallId → name` cache; null when no preceding `tool_call` was seen on the stream.
|
|
383
383
|
- `rate_limit` — Provider reported rate-limit state (`status`, `limitType`, `resetAt`, `overageStatus`, `isUsingOverage`)
|
|
384
384
|
- `permission_mode` — Permission mode change mid-session (`permissionMode: string`). Claude only, e.g., when the user accepts a plan and the session leaves `plan` mode.
|
|
385
|
-
- `background_task` — Provider-neutral lifecycle for work that can outlive the root turn (`taskId`, `taskType`, `phase`, `status`, `description`, `summary`, `parentTaskId`). A terminal task event never settles the root turn.
|
|
386
|
-
- `
|
|
385
|
+
- `background_task` — Provider-neutral lifecycle for work that can outlive the root turn (`taskId`, `taskType`, `phase`, `status`, `description`, `summary`, `parentTaskId`, `toolUseId`, `report`). A terminal task event never settles the root turn.
|
|
386
|
+
- `turn_start` — A turn opened (`turnId`, `trigger`). `trigger` is `"send"` when the host dispatched it and `"resume"` when the provider started it on its own — Claude does the latter when a background task's result comes back, opening a turn no `send()` can see. Track "is the agent working" from `turn_start` → `turn_end`, not from your own dispatch. `turnId` is session-local (`turn-1`, `turn-2`, …), not a provider id. `turn_start` deliberately does not name the task behind a `resume`: Claude delivers a task's result and opens the turn as two unlinked records, so with several tasks in flight the pairing is not recoverable from the wire — correlate through `background_task.report` and `toolUseId` instead.
|
|
387
|
+
- `turn_end` — The matching close (`turnId`, `trigger`, `reason`). Exactly one per `turn_start`, carrying the same `turnId`. **Close on this, not on `result`.** `reason: "result"` is a normal completion whose payload arrives as the accompanying `result` event, ordered immediately before this one. `"cancelled" | "discarded" | "refused"` are the CLI's verdicts on a message that opened a turn and produced no result at all, and `"session_closed"` is a turn still open when the session went down. A host pairing `turn_start` with `result` stays busy forever on those four paths.
|
|
388
|
+
- `result` — Final result (`text`, `costUsd`, `isError`, `stopReason`, `terminalReason`, `numTurns`, `durationMs`). The outcome payload, not the close signal — see `turn_end`.
|
|
387
389
|
|
|
388
390
|
Lifecycle events (via `onLifecycle`) report phases: `preparing`, `spawning`, `running`, `waiting_for_input`, `completed`, `cancelled`, `error`.
|
|
389
391
|
|
|
@@ -396,7 +398,7 @@ Verified live against `claude 2.1.116` and `codex-cli 0.122.0` (2026-04-21). ACP
|
|
|
396
398
|
| `sessionId` | `session_id` (UUID, stable across turns + resume) | `thread_id` (UUIDv7, emitted once on `thread.started`, tracked across lines) |
|
|
397
399
|
| `messageId` | `message.id` (Anthropic API message, e.g. `msg_*`) | v2 app-server: globally unique (`msg_*`, `rs_*`, `call_*`). NDJSON: `item_N` — **turn-local, not globally unique** |
|
|
398
400
|
| `eventId` | Top-level per-line `uuid` | Synthetic where derivable (see below); else null |
|
|
399
|
-
| `turnId` | **null** — Claude
|
|
401
|
+
| `turnId` | **null** on every event except `turn_start` / `turn_end`, which carry a session-local `turn-N` — Claude has no native turn id | v2 app-server: native UUIDv7 from `params.turnId`. NDJSON: **null** — no turn id in legacy format |
|
|
400
402
|
| `parentToolCallId` | `parent_tool_use_id` (set for sub-agent messages) | **null** — not emitted |
|
|
401
403
|
| Tool correlation | `tool_use.id` (`toolu_*`) ↔ `tool_result.tool_use_id`; the library stamps `tool_result.toolName` from the matching call | `item.id` reappears on the same item's `item.completed`; `toolName` set directly from the item type |
|
|
402
404
|
| `tool_result.exitCode` | **null** (Claude doesn't expose shell exit codes) | `item.exit_code` for `command_execution` |
|
|
@@ -550,6 +552,15 @@ first-class `background_task` event. Its normalized contract is:
|
|
|
550
552
|
description: string | null;
|
|
551
553
|
summary: string | null;
|
|
552
554
|
parentTaskId: string | null;
|
|
555
|
+
// The tool call that launched the task, when the provider reports it.
|
|
556
|
+
toolUseId: string | null;
|
|
557
|
+
// The task's delivered output — present only on the event that hands the
|
|
558
|
+
// result back, null on every state change. See below.
|
|
559
|
+
report: {
|
|
560
|
+
summary: string | null;
|
|
561
|
+
outputFile: string | null;
|
|
562
|
+
usage: { totalTokens: number | null; toolUses: number | null; durationMs: number | null } | null;
|
|
563
|
+
} | null;
|
|
553
564
|
}
|
|
554
565
|
```
|
|
555
566
|
|
|
@@ -558,6 +569,15 @@ from the active set when `phase === "completed"`. Root turn state is a separate
|
|
|
558
569
|
axis. A `result` may arrive while background tasks are still active, and a
|
|
559
570
|
background task may complete while the root is still running.
|
|
560
571
|
|
|
572
|
+
A completion can arrive as more than one event. Claude emits both a state
|
|
573
|
+
patch (`task_updated`) and a result delivery (`task_notification`) for a single
|
|
574
|
+
finished task, and they are different records rather than duplicates: only the
|
|
575
|
+
delivery carries the summary and the output file. Render one row per completion
|
|
576
|
+
by keying on `report !== null` rather than on `phase`. A task that was stopped
|
|
577
|
+
or killed delivered nothing and carries no report, so it produces no row under
|
|
578
|
+
that rule — surface those from the terminal `status` if your UI needs to show
|
|
579
|
+
that a task was cut short.
|
|
580
|
+
|
|
561
581
|
```typescript
|
|
562
582
|
createSession({
|
|
563
583
|
onEvent(event) {
|