diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 02:53:20 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 02:53:20 +0900 |
| commit | 20db60b0705ab65b6ade67ff614d347e13dc9803 (patch) | |
| tree | 02361b6b94d6a397355b42e7208b1c3ba39fb692 /packages/transport-http/src | |
| parent | d233842752d32659bba6f0e47b536e50d03145aa (diff) | |
| download | dispatch-20db60b0705ab65b6ade67ff614d347e13dc9803.tar.gz dispatch-20db60b0705ab65b6ade67ff614d347e13dc9803.zip | |
feat: stop generation mid-turn (POST /conversations/:id/stop)
Add stopTurn to the orchestrator: aborts the in-flight turn's
AbortController without changing conversation status. The turn
seals normally (finishReason: 'aborted'), partial messages are
persisted, and the conversation transitions active → idle via the
normal settle path.
Distinct from closeConversation which marks the conversation closed.
- POST /conversations/:id/stop endpoint
- dispatch stop <id> CLI command
- FE handoff: frontend-stop-generation-handoff.md
Diffstat (limited to 'packages/transport-http/src')
| -rw-r--r-- | packages/transport-http/src/app.test.ts | 9 | ||||
| -rw-r--r-- | packages/transport-http/src/app.ts | 7 | ||||
| -rw-r--r-- | packages/transport-http/src/extension.ts | 1 | ||||
| -rw-r--r-- | packages/transport-http/src/server.bun.test.ts | 3 |
4 files changed, 20 insertions, 0 deletions
diff --git a/packages/transport-http/src/app.test.ts b/packages/transport-http/src/app.test.ts index b265f5e..674fd94 100644 --- a/packages/transport-http/src/app.test.ts +++ b/packages/transport-http/src/app.test.ts @@ -165,6 +165,9 @@ function createFakeOrchestrator(events: AgentEvent[]): SessionOrchestrator { closeConversation() { return { abortedTurn: false }; }, + stopTurn() { + return { abortedTurn: false }; + }, async handleMessage(input) { for (const event of events) { input.onEvent(event); @@ -198,6 +201,9 @@ function createCapturingOrchestrator(): SessionOrchestrator & { closeConversation() { return { abortedTurn: false }; }, + stopTurn() { + return { abortedTurn: false }; + }, async handleMessage(input) { state.received = input; }, @@ -221,6 +227,9 @@ function createThrowingOrchestrator(error: Error): SessionOrchestrator { closeConversation() { return { abortedTurn: false }; }, + stopTurn() { + return { abortedTurn: false }; + }, async handleMessage() { throw error; }, diff --git a/packages/transport-http/src/app.ts b/packages/transport-http/src/app.ts index 64e46fd..a2ee18c 100644 --- a/packages/transport-http/src/app.ts +++ b/packages/transport-http/src/app.ts @@ -396,6 +396,13 @@ export function createApp(opts: CreateServerOptions): Hono { return c.json(body, 200); }); + app.post("/conversations/:id/stop", (c) => { + const conversationId = c.req.param("id"); + const { abortedTurn } = opts.orchestrator.stopTurn(conversationId); + log.info("conversations: stop", { conversationId, abortedTurn }); + return c.json({ conversationId, abortedTurn }, 200); + }); + app.post("/conversations/:id/queue", async (c) => { const conversationId = c.req.param("id"); diff --git a/packages/transport-http/src/extension.ts b/packages/transport-http/src/extension.ts index 351dd4a..f7af615 100644 --- a/packages/transport-http/src/extension.ts +++ b/packages/transport-http/src/extension.ts @@ -39,6 +39,7 @@ export const manifest: Manifest = { "/conversations/:id/open", "/conversations/:id/queue", "/conversations/:id/reasoning-effort", + "/conversations/:id/stop", "/conversations/:id/title", "/health", "/models", diff --git a/packages/transport-http/src/server.bun.test.ts b/packages/transport-http/src/server.bun.test.ts index 3b7700e..fa519af 100644 --- a/packages/transport-http/src/server.bun.test.ts +++ b/packages/transport-http/src/server.bun.test.ts @@ -92,6 +92,9 @@ function fakeOrchestrator(): SessionOrchestrator { closeConversation() { return { abortedTurn: false }; }, + stopTurn() { + return { abortedTurn: false }; + }, async handleMessage() {}, }; } |
