Treat OpenCode abort as cancellation, not error (#1169)
OpenCode emits session.error with MessageAbortedError when the user interrupts a running turn. Paseo was mapping every session.error to turn_failed, which set the agent lifecycle to error and appended a raw JSON system error message to the timeline. Now MessageAbortedError translates to turn_canceled (same as an explicit cancel), so the agent returns to idle with no error state. Real errors still produce turn_failed.
This commit is contained in:
@@ -1691,14 +1691,7 @@ export function translateOpenCodeEvent(
|
||||
}
|
||||
break;
|
||||
case "session.error":
|
||||
if (event.properties.sessionID === state.sessionId) {
|
||||
resetOpenCodeTurnTrackingState(state);
|
||||
events.push({
|
||||
type: "turn_failed",
|
||||
provider: "opencode",
|
||||
error: toDiagnosticErrorMessage(event.properties.error),
|
||||
});
|
||||
}
|
||||
appendOpenCodeSessionError(event, state, events);
|
||||
break;
|
||||
case "session.status":
|
||||
appendOpenCodeSessionStatus(event, state, events);
|
||||
@@ -2294,6 +2287,36 @@ function appendOpenCodeQuestionAsked(
|
||||
});
|
||||
}
|
||||
|
||||
function appendOpenCodeSessionError(
|
||||
event: Extract<OpenCodeEvent, { type: "session.error" }>,
|
||||
state: OpenCodeEventTranslationState,
|
||||
events: AgentStreamEvent[],
|
||||
): void {
|
||||
if (event.properties.sessionID !== state.sessionId) {
|
||||
return;
|
||||
}
|
||||
resetOpenCodeTurnTrackingState(state);
|
||||
const error = event.properties.error;
|
||||
if (
|
||||
typeof error === "object" &&
|
||||
error !== null &&
|
||||
"name" in error &&
|
||||
error.name === "MessageAbortedError"
|
||||
) {
|
||||
events.push({
|
||||
type: "turn_canceled",
|
||||
provider: "opencode",
|
||||
reason: "interrupted",
|
||||
});
|
||||
} else {
|
||||
events.push({
|
||||
type: "turn_failed",
|
||||
provider: "opencode",
|
||||
error: toDiagnosticErrorMessage(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function appendOpenCodeSessionStatus(
|
||||
event: Extract<OpenCodeEvent, { type: "session.status" }>,
|
||||
state: OpenCodeEventTranslationState,
|
||||
|
||||
@@ -924,4 +924,52 @@ describe("translateOpenCodeEvent", () => {
|
||||
]);
|
||||
expect(second).toEqual([]);
|
||||
});
|
||||
|
||||
it("translates session.error with MessageAbortedError as turn_canceled, not turn_failed", () => {
|
||||
const state = createState();
|
||||
|
||||
const events = translateOpenCodeEvent(
|
||||
{
|
||||
type: "session.error",
|
||||
properties: {
|
||||
sessionID: "session-1",
|
||||
error: {
|
||||
name: "MessageAbortedError",
|
||||
data: { message: "aborted" },
|
||||
},
|
||||
},
|
||||
},
|
||||
state,
|
||||
);
|
||||
|
||||
expect(events).toEqual([
|
||||
{ type: "turn_canceled", provider: "opencode", reason: "interrupted" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("translates session.error with a real error as turn_failed", () => {
|
||||
const state = createState();
|
||||
|
||||
const events = translateOpenCodeEvent(
|
||||
{
|
||||
type: "session.error",
|
||||
properties: {
|
||||
sessionID: "session-1",
|
||||
error: {
|
||||
name: "UnknownError",
|
||||
data: { message: "something broke" },
|
||||
},
|
||||
},
|
||||
},
|
||||
state,
|
||||
);
|
||||
|
||||
expect(events).toEqual([
|
||||
{
|
||||
type: "turn_failed",
|
||||
provider: "opencode",
|
||||
error: '{"name":"UnknownError","data":{"message":"something broke"}}',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user