feat: add turn_canceled event for proper interruption handling

Adds a new turn_canceled event type to distinguish intentional turn
interruption from errors. Fixes race conditions in Claude agent where
concurrent stream() calls could cause garbled output by serializing
turn handoff with turn IDs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2025-12-28 16:29:01 +00:00
co-authored by Claude Opus 4.5
parent 076080db48
commit 69b6f755fa
7 changed files with 123 additions and 7 deletions
@@ -403,6 +403,7 @@ export class AgentManager {
const timeline: AgentTimelineItem[] = [];
let finalText = "";
let usage: AgentUsage | undefined;
let canceled = false;
for await (const event of events) {
if (event.type === "timeline") {
@@ -414,6 +415,8 @@ export class AgentManager {
usage = event.usage;
} else if (event.type === "turn_failed") {
throw new Error(event.error);
} else if (event.type === "turn_canceled") {
canceled = true;
}
}
@@ -423,6 +426,7 @@ export class AgentManager {
finalText,
usage,
timeline,
canceled,
};
}
@@ -734,6 +738,11 @@ export class AgentManager {
hasStarted = true;
finish(null);
}
if (event.event.type === "turn_canceled") {
currentStatus = "idle";
hasStarted = true;
finish(null);
}
}
},
{ agentId, replayState: true }
@@ -889,6 +898,10 @@ export class AgentManager {
case "turn_failed":
agent.lastError = event.error;
break;
case "turn_canceled":
// Cancellation is not an error, just clear any previous error
agent.lastError = undefined;
break;
case "permission_requested":
agent.pendingPermissions.set(event.request.id, event.request);
this.emitState(agent);