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:
teknium1
2026-09-12 23:57:41 -07:00
committed by Teknium
parent 2435131573
commit c1e0fd83f9
3 changed files with 14 additions and 36 deletions

View File

@@ -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. */

View File

@@ -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,

View File

@@ -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,