@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.
- package/README.md +72 -87
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/hono/agent.d.ts +21 -18
- package/dist/hono/agent.d.ts.map +1 -1
- package/dist/hono/agent.js +254 -115
- package/dist/hono/agent.js.map +1 -1
- package/dist/hono/callback.d.ts +5 -0
- package/dist/hono/callback.d.ts.map +1 -1
- package/dist/hono/callback.js +6 -49
- package/dist/hono/callback.js.map +1 -1
- package/dist/hono/convex.d.ts +28 -5
- package/dist/hono/convex.d.ts.map +1 -1
- package/dist/hono/convex.js +234 -52
- package/dist/hono/convex.js.map +1 -1
- package/dist/hono/convex.test.d.ts +2 -0
- package/dist/hono/convex.test.d.ts.map +1 -0
- package/dist/hono/convex.test.js +90 -0
- package/dist/hono/convex.test.js.map +1 -0
- package/dist/hono/server.d.ts.map +1 -1
- package/dist/hono/server.js +7 -109
- package/dist/hono/server.js.map +1 -1
- package/dist/hono/types.d.ts +2 -17
- package/dist/hono/types.d.ts.map +1 -1
- package/package.json +3 -5
package/dist/hono/convex.js
CHANGED
|
@@ -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
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
package/dist/hono/convex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/hono/convex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"convex.js","sourceRoot":"","sources":["../../src/hono/convex.ts"],"names":[],"mappings":"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"}
|
|
@@ -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":"
|
|
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"}
|
package/dist/hono/server.js
CHANGED
|
@@ -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
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
//
|
|
183
|
+
// Stop button — terminates 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
|
|
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() {
|