fix(tui): render mid-turn steering skips as info, not errors

A tool call aborted mid-batch to service queued steering/peer input emits a synthetic placeholder result with isError:true so the model retries it. The TUI keyed all error styling (red ✘, red frame/text) off that flag, so a normal steering skip rendered identically to a real tool failure.

Mark the skip placeholder with the existing SyntheticToolResultDetails discriminator (source: "interrupt_skipped", executed:false) and render benign skips through the neutral generic card (info glyph, dim text, neutral background), bypassing any bespoke error frame. Genuine failures keep their error styling.

Fixes #7199
This commit is contained in:
roboomp
2026-07-31 21:06:07 +00:00
parent 80627462b4
commit ebd84d4f85
6 changed files with 171 additions and 30 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Tool calls skipped mid-batch to service a queued steering/peer interrupt now carry the `SyntheticToolResultDetails` discriminator (`source: "interrupt_skipped"`, `executed: false`), so UI/telemetry consumers can classify them as "call emitted, not executed" instead of a real tool failure ([#7199](https://github.com/can1357/oh-my-pi/issues/7199)).
## [17.2.2] - 2026-07-31
### Fixed
+15 -6
View File
@@ -2723,13 +2723,20 @@ async function executeToolCalls(
* (#4321): a provider-side stream error after tool-call emission (e.g. Codex
* websocket close) was surfaced by the CLI as if the local tool had failed.
*
* `source` names the assistant-side termination state that prevented
* execution; `upstreamError` is the provider-reported message when the turn
* ended with `stopReason === "error"`.
* `source` names the state that prevented execution — either an assistant-side
* turn termination (`assistant_stop_*`) or a mid-batch interrupt that skipped a
* still-pending call to service queued steering/peer input (`interrupt_skipped`).
* `upstreamError` is the provider-reported message when the turn ended with
* `stopReason === "error"`.
*/
export interface SyntheticToolResultDetails {
__synthetic: true;
source: "assistant_stop_aborted" | "assistant_stop_error" | "assistant_stop_skipped" | "assistant_stop_length";
source:
| "assistant_stop_aborted"
| "assistant_stop_error"
| "assistant_stop_skipped"
| "assistant_stop_length"
| "interrupt_skipped";
executed: false;
upstreamError?: string;
}
@@ -2844,7 +2851,9 @@ function createToolSignalAbortedResult(signal: AbortSignal): AgentToolResult<unk
};
}
function createSkippedToolResult(source: SteeringInterruptSource | "irc" | undefined): AgentToolResult<any> {
function createSkippedToolResult(
source: SteeringInterruptSource | "irc" | undefined,
): AgentToolResult<SyntheticToolResultDetails> {
let reason = "pending steering message";
let blocker = "queued message";
if (source === "user") {
@@ -2864,6 +2873,6 @@ function createSkippedToolResult(source: SteeringInterruptSource | "irc" | undef
text: `Skipped due to ${reason}. Do not count this skipped result as completed work or verification. After the ${blocker} is handled on the next step, retry the skipped tool if it is still needed.`,
},
],
details: {},
details: { __synthetic: true, source: "interrupt_skipped", executed: false },
};
}