Files
orca/src/shared/structured-agent-session-tool-call-block.ts
T
Brennan Benson 8757e40063 fix(native-chat): keep a structured agent's tool line between tool calls (#22349)
* fix(native-chat): keep a structured agent's tool line between tool calls

A structured session's status named a tool only while the call was still
running, so the sidebar's tool line went blank whenever the agent was
thinking or writing between calls. Terminal agents keep naming the finished
tool until the next one starts, and clear it after a failure. The structured
status projection now does the same: a running call wins, otherwise the
turn's newest root call if it completed.

* fix(native-chat): bound the structured tool line by the running turn, not the user row

A send made while a turn is running writes its user row into the journal
straight away, and the turn keeps going. Stopping the scan at that row
blanked the tool line while a tool was still running. The scan now runs to
the turn record and names a call only when that record is still running, so
a turn that already ended never lends its last tool to a pending follow-up.

This lookup was the running-only lookup's only production caller, so it
replaces that lookup instead of sitting beside it.

* fix(native-chat): keep naming a structured agent's failed tool until the next one

Clearing the tool line after a failed call brought the blank gap back for
much of a turn: Codex marks any nonzero exit as failed, so a search with no
match or a red test run is enough. The failure already shows on the tool's
own row in the transcript. The running turn's newest running call still
wins; otherwise its newest root call is named whatever it settled to.

* fix(native-chat): name a structured Codex edit on the tool line as the chat draws it

Once a Codex edit's changes exist, its apply_patch call becomes a diff row,
which the status lookup skipped, so the row named the command before the edit.
The chat's tool-call block for a journal row now comes from one shared builder,
and the status lookup reads the same definition: a diff is named as Diff with
its path, and counts as settled since it carries no lifecycle.

* docs(native-chat): describe the structured tool field as running-or-latest

The status summary's toolName/toolInput now name the running turn's
latest tool between calls, not only a running one. Update the wire type
and status bridge comments that still said "the running tool".
2026-09-22 22:42:31 -07:00

47 lines
1.9 KiB
TypeScript

// The one definition of a structured journal's tool actions: which rows the chat draws as a
// tool call, and the block it draws. The status line reads the same definition, so the sidebar
// names a tool exactly as the chat draws it.
import type {
AgentJournalDiffItem,
AgentJournalItemBody,
AgentJournalToolCallItem
} from './agent-session-journal-types'
import type { NativeChatToolCallBlock } from './native-chat-types'
export type StructuredAgentSessionToolAction = AgentJournalToolCallItem | AgentJournalDiffItem
// Codex rewrites an edit's `apply_patch` call into a diff once its changes exist.
export function isStructuredAgentSessionToolAction(
body: AgentJournalItemBody | undefined
): body is StructuredAgentSessionToolAction {
return body?.kind === 'tool-call' || body?.kind === 'diff'
}
/** A diff carries no lifecycle, so it reads as settled; otherwise a finished edit would stay the
* running call for the rest of its turn. */
export function isRunningStructuredAgentSessionToolAction(
action: StructuredAgentSessionToolAction
): boolean {
return action.kind === 'tool-call' && action.state === 'running'
}
export function structuredAgentSessionToolCallBlock(
action: StructuredAgentSessionToolAction
): NativeChatToolCallBlock {
if (action.kind === 'diff') {
return { type: 'tool-call', name: 'Diff', input: { path: action.path } }
}
return {
type: 'tool-call',
name: action.name,
input: action.input,
state: action.state,
...(action.callId !== undefined ? { callId: action.callId } : {}),
...(action.mcpIdentity !== undefined ? { mcpIdentity: action.mcpIdentity } : {}),
...(action.exitCode !== undefined ? { exitCode: action.exitCode } : {}),
...(action.durationMs !== undefined ? { durationMs: action.durationMs } : {}),
...(action.webSearchResults !== undefined ? { webSearchResults: action.webSearchResults } : {})
}
}