@koda-sl/baker-bridge 0.35.2 → 0.37.0-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 -92
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/hono/agent.d.ts +23 -14
- package/dist/hono/agent.d.ts.map +1 -1
- package/dist/hono/agent.js +257 -90
- 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 +189 -67
- 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 +16 -105
- package/dist/hono/server.js.map +1 -1
- package/dist/hono/types.d.ts +2 -13
- package/dist/hono/types.d.ts.map +1 -1
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# @koda-sl/baker-bridge
|
|
2
2
|
|
|
3
|
-
HTTP server wrapping the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk)
|
|
3
|
+
HTTP server wrapping the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk).
|
|
4
|
+
Runs inside a sandbox, persists every chat event directly to Convex, and exposes
|
|
5
|
+
HTTP endpoints for dispatch / abort / answer-question. The dashboard subscribes
|
|
6
|
+
to Convex realtime — there is no chat WebSocket.
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
6
9
|
|
|
@@ -33,9 +36,10 @@ await server.start();
|
|
|
33
36
|
|
|
34
37
|
| Variable | Description |
|
|
35
38
|
|---|---|
|
|
36
|
-
| `AUTH_TOKEN` | Bearer token for
|
|
37
|
-
| `BAKER_CONVEX_SITE_URL` | Convex site URL for
|
|
39
|
+
| `AUTH_TOKEN` | Bearer token for `/message/*` and `/answer-question` |
|
|
40
|
+
| `BAKER_CONVEX_SITE_URL` | Convex site URL for chat persistence (e.g. `https://your-deployment.convex.site`) |
|
|
38
41
|
| `BAKER_API_KEY` | API key for Convex callbacks (`bk_...`) |
|
|
42
|
+
| `INTERNAL_SECRET_KEY` | AES key for encrypting flow `_data.json` config at rest |
|
|
39
43
|
| `ANTHROPIC_API_KEY` | Anthropic API key (required by the Agent SDK) |
|
|
40
44
|
|
|
41
45
|
All variables are validated at startup via `@t3-oss/env-core` + `zod`.
|
|
@@ -45,60 +49,61 @@ All variables are validated at startup via `@t3-oss/env-core` + `zod`.
|
|
|
45
49
|
| Method | Path | Auth | Description |
|
|
46
50
|
|--------|------|------|-------------|
|
|
47
51
|
| `GET` | `/health` | None | Health check — returns `{ status, projectDir }` |
|
|
48
|
-
| `GET` | `/
|
|
49
|
-
| `GET` | `/
|
|
50
|
-
| `GET` | `/
|
|
52
|
+
| `GET` | `/commands` | None | List available slash commands (cached 5m) |
|
|
53
|
+
| `GET` | `/company` | None | Return COMPANY.md as `{ content, title }` |
|
|
54
|
+
| `GET` | `/brand` | None | Return BRAND.md as `{ content, title }` |
|
|
55
|
+
| `GET` | `/knowledge/:collection/:entry` | None | Return knowledge entry as `{ content, title }` |
|
|
51
56
|
| `GET` | `/documents/:slug` | None | Serve document binary (pdf, pptx, docx, xlsx) |
|
|
52
57
|
| `GET` | `/flows/:slug` | None | Read flow `_data.json` (decrypted) |
|
|
53
58
|
| `PUT` | `/flows/:slug` | None | Write flow `_data.json` (encrypts at rest) |
|
|
54
|
-
| `
|
|
55
|
-
| `POST` | `/message/
|
|
56
|
-
| `POST` | `/answer-question` | Bearer token | Resolve a pending `AskUserQuestion`
|
|
59
|
+
| `POST` | `/message/async` | Bearer token | Dispatch a chat turn — fire-and-forget, 202 immediately |
|
|
60
|
+
| `POST` | `/message/abort` | Bearer token | Abort a running query — kills the SDK iterator |
|
|
61
|
+
| `POST` | `/answer-question` | Bearer token | Resolve a pending `AskUserQuestion` |
|
|
57
62
|
|
|
58
|
-
##
|
|
63
|
+
## Chat flow
|
|
59
64
|
|
|
60
|
-
Connect to `/ws?token=<AUTH_TOKEN>` for real-time bidirectional communication.
|
|
61
|
-
|
|
62
|
-
### Client to Server
|
|
63
|
-
|
|
64
|
-
**Chat message:**
|
|
65
|
-
|
|
66
|
-
```json
|
|
67
|
-
{
|
|
68
|
-
"type": "chat",
|
|
69
|
-
"threadId": "thread_abc123",
|
|
70
|
-
"content": "Build a landing page for our new product",
|
|
71
|
-
"attachments": [
|
|
72
|
-
{ "url": "https://...", "contentType": "image/png", "filename": "mockup.png" }
|
|
73
|
-
]
|
|
74
|
-
}
|
|
75
65
|
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
66
|
+
Convex action ──POST /message/async──▶ Bridge AgentSession
|
|
67
|
+
│
|
|
68
|
+
▼
|
|
69
|
+
SDK query() iterator
|
|
70
|
+
│
|
|
71
|
+
┌───────────────────────────┼───────────────────────────┐
|
|
72
|
+
▼ ▼ ▼
|
|
73
|
+
/api/chat/stream /api/chat/event /api/chat/complete
|
|
74
|
+
(throttled ~50ms (assistant events, (final cost / status)
|
|
75
|
+
text + tool list, idempotent on uuid)
|
|
76
|
+
replaces preview)
|
|
77
|
+
│
|
|
78
|
+
▼
|
|
79
|
+
Convex (single source
|
|
80
|
+
of truth — UI reads
|
|
81
|
+
via realtime query)
|
|
86
82
|
```
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
##
|
|
100
|
-
|
|
101
|
-
|
|
84
|
+
- **Live preview** is throttled to ~50ms — the bridge buffers text deltas and
|
|
85
|
+
in-flight tool starts and POSTs them to `/api/chat/stream`. The Convex
|
|
86
|
+
mutation overwrites `thread.streamingState`. The UI reactively re-renders.
|
|
87
|
+
- **Persisted messages** go to `/api/chat/event` with the SDK's `event.uuid`
|
|
88
|
+
for idempotent insert. The mutation also clears `streamingState` inline so
|
|
89
|
+
the UI snaps from preview to persisted text without a flicker.
|
|
90
|
+
- **Stop button** flips the thread to `cancelled` via Convex first (UI is
|
|
91
|
+
freed immediately) and best-effort POSTs `/message/abort` to kill the SDK.
|
|
92
|
+
- **AskUserQuestion** posts `/api/chat/input-request` and blocks until the
|
|
93
|
+
dashboard answers via Convex action → bridge `/answer-question`.
|
|
94
|
+
|
|
95
|
+
## Resilience
|
|
96
|
+
|
|
97
|
+
- All Convex POSTs retry 3× with exponential backoff (1s, 2s, 4s).
|
|
98
|
+
- Persistent failures of `/api/chat/event` and `/api/chat/complete` are
|
|
99
|
+
appended to `~/.baker/relay-errors.jsonl` for post-mortem.
|
|
100
|
+
- The SDK iterator is wrapped in a 5 min watchdog: if no event arrives for
|
|
101
|
+
that long, the run is aborted and the thread is marked errored.
|
|
102
|
+
- The session registry evicts entries idle for >24h to bound memory.
|
|
103
|
+
- A new chat that arrives while a previous one is still running aborts the
|
|
104
|
+
previous run with a 30s timeout cap — a hung run cannot block the next message.
|
|
105
|
+
|
|
106
|
+
## Sending dispatch by hand
|
|
102
107
|
|
|
103
108
|
```bash
|
|
104
109
|
curl -X POST http://localhost:3000/message/async \
|
|
@@ -107,18 +112,19 @@ curl -X POST http://localhost:3000/message/async \
|
|
|
107
112
|
-d '{"threadId": "thread_abc123", "prompt": "Analyze the campaign performance"}'
|
|
108
113
|
```
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- `POST /api/chat/complete` — final callback with `{ threadId, isError, costUsd, errors? }`
|
|
113
|
-
- `POST /api/chat/input-request` — sets the thread to `awaiting_input` when the agent calls `AskUserQuestion`
|
|
114
|
-
- `POST /api/chat/input-resolved` — returns the thread to `streaming` after the user answers
|
|
115
|
+
The bridge processes in the background and writes to Convex; nothing is
|
|
116
|
+
returned in the dispatch response besides 202.
|
|
115
117
|
|
|
116
118
|
## Architecture
|
|
117
119
|
|
|
118
|
-
- **One session per thread** — `getOrCreateSession(threadId)` ensures
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- **
|
|
120
|
+
- **One session per thread** — `getOrCreateSession(threadId)` ensures every
|
|
121
|
+
message on the same thread shares an `AgentSession`. Multi-turn context is
|
|
122
|
+
preserved across bridge restarts via `~/.baker/sessions/<threadId>.json`.
|
|
123
|
+
- **Run-token cancellation** — each call to `sendAndStream` mints a Symbol; if
|
|
124
|
+
the symbol changes (abort, replace) the run cleans up and exits without
|
|
125
|
+
writing further. No globally-mutable abort flag.
|
|
126
|
+
- **Single-writer to Convex** — there is no parallel WebSocket. Convex is the
|
|
127
|
+
only place the UI reads state from, eliminating dual-write races.
|
|
122
128
|
|
|
123
129
|
## Development
|
|
124
130
|
|
|
@@ -139,56 +145,30 @@ pnpm --filter @koda-sl/baker-bridge unlink:local # Remove link, restore registr
|
|
|
139
145
|
|
|
140
146
|
### Auto-publish (CI)
|
|
141
147
|
|
|
142
|
-
Pushing to `main` with changes in `packages/bridge/` triggers the GitHub Actions
|
|
148
|
+
Pushing to `main` with changes in `packages/bridge/` triggers the GitHub Actions
|
|
149
|
+
workflow, which publishes to npm with the `latest` tag by default. To publish a
|
|
150
|
+
pre-release, use the workflow dispatch with `npm_tag: next`.
|
|
143
151
|
|
|
144
152
|
### Manual publish
|
|
145
153
|
|
|
146
154
|
From the monorepo root:
|
|
147
155
|
|
|
148
156
|
```bash
|
|
149
|
-
#
|
|
150
|
-
./scripts/publish-package.sh bridge
|
|
151
|
-
|
|
152
|
-
# Publish as @next (pre-release)
|
|
153
|
-
./scripts/publish-package.sh bridge next
|
|
157
|
+
./scripts/publish-package.sh bridge # @latest
|
|
158
|
+
./scripts/publish-package.sh bridge next # @next pre-release
|
|
154
159
|
```
|
|
155
160
|
|
|
156
|
-
|
|
161
|
+
Bump `version` in `package.json` (and the `VERSION` const in `src/cli.ts`)
|
|
162
|
+
before publishing.
|
|
157
163
|
|
|
158
164
|
### Testing a pre-release in sandboxes
|
|
159
165
|
|
|
160
|
-
When you publish a `@next` version, sandboxes won't use it automatically — they use the version baked into the template. To override:
|
|
161
|
-
|
|
162
166
|
```bash
|
|
163
|
-
# Point sandboxes to the pre-release version
|
|
164
167
|
npx convex env set BAKER_BRIDGE_VERSION <version>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
The sandbox startup (`nodeActions.ts`) checks this env var and runs `npm install -g @koda-sl/baker-bridge@<version>` before starting. This is optional — when unset, the default template version is used.
|
|
168
|
-
|
|
169
|
-
After testing, always clean up to avoid running pre-release in production:
|
|
170
|
-
|
|
171
|
-
```bash
|
|
168
|
+
# … test …
|
|
172
169
|
npx convex env remove BAKER_BRIDGE_VERSION
|
|
173
170
|
```
|
|
174
171
|
|
|
175
|
-
## Debugging relay failures
|
|
176
|
-
|
|
177
|
-
When `postToConvex` exhausts all retries, the error is appended to `~/.baker/relay-errors.jsonl` on the sandbox filesystem. Each line is a JSON object:
|
|
178
|
-
|
|
179
|
-
```jsonl
|
|
180
|
-
{"timestamp":"2026-04-24T15:19:55.123Z","path":"/api/chat/complete","threadId":"abc123","status":502,"responseBody":"Bad Gateway"}
|
|
181
|
-
{"timestamp":"2026-04-24T15:20:01.456Z","path":"/api/chat/event","threadId":"abc123","error":"fetch failed"}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
To inspect on a running sandbox:
|
|
185
|
-
|
|
186
|
-
```bash
|
|
187
|
-
cat ~/.baker/relay-errors.jsonl
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
If a thread is stuck in `"streaming"`, this file tells you whether the bridge failed to reach Convex (and why — auth, network, HTTP status).
|
|
191
|
-
|
|
192
172
|
## Requirements
|
|
193
173
|
|
|
194
174
|
- Node.js 18+
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
declare const args: string[];
|
|
3
3
|
declare const command: string;
|
|
4
|
-
declare const VERSION = "0.
|
|
4
|
+
declare const VERSION = "0.37.0";
|
|
5
5
|
declare function printUsage(): void;
|
|
6
6
|
declare function main(): Promise<void>;
|
|
7
7
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.js
CHANGED
package/dist/hono/agent.d.ts
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SlashCommand } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import type { ChatAttachment } from "./types.ts";
|
|
3
|
-
interface StreamCallbacks {
|
|
4
|
-
onMessage: (msg: SDKMessage) => void;
|
|
5
|
-
onInputRequest: (toolUseId: string, questions: unknown) => void;
|
|
6
|
-
onComplete: (costUsd: number, isError: boolean, errors?: string[], model?: string) => void;
|
|
7
|
-
onModelSelected?: (model: string) => void;
|
|
8
|
-
onRouterSample?: (model: string, messages: Array<{
|
|
9
|
-
role: string;
|
|
10
|
-
content: string;
|
|
11
|
-
}>) => void;
|
|
12
|
-
}
|
|
13
3
|
declare class AgentSession {
|
|
14
4
|
readonly threadId: string;
|
|
15
5
|
private sessionId;
|
|
16
6
|
private pendingQuestions;
|
|
17
7
|
private handlerCtx;
|
|
18
8
|
private options;
|
|
9
|
+
/** Identity token for the currently-running stream. abort() nulls this; if a
|
|
10
|
+
* later check sees a different value, the run knows it was abandoned. This
|
|
11
|
+
* replaces a global `aborted` flag whose reset-on-entry was racy. */
|
|
12
|
+
private activeRun;
|
|
13
|
+
private activeAbort;
|
|
14
|
+
private streamingPromise;
|
|
15
|
+
private replacingStream;
|
|
16
|
+
private currentTurnTools;
|
|
17
|
+
private model;
|
|
18
|
+
lastUsedAt: number;
|
|
19
19
|
constructor(threadId: string);
|
|
20
|
-
/**
|
|
20
|
+
/** Cancel any in-flight run. The next status check inside runStream will trip. */
|
|
21
|
+
abort(): void;
|
|
21
22
|
setSessionId(id: string): void;
|
|
22
|
-
sendAndStream(content: string, callbacks: StreamCallbacks, attachments?: ChatAttachment[]): Promise<void>;
|
|
23
23
|
resolveQuestion(toolUseId: string, answers: Record<string, string>): boolean;
|
|
24
|
+
sendAndStream(content: string, attachments?: ChatAttachment[]): Promise<void>;
|
|
25
|
+
private runStream;
|
|
26
|
+
private handleStreamEvent;
|
|
27
|
+
private handleContentBlockStart;
|
|
28
|
+
private handleMessage;
|
|
29
|
+
private preparePrompt;
|
|
30
|
+
private completeWithCancel;
|
|
31
|
+
private completeWithError;
|
|
24
32
|
}
|
|
25
|
-
export declare function getSlashCommands(): Promise<SlashCommand[]>;
|
|
26
33
|
export declare function getOrCreateSession(threadId: string): Promise<AgentSession>;
|
|
34
|
+
export declare function abortSession(threadId: string): boolean;
|
|
35
|
+
export declare function getSlashCommands(): Promise<SlashCommand[]>;
|
|
27
36
|
export {};
|
|
28
37
|
//# sourceMappingURL=agent.d.ts.map
|
package/dist/hono/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/hono/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/hono/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAgBxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAuCjD,cAAM,YAAY;IAChB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,gBAAgB,CAAsC;IAC9D,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,OAAO,CAAU;IAEzB;;0EAEsE;IACtE,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,KAAK,CAAqB;IAClC,UAAU,EAAE,MAAM,CAAc;gBAEpB,QAAQ,EAAE,MAAM;IAM5B,kFAAkF;IAClF,KAAK,IAAI,IAAI;IAkBb,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI9B,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO;IAUtE,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YA8BrE,SAAS;IAgFvB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,uBAAuB;YAiBjB,aAAa;YAiBb,aAAa;YAoBb,kBAAkB;YAYlB,iBAAiB;CAehC;AAqFD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAahF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOtD;AAWD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAuBhE"}
|