@koda-sl/baker-bridge 0.36.0-dev.7ebf7748-dev.7ebf7748 → 0.37.1-dev.c223fbd9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,70 +1,252 @@
1
+ import { appendFile, mkdir } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
1
4
  import { env } from "./env.js";
2
5
  function delay(ms) {
3
6
  return new Promise((resolve) => setTimeout(resolve, ms));
4
7
  }
5
- // Track posted event UUIDs per thread to prevent duplicate POSTs from retries
6
- // interleaving with new events. Key: threadId, Value: Set of posted UUIDs.
7
- // Capped at MAX_UUIDS_PER_THREAD to bound memory in long-running processes.
8
- const postedUuids = new Map();
9
- const MAX_UUIDS_PER_THREAD = 200;
10
- /** Check if an event UUID was already posted for a thread. If not, mark it. */
11
- function markPosted(threadId, uuid) {
12
- if (!uuid) {
13
- return false;
8
+ // ---------------------------------------------------------------------------
9
+ // Filesystem error log survives process crashes, inspectable via cat/tail.
10
+ // Lives at ~/.baker/relay-errors.jsonl. Critical for diagnosing stuck threads.
11
+ // ---------------------------------------------------------------------------
12
+ const RELAY_ERRORS_DIR = join(homedir(), ".baker");
13
+ const RELAY_ERRORS_FILE = join(RELAY_ERRORS_DIR, "relay-errors.jsonl");
14
+ let dirEnsured = false;
15
+ async function logRelayError(entry) {
16
+ try {
17
+ if (!dirEnsured) {
18
+ await mkdir(RELAY_ERRORS_DIR, { recursive: true });
19
+ dirEnsured = true;
20
+ }
21
+ const line = JSON.stringify({ timestamp: new Date().toISOString(), ...entry });
22
+ await appendFile(RELAY_ERRORS_FILE, `${line}\n`);
14
23
  }
15
- let set = postedUuids.get(threadId);
16
- if (!set) {
17
- set = new Set();
18
- postedUuids.set(threadId, set);
24
+ catch {
25
+ // Best-effort — don't let logging failures break the relay
19
26
  }
20
- if (set.has(uuid)) {
21
- return true;
27
+ }
28
+ async function attemptOnce(path, body) {
29
+ try {
30
+ const res = await fetch(`${env.BAKER_CONVEX_SITE_URL}${path}`, {
31
+ method: "POST",
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ Authorization: `Bearer ${env.BAKER_API_KEY}`,
35
+ },
36
+ body: JSON.stringify(body),
37
+ });
38
+ if (res.ok) {
39
+ return { ok: true };
40
+ }
41
+ return {
42
+ ok: false,
43
+ status: res.status,
44
+ responseBody: await res.text().catch(() => "(unreadable)"),
45
+ };
46
+ }
47
+ catch (err) {
48
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
22
49
  }
23
- // Evict oldest entries if at capacity
24
- if (set.size >= MAX_UUIDS_PER_THREAD) {
25
- const first = set.values().next().value;
26
- if (first !== undefined) {
27
- set.delete(first);
50
+ }
51
+ async function postToConvex(path, body, options = {}) {
52
+ const retries = options.retries ?? 3;
53
+ let last = { ok: false };
54
+ for (let attempt = 0; attempt < retries; attempt++) {
55
+ last = await attemptOnce(path, body);
56
+ if (last.ok) {
57
+ return true;
58
+ }
59
+ if (last.error && !options.silent) {
60
+ console.error(`POST to Convex ${path} attempt ${attempt + 1} failed:`, last.error);
28
61
  }
62
+ if (attempt < retries - 1) {
63
+ await delay(1000 * 2 ** attempt);
64
+ }
65
+ }
66
+ if (options.silent || last.ok) {
67
+ return false;
29
68
  }
30
- set.add(uuid);
69
+ console.error(`Failed to POST to Convex ${path} after ${retries} attempts`);
70
+ void logRelayError({
71
+ path,
72
+ threadId: body.threadId,
73
+ status: last.status,
74
+ responseBody: last.responseBody,
75
+ error: last.error,
76
+ body: path === "/api/chat/complete" ? body : undefined,
77
+ });
31
78
  return false;
32
79
  }
33
- /** Clear tracked UUIDs for a thread (call on completion to avoid memory leaks). */
34
- export function clearPostedUuids(threadId) {
35
- postedUuids.delete(threadId);
36
- }
37
- /** Skip posting if this event's UUID was already sent. Returns true if duplicate. */
38
- export function isAlreadyPosted(threadId, message) {
39
- const uuid = message.uuid;
40
- return markPosted(threadId, uuid);
80
+ // ---------------------------------------------------------------------------
81
+ // Streaming preview buffer — throttle live deltas to ~50ms cadence
82
+ //
83
+ // The SDK emits text deltas at 50–100/sec. POSTing each one would drown
84
+ // Convex and the UI. Instead we accumulate per-thread and flush:
85
+ // - 50ms after the last delta (cadence)
86
+ // - immediately at 4KB buffered (avoid huge bursts)
87
+ // - immediately on assistant event / completion (await drain)
88
+ //
89
+ // Stream POSTs are silent — a dropped frame is a millisecond of preview lost,
90
+ // the persisted assistant event will fill the gap. Event/complete POSTs are
91
+ // loud and survive in relay-errors.jsonl.
92
+ // ---------------------------------------------------------------------------
93
+ const STREAM_FLUSH_MS = 50;
94
+ const MAX_BUFFER_BYTES = 4 * 1024;
95
+ const buffers = new Map();
96
+ const inFlightFlush = new Map();
97
+ function getBuffer(threadId) {
98
+ let buf = buffers.get(threadId);
99
+ if (!buf) {
100
+ buf = { text: "", tools: null, timer: null };
101
+ buffers.set(threadId, buf);
102
+ }
103
+ return buf;
41
104
  }
42
- export async function postToConvex(path, body, retries = 3) {
43
- for (let attempt = 0; attempt < retries; attempt++) {
44
- try {
45
- const res = await fetch(`${env.BAKER_CONVEX_SITE_URL}${path}`, {
46
- method: "POST",
47
- headers: {
48
- "Content-Type": "application/json",
49
- Authorization: `Bearer ${env.BAKER_API_KEY}`,
50
- },
51
- body: JSON.stringify(body),
52
- });
53
- if (res.ok) {
54
- return true;
55
- }
56
- if (attempt < retries - 1) {
57
- await delay(1000 * 2 ** attempt);
58
- }
105
+ function scheduleFlush(threadId) {
106
+ const buf = buffers.get(threadId);
107
+ if (!buf || buf.timer) {
108
+ return;
109
+ }
110
+ buf.timer = setTimeout(() => {
111
+ if (buf.timer) {
112
+ buf.timer = null;
59
113
  }
60
- catch (err) {
61
- console.error(`POST to Convex ${path} attempt ${attempt + 1} failed:`, err);
62
- if (attempt < retries - 1) {
63
- await delay(1000 * 2 ** attempt);
64
- }
114
+ void flushNow(threadId);
115
+ }, STREAM_FLUSH_MS);
116
+ }
117
+ async function flushNow(threadId) {
118
+ const prior = inFlightFlush.get(threadId);
119
+ if (prior) {
120
+ await prior;
121
+ }
122
+ const buf = buffers.get(threadId);
123
+ if (!buf || (buf.text.length === 0 && buf.tools === null)) {
124
+ return;
125
+ }
126
+ const body = { threadId };
127
+ if (buf.text.length > 0) {
128
+ body.text = buf.text;
129
+ }
130
+ if (buf.tools !== null) {
131
+ body.tools = buf.tools;
132
+ }
133
+ buf.text = "";
134
+ buf.tools = null;
135
+ const promise = postToConvex("/api/chat/stream", body, { silent: true }).then(() => undefined);
136
+ inFlightFlush.set(threadId, promise);
137
+ try {
138
+ await promise;
139
+ }
140
+ finally {
141
+ if (inFlightFlush.get(threadId) === promise) {
142
+ inFlightFlush.delete(threadId);
65
143
  }
66
144
  }
67
- console.error(`Failed to POST to Convex ${path} after ${retries} attempts`);
145
+ }
146
+ export function enqueueStreamText(threadId, delta) {
147
+ if (!delta) {
148
+ return;
149
+ }
150
+ const buf = getBuffer(threadId);
151
+ buf.text += delta;
152
+ if (buf.text.length >= MAX_BUFFER_BYTES) {
153
+ void flushNow(threadId);
154
+ return;
155
+ }
156
+ scheduleFlush(threadId);
157
+ }
158
+ export function enqueueStreamingTools(threadId, tools) {
159
+ const buf = getBuffer(threadId);
160
+ buf.tools = tools;
161
+ scheduleFlush(threadId);
162
+ }
163
+ /** Force-drain the buffer to Convex and wait for the round-trip. */
164
+ export async function flushStream(threadId) {
165
+ const buf = buffers.get(threadId);
166
+ if (buf?.timer) {
167
+ clearTimeout(buf.timer);
168
+ buf.timer = null;
169
+ }
170
+ await flushNow(threadId);
171
+ const prior = inFlightFlush.get(threadId);
172
+ if (prior) {
173
+ await prior;
174
+ }
175
+ }
176
+ /** Drop any buffered preview without flushing — call after stream end. */
177
+ export function clearStreamBuffer(threadId) {
178
+ const buf = buffers.get(threadId);
179
+ if (buf?.timer) {
180
+ clearTimeout(buf.timer);
181
+ }
182
+ buffers.delete(threadId);
183
+ }
184
+ // ---------------------------------------------------------------------------
185
+ // High-level helpers — used by agent.ts / callback.ts
186
+ // ---------------------------------------------------------------------------
187
+ /**
188
+ * Persist an assistant event. Drains any in-flight stream preview first so
189
+ * Convex sees [..stream..][event] in order; the mutation clears streamingState
190
+ * inline so the UI snaps from preview to persisted text without a flicker.
191
+ *
192
+ * Non-assistant events (system, result) are not persisted.
193
+ */
194
+ export async function postEvent(threadId, message, model) {
195
+ if (message.type !== "assistant") {
196
+ return true;
197
+ }
198
+ await flushStream(threadId);
199
+ return postToConvex("/api/chat/event", {
200
+ threadId,
201
+ event: message,
202
+ ...(model && { model }),
203
+ });
204
+ }
205
+ export async function postRouterSample(threadId, model, messages) {
206
+ await postToConvex("/api/chat/router-sample", {
207
+ threadId,
208
+ model,
209
+ messages: JSON.stringify(messages),
210
+ });
211
+ }
212
+ export function postInputRequest(threadId, toolUseId, questions) {
213
+ return postToConvex("/api/chat/input-request", { threadId, toolUseId, questions });
214
+ }
215
+ export function postInputResolved(threadId) {
216
+ return postToConvex("/api/chat/input-resolved", { threadId });
217
+ }
218
+ /** Background retry schedule for `/api/chat/complete` (~5 min total). Threads
219
+ * stuck in `streaming` because Convex was briefly unreachable should self-heal
220
+ * before the 2-min cron sweeper marks them errored. */
221
+ const COMPLETE_BG_BACKOFF_MS = [10_000, 30_000, 60_000, 90_000, 120_000];
222
+ export async function signalComplete(threadId, costUsd, isError, errors, model) {
223
+ await flushStream(threadId);
224
+ clearStreamBuffer(threadId);
225
+ const body = {
226
+ threadId,
227
+ isError,
228
+ costUsd,
229
+ ...(errors && { errors }),
230
+ ...(model && { model }),
231
+ };
232
+ const ok = await postToConvex("/api/chat/complete", body);
233
+ if (ok) {
234
+ return true;
235
+ }
236
+ // Standard retries exhausted (~7s). Keep trying in the background — Convex
237
+ // outages or temporary network blips shouldn't strand a thread.
238
+ void backgroundCompleteRetry(threadId, body);
68
239
  return false;
69
240
  }
241
+ async function backgroundCompleteRetry(threadId, body) {
242
+ for (let i = 0; i < COMPLETE_BG_BACKOFF_MS.length; i++) {
243
+ await delay(COMPLETE_BG_BACKOFF_MS[i] ?? 60_000);
244
+ const ok = await postToConvex("/api/chat/complete", body);
245
+ if (ok) {
246
+ console.warn(`[signalComplete] thread ${threadId} completion eventually accepted after ${i + 1} bg retries`);
247
+ return;
248
+ }
249
+ }
250
+ console.error(`[signalComplete] giving up on thread ${threadId} after extended retries — cron will sweep`);
251
+ }
70
252
  //# sourceMappingURL=convex.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/hono/convex.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/hono/convex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=convex.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.test.d.ts","sourceRoot":"","sources":["../../src/hono/convex.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,90 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ // Stub the env module before importing convex.ts so the env validation passes.
3
+ vi.mock("./env.ts", () => ({
4
+ env: {
5
+ BAKER_CONVEX_SITE_URL: "https://example.convex.site",
6
+ BAKER_API_KEY: "bk_test",
7
+ AUTH_TOKEN: "tok",
8
+ INTERNAL_SECRET_KEY: "0".repeat(64),
9
+ NOTDIAMOND_ENABLED: false,
10
+ },
11
+ }));
12
+ import { clearStreamBuffer, enqueueStreamingTools, enqueueStreamText, flushStream } from "./convex.js";
13
+ const THREAD = "thread_test";
14
+ let fetchCalls;
15
+ let fetchMock;
16
+ beforeEach(() => {
17
+ fetchCalls = [];
18
+ fetchMock = vi.fn((url, init) => {
19
+ fetchCalls.push({ url, body: JSON.parse(init.body ?? "{}") });
20
+ return Promise.resolve(new Response(JSON.stringify({ ok: true }), { status: 200 }));
21
+ });
22
+ vi.stubGlobal("fetch", fetchMock);
23
+ vi.useFakeTimers();
24
+ });
25
+ afterEach(() => {
26
+ clearStreamBuffer(THREAD);
27
+ vi.useRealTimers();
28
+ vi.unstubAllGlobals();
29
+ });
30
+ function streamCalls() {
31
+ return fetchCalls.filter((c) => c.url.endsWith("/api/chat/stream"));
32
+ }
33
+ describe("stream buffer", () => {
34
+ it("flushes accumulated text after the throttle window", async () => {
35
+ enqueueStreamText(THREAD, "Hello");
36
+ enqueueStreamText(THREAD, " ");
37
+ enqueueStreamText(THREAD, "world");
38
+ expect(streamCalls()).toHaveLength(0);
39
+ await vi.advanceTimersByTimeAsync(60);
40
+ expect(streamCalls()).toHaveLength(1);
41
+ expect(streamCalls()[0]?.body).toMatchObject({
42
+ threadId: THREAD,
43
+ text: "Hello world",
44
+ });
45
+ });
46
+ it("force-flushes immediately when buffer exceeds 4KB", async () => {
47
+ const big = "x".repeat(5000);
48
+ enqueueStreamText(THREAD, big);
49
+ // No timer advance — flush kicked off synchronously
50
+ await vi.advanceTimersByTimeAsync(1);
51
+ expect(streamCalls()).toHaveLength(1);
52
+ expect((streamCalls()[0]?.body.text).length).toBe(5000);
53
+ });
54
+ it("flushStream drains pending content and awaits the round trip", async () => {
55
+ enqueueStreamText(THREAD, "abc");
56
+ const flushPromise = flushStream(THREAD);
57
+ await vi.advanceTimersByTimeAsync(0);
58
+ await flushPromise;
59
+ expect(streamCalls()).toHaveLength(1);
60
+ expect(streamCalls()[0]?.body).toMatchObject({ threadId: THREAD, text: "abc" });
61
+ });
62
+ it("clearStreamBuffer drops pending content without posting", async () => {
63
+ enqueueStreamText(THREAD, "lost");
64
+ clearStreamBuffer(THREAD);
65
+ await vi.advanceTimersByTimeAsync(100);
66
+ expect(streamCalls()).toHaveLength(0);
67
+ });
68
+ it("includes tools alongside text in a single flush", async () => {
69
+ enqueueStreamingTools(THREAD, [{ toolUseId: "t1", toolName: "Bash", startedAt: 1 }]);
70
+ enqueueStreamText(THREAD, "running");
71
+ await vi.advanceTimersByTimeAsync(60);
72
+ expect(streamCalls()).toHaveLength(1);
73
+ expect(streamCalls()[0]?.body).toMatchObject({
74
+ threadId: THREAD,
75
+ text: "running",
76
+ tools: [{ toolUseId: "t1", toolName: "Bash", startedAt: 1 }],
77
+ });
78
+ });
79
+ it("coalesces multiple deltas inside the throttle window into one POST", async () => {
80
+ enqueueStreamText(THREAD, "a");
81
+ await vi.advanceTimersByTimeAsync(20);
82
+ enqueueStreamText(THREAD, "b");
83
+ await vi.advanceTimersByTimeAsync(20);
84
+ enqueueStreamText(THREAD, "c");
85
+ await vi.advanceTimersByTimeAsync(60);
86
+ expect(streamCalls()).toHaveLength(1);
87
+ expect(streamCalls()[0]?.body.text).toBe("abc");
88
+ });
89
+ });
90
+ //# sourceMappingURL=convex.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.test.js","sourceRoot":"","sources":["../../src/hono/convex.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/hono/server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAgD5B,wBAAgB,YAAY;;iBAmSR,OAAO,CAAC,IAAI,CAAC;gBAUd,OAAO,CAAC,IAAI,CAAC;EAiB/B"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/hono/server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAwB5B,wBAAgB,YAAY;;iBA+MR,OAAO,CAAC,IAAI,CAAC;gBASd,OAAO,CAAC,IAAI,CAAC;EAiB/B"}
@@ -1,13 +1,11 @@
1
1
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { serve } from "@hono/node-server";
4
- import { createNodeWebSocket } from "@hono/node-ws";
5
4
  import { Hono } from "hono";
6
5
  import { bearerAuth } from "hono/bearer-auth";
7
6
  import { cors } from "hono/cors";
8
7
  import { abortSession, getOrCreateSession, getSlashCommands } from "./agent.js";
9
8
  import { processAsync } from "./callback.js";
10
- import { clearPostedUuids, isAlreadyPosted, postToConvex } from "./convex.js";
11
9
  import { env } from "./env.js";
12
10
  import { createFlowEncoder } from "./flow-crypto.js";
13
11
  import { extractTitle, stripFrontmatter } from "./markdown.js";
@@ -23,32 +21,10 @@ const DOC_MIME_MAP = {
23
21
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
24
22
  xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
25
23
  };
26
- /** Relay a non-streaming event to Convex (fire-and-forget) */
27
- function relayToConvex(threadId, message, model) {
28
- if (message.type === "stream_event") {
29
- return;
30
- }
31
- if (isAlreadyPosted(threadId, message)) {
32
- return;
33
- }
34
- void postToConvex("/api/chat/event", { threadId, event: message, ...(model && { model }) });
35
- }
36
- /** Signal stream completion to Convex */
37
- function signalComplete(threadId, costUsd, isError, errors, model) {
38
- clearPostedUuids(threadId);
39
- void postToConvex("/api/chat/complete", {
40
- threadId,
41
- isError,
42
- costUsd,
43
- ...(errors && { errors }),
44
- ...(model && { model }),
45
- });
46
- }
47
24
  export function createServer() {
48
25
  const port = 3000;
49
26
  const host = "0.0.0.0";
50
27
  const app = new Hono();
51
- const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
52
28
  app.use("*", cors());
53
29
  app.use("/message/*", bearerAuth({ token: env.AUTH_TOKEN }));
54
30
  app.use("/answer-question", bearerAuth({ token: env.AUTH_TOKEN }));
@@ -186,88 +162,11 @@ export function createServer() {
186
162
  },
187
163
  });
188
164
  });
189
- // WebSocket endpoint — persistent bidirectional communication
190
- app.get("/ws", upgradeWebSocket((c) => {
191
- const token = c.req.query("token");
192
- return {
193
- onOpen(_event, ws) {
194
- if (token !== env.AUTH_TOKEN) {
195
- ws.close(4001, "Unauthorized");
196
- }
197
- },
198
- async onMessage(event, ws) {
199
- let msg;
200
- try {
201
- msg = JSON.parse(typeof event.data === "string" ? event.data : "");
202
- }
203
- catch {
204
- ws.send(JSON.stringify({ type: "error", error: "Invalid JSON" }));
205
- return;
206
- }
207
- switch (msg.type) {
208
- case "chat": {
209
- const { threadId, content, attachments } = msg;
210
- const session = await getOrCreateSession(threadId);
211
- let selectedModel;
212
- session.sendAndStream(content, {
213
- onModelSelected: (m) => {
214
- selectedModel = m;
215
- },
216
- onRouterSample: (model, messages) => {
217
- void postToConvex("/api/chat/router-sample", {
218
- threadId,
219
- model,
220
- messages: JSON.stringify(messages),
221
- });
222
- },
223
- onMessage: (m) => {
224
- try {
225
- ws.send(JSON.stringify({ type: m.type, threadId, data: m }));
226
- }
227
- catch {
228
- // WS may have closed — still relay to Convex
229
- }
230
- relayToConvex(threadId, m, selectedModel);
231
- },
232
- onInputRequest: (toolUseId, questions) => {
233
- try {
234
- ws.send(JSON.stringify({ type: "input_request", threadId, toolUseId, questions }));
235
- }
236
- catch {
237
- // WS may have closed — question is still registered in pendingQuestions
238
- }
239
- },
240
- onComplete: (costUsd, isError, errors, model) => {
241
- try {
242
- ws.send(JSON.stringify({ type: "result", threadId, costUsd, isError, errors }));
243
- }
244
- catch {
245
- // WS may have closed — still signal Convex
246
- }
247
- signalComplete(threadId, costUsd, isError, errors, model);
248
- },
249
- }, attachments);
250
- break;
251
- }
252
- case "answer": {
253
- const session = await getOrCreateSession(msg.threadId);
254
- session.resolveQuestion(msg.toolUseId, msg.answers);
255
- break;
256
- }
257
- case "abort": {
258
- abortSession(msg.threadId);
259
- break;
260
- }
261
- default:
262
- break;
263
- }
264
- },
265
- onClose() {
266
- // Don't destroy sessions — they persist for reconnection
267
- },
268
- };
269
- }));
270
- // Async endpoint — returns 202 immediately, processes in background
165
+ // ---------------------------------------------------------------------------
166
+ // Chat endpoints — HTTP only. The bridge writes events directly to Convex; the
167
+ // dashboard subscribes via Convex realtime. There is no chat WebSocket.
168
+ // ---------------------------------------------------------------------------
169
+ // Async dispatch — Convex calls this to start a turn. Returns 202 immediately.
271
170
  app.post("/message/async", async (c) => {
272
171
  const body = await c.req.json();
273
172
  if (!body.prompt) {
@@ -281,7 +180,7 @@ export function createServer() {
281
180
  });
282
181
  return c.json({ status: "accepted" }, 202);
283
182
  });
284
- // Abort a running query kills the CLI subprocess immediately
183
+ // Stop buttonterminates the SDK iterator for a thread.
285
184
  app.post("/message/abort", async (c) => {
286
185
  const body = await c.req.json();
287
186
  if (!body.threadId) {
@@ -290,7 +189,7 @@ export function createServer() {
290
189
  const aborted = abortSession(body.threadId);
291
190
  return c.json({ aborted });
292
191
  });
293
- // Resolve a pending AskUserQuestion — HTTP fallback for non-WS clients
192
+ // Resolve a pending AskUserQuestion.
294
193
  app.post("/answer-question", async (c) => {
295
194
  const body = await c.req.json();
296
195
  if (!body.toolUseId) {
@@ -311,7 +210,6 @@ export function createServer() {
311
210
  console.warn(`Agent server listening on http://${host}:${port}`);
312
211
  resolve();
313
212
  });
314
- injectWebSocket(server);
315
213
  });
316
214
  }
317
215
  function stop() {