fix(shared): GatewayEventMap drops phantom keys and types child_session_id
Re-verified against the tui_gateway emitters:
- SubagentEventPayload.cost_usd / .iteration: not in
tool_progress.py::_SUBAGENT_FIELDS, never emitted → removed; the TUI's
turnController no longer copies them (its SubagentProgress keeps the
fields for spawn-history persistence).
- SubagentEventPayload.child_session_id: emitted (in _SUBAGENT_FIELDS, read
by agent_callbacks.py::_mirror_subagent_to_child) but untyped → added.
- ToolCompletePayload.error: _on_tool_complete never sets it → removed;
the TUI's completeTool drops its dead `error` parameter and renders the
trail line as non-error (which is what it always did on the wire).
- ToolStartPayload.todos: not on the wire either, but the TUI handler and
its fixtures exercise recordTodos from tool.start; kept with a comment
saying so rather than churning the handler.
- MessageCompletePayload.failure_reason: prompt_turn.py passes
result.get("failure_reason") through → `string | null`.
This commit is contained in:
@@ -64,7 +64,8 @@ export type SubagentStatus = 'completed' | 'error' | 'failed' | 'interrupted' |
|
||||
/** `tui_gateway/tool_progress.py::_progress_subagent` — every `subagent.*` frame. */
|
||||
export interface SubagentEventPayload {
|
||||
api_calls?: number
|
||||
cost_usd?: number
|
||||
/** The child's own gateway session id — the key a watch window mirrors. */
|
||||
child_session_id?: string
|
||||
/** Batch (delegation) id this subagent belongs to — distinguishes
|
||||
* interleaved `[n/N]` progress from concurrent or nested fan-outs. */
|
||||
delegation_id?: string
|
||||
@@ -74,7 +75,6 @@ export interface SubagentEventPayload {
|
||||
files_written?: string[]
|
||||
goal: string
|
||||
input_tokens?: number
|
||||
iteration?: number
|
||||
model?: string
|
||||
output_tail?: { is_error?: boolean; preview?: string; tool?: string }[]
|
||||
output_tokens?: number
|
||||
@@ -115,7 +115,7 @@ export interface MessageCompletePayload {
|
||||
/** `status: "error"` — the failure message (`text` may be streamed output). */
|
||||
error?: string
|
||||
error_surface?: ErrorSurface
|
||||
failure_reason?: string
|
||||
failure_reason?: string | null
|
||||
/** `status: "error"` — `text` is streamed partial output to keep, not the error string. */
|
||||
partial?: boolean
|
||||
reasoning?: string
|
||||
@@ -142,6 +142,8 @@ export interface ToolStartPayload {
|
||||
/** Mirrored child tool rows carry a short preview instead of args. */
|
||||
preview?: string
|
||||
tool_id: string
|
||||
/** Not on the wire (`_on_tool_start` never sets it): the todo snapshot rides `tool.complete` /
|
||||
* `todo.updated`. Kept because the TUI handler reads it and its fixtures exercise that path. */
|
||||
todos?: unknown[]
|
||||
}
|
||||
|
||||
@@ -149,7 +151,6 @@ export interface ToolStartPayload {
|
||||
export interface ToolCompletePayload {
|
||||
args?: Record<string, unknown>
|
||||
duration_s?: number
|
||||
error?: string
|
||||
inline_diff?: string
|
||||
name?: string
|
||||
/** Parsed JSON when the tool returned JSON, else the raw string. */
|
||||
|
||||
@@ -1239,7 +1239,6 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
|
||||
inlineDiffText,
|
||||
ev.payload.tool_id,
|
||||
ev.payload.name,
|
||||
ev.payload.error,
|
||||
ev.payload.duration_s,
|
||||
resultText
|
||||
)
|
||||
@@ -1247,7 +1246,6 @@ export function createGatewayEventHandler(ctx: GatewayEventHandlerContext): (ev:
|
||||
turnController.recordToolComplete(
|
||||
ev.payload.tool_id,
|
||||
ev.payload.name,
|
||||
ev.payload.error,
|
||||
ev.payload.summary,
|
||||
ev.payload.duration_s,
|
||||
ev.payload.todos,
|
||||
|
||||
@@ -796,7 +796,6 @@ class TurnController {
|
||||
recordToolComplete(
|
||||
toolId: string,
|
||||
fallbackName?: string,
|
||||
error?: string,
|
||||
summary?: string,
|
||||
duration?: number,
|
||||
todos?: unknown,
|
||||
@@ -807,38 +806,26 @@ class TurnController {
|
||||
}
|
||||
|
||||
this.recordTodos(todos)
|
||||
const line = this.completeTool(toolId, fallbackName, error, summary, duration, resultText)
|
||||
const line = this.completeTool(toolId, fallbackName, summary, duration, resultText)
|
||||
|
||||
this.pendingSegmentTools = [...this.pendingSegmentTools, line]
|
||||
this.flushPendingToolsIntoLastSegment()
|
||||
this.publishToolState()
|
||||
}
|
||||
|
||||
recordInlineDiffToolComplete(
|
||||
diffText: string,
|
||||
toolId: string,
|
||||
fallbackName?: string,
|
||||
error?: string,
|
||||
duration?: number,
|
||||
resultText?: string
|
||||
) {
|
||||
recordInlineDiffToolComplete(diffText: string, toolId: string, fallbackName?: string, duration?: number, resultText?: string) {
|
||||
if (this.interrupted) {
|
||||
return
|
||||
}
|
||||
|
||||
this.flushStreamingSegment()
|
||||
this.pushInlineDiffSegment(diffText, [this.completeTool(toolId, fallbackName, error, '', duration, resultText)])
|
||||
this.pushInlineDiffSegment(diffText, [this.completeTool(toolId, fallbackName, '', duration, resultText)])
|
||||
this.publishToolState()
|
||||
}
|
||||
|
||||
private completeTool(
|
||||
toolId: string,
|
||||
fallbackName?: string,
|
||||
error?: string,
|
||||
summary?: string,
|
||||
duration?: number,
|
||||
resultText?: string
|
||||
) {
|
||||
// `tool.complete` carries no error flag on the wire (tui_gateway/tool_progress.py::_on_tool_complete);
|
||||
// a failed tool surfaces through its result text, so every trail line renders as non-error.
|
||||
private completeTool(toolId: string, fallbackName?: string, summary?: string, duration?: number, resultText?: string) {
|
||||
const done = this.activeTools.find(tool => tool.id === toolId)
|
||||
const name = done?.name ?? fallbackName ?? 'tool'
|
||||
const label = toolTrailLabel(name)
|
||||
@@ -849,18 +836,12 @@ class TurnController {
|
||||
? buildVerboseToolTrailLine(
|
||||
name,
|
||||
done?.context || '',
|
||||
Boolean(error),
|
||||
false,
|
||||
duration ?? fallbackDuration,
|
||||
done?.verboseArgs,
|
||||
error || resultText || summary || ''
|
||||
)
|
||||
: buildToolTrailLine(
|
||||
name,
|
||||
done?.context || '',
|
||||
Boolean(error),
|
||||
error || summary || '',
|
||||
duration ?? fallbackDuration
|
||||
resultText || summary || ''
|
||||
)
|
||||
: buildToolTrailLine(name, done?.context || '', false, summary || '', duration ?? fallbackDuration)
|
||||
|
||||
this.activeTools = this.activeTools.filter(tool => tool.id !== toolId)
|
||||
|
||||
@@ -1044,14 +1025,12 @@ class TurnController {
|
||||
const next: SubagentProgress = {
|
||||
...base,
|
||||
apiCalls: p.api_calls ?? base.apiCalls,
|
||||
costUsd: p.cost_usd ?? base.costUsd,
|
||||
delegationId: p.delegation_id ?? base.delegationId,
|
||||
depth: p.depth ?? base.depth,
|
||||
filesRead: p.files_read ?? base.filesRead,
|
||||
filesWritten: p.files_written ?? base.filesWritten,
|
||||
goal: p.goal || base.goal,
|
||||
inputTokens: p.input_tokens ?? base.inputTokens,
|
||||
iteration: p.iteration ?? base.iteration,
|
||||
model: p.model ?? base.model,
|
||||
outputTail,
|
||||
outputTokens: p.output_tokens ?? base.outputTokens,
|
||||
|
||||
Reference in New Issue
Block a user