From dcbc51e22dcd3d7b5a01d6c3b7b0285efa49bca4 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Fri, 29 May 2026 18:45:45 +0900 Subject: feat: stop generation button with abort signal plumbing - Add POST /chat/stop endpoint on API - Thread abortSignal from agent-manager through Agent.run() to streamText - Thread abortSignal option through the Agent.run() signature - Emit status:idle on stopTab() so frontend WS gets the update - Add stopGeneration() store method on frontend tabStore - Add stop button in ChatInput (btn-sm lg:btn-xs for mobile tap target) - Add tests for /chat/stop endpoint - Refactor processMessage to pass abortSignal to agent.run --- packages/api/src/agent-manager.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'packages/api/src/agent-manager.ts') diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts index 69c071d..c09a607 100644 --- a/packages/api/src/agent-manager.ts +++ b/packages/api/src/agent-manager.ts @@ -885,6 +885,7 @@ export class AgentManager { } tabAgent.abortController?.abort(); tabAgent.status = "idle"; + this.emit({ type: "status", status: "idle" }, tabId); tabAgent.agent = null; // Resolve any pending completion promise so retrieve doesn't hang tabAgent.completionResolve?.({ status: "error", error: "Agent was stopped." }); @@ -1193,10 +1194,10 @@ export class AgentManager { // Best-effort — if this fails, appendMessage will throw and we'll catch it below } - for await (const event of agent.run( - message, - reasoningEffort ? { reasoningEffort } : undefined, - )) { + for await (const event of agent.run(message, { + ...(reasoningEffort ? { reasoningEffort } : {}), + abortSignal: tabAgent.abortController?.signal, + })) { // Stop processing if the tab was aborted (closed/stopped). // stopTab() already injected a `cancelled` system chunk into // `chunks` before flipping the abort flag, so we just need -- cgit v1.2.3