fix(client-runtime): settled-only dispatch index carries null callTime; README matches
Responding to ds-review-bot round 2 on #653: the tool/code-dispatch event is appended at settlement, so using its time as callTime fabricated a zero-duration call for duration-aware consumers — it is now null (start unknown) per the ToolResultNode contract, pinned in the session spec. The README's codeDispatches section described the PR3 running→settled lifecycle a stack ahead of this tree; it now documents the settled-only index this PR ships (the running shape lands with the start event in #658, which already merges cleanly over this).
This commit is contained in:
@@ -14,7 +14,7 @@ SlotsService gives the renderer separate bare observables for `useSessions` and
|
|||||||
|
|
||||||
## Code Mode sub-dispatch index
|
## Code Mode sub-dispatch index
|
||||||
|
|
||||||
`ConversationSnapshot.codeDispatches` groups a `run_code` call's sub-dispatches under their parent callId, in start order, using the native call-block shapes: a started-but-unsettled sub-call is a `RunningToolCall` (rows derive the running ring from the shape) and its `tool/code-dispatch` settlement replaces it in place with the `ToolResultNode` form, `callTime` carrying the paired start's time. Live mux frames and history replay build the identical index; sub-calls never join the surface `nodes` flow; per-parent array and map references are memo-stable across unrelated snapshot swaps.
|
`ConversationSnapshot.codeDispatches` groups a `run_code` call's sub-dispatches under their parent callId, in dispatch order, as settled `ToolResultNode` entries (the native result shape): each `tool/code-dispatch` event appends one. The event carries only the settle timestamp, so `callTime` is `null` (start unknown) — no duration claim is possible from this index yet. Live mux frames and history replay build the identical index; sub-calls never join the surface `nodes` flow; per-parent array and map references are memo-stable across unrelated snapshot swaps.
|
||||||
|
|
||||||
## Session title projection
|
## Session title projection
|
||||||
|
|
||||||
|
|||||||
@@ -638,7 +638,10 @@ export class Session implements ObservableSnapshot<ConversationSnapshot> {
|
|||||||
kind: 'tool-result', seq: event.seq, time: event.time,
|
kind: 'tool-result', seq: event.seq, time: event.time,
|
||||||
callId: data.subCallId,
|
callId: data.subCallId,
|
||||||
call: { name: data.name, argsRaw: JSON.stringify(data.arguments) },
|
call: { name: data.name, argsRaw: JSON.stringify(data.arguments) },
|
||||||
callTime: event.time,
|
// The settle event is the only timestamp this event carries; the
|
||||||
|
// start time is unknown (null per the ToolResultNode contract), so
|
||||||
|
// duration-aware consumers never see a fabricated zero-duration call.
|
||||||
|
callTime: null,
|
||||||
content: data.content, isError: data.isError,
|
content: data.content, isError: data.isError,
|
||||||
callView: null, resultView: null,
|
callView: null, resultView: null,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -659,6 +659,9 @@ describe('run_code sub-dispatch indexing', () => {
|
|||||||
expect(subs?.[0]).toMatchObject({
|
expect(subs?.[0]).toMatchObject({
|
||||||
kind: 'tool-result', callId: 'p1:code:1',
|
kind: 'tool-result', callId: 'p1:code:1',
|
||||||
call: { name: 'bash', argsRaw: '{"command":"ls","description":"列目录"}' },
|
call: { name: 'bash', argsRaw: '{"command":"ls","description":"列目录"}' },
|
||||||
|
// The settle event carries no start time: callTime stays null (never a
|
||||||
|
// fabricated zero-duration).
|
||||||
|
callTime: null,
|
||||||
isError: false, content: [{ type: 'text', text: 'demo.txt' }],
|
isError: false, content: [{ type: 'text', text: 'demo.txt' }],
|
||||||
})
|
})
|
||||||
expect(subs?.[1]).toMatchObject({ callId: 'p1:code:2', isError: true })
|
expect(subs?.[1]).toMatchObject({ callId: 'p1:code:2', isError: true })
|
||||||
|
|||||||
Reference in New Issue
Block a user