@koda-sl/baker-bridge 0.36.0-dev.7ebf7748-dev.7ebf7748 → 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 -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 +233 -114
- 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 +212 -53
- 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/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,72 +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` | `/
|
|
57
|
-
| `POST` | `/answer-question` | Bearer token | Resolve a pending `AskUserQuestion` tool call |
|
|
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` |
|
|
58
62
|
|
|
59
|
-
##
|
|
63
|
+
## Chat flow
|
|
60
64
|
|
|
61
|
-
Connect to `/ws?token=<AUTH_TOKEN>` for real-time bidirectional communication.
|
|
62
|
-
|
|
63
|
-
### Client to Server
|
|
64
|
-
|
|
65
|
-
**Chat message:**
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
{
|
|
69
|
-
"type": "chat",
|
|
70
|
-
"threadId": "thread_abc123",
|
|
71
|
-
"content": "Build a landing page for our new product",
|
|
72
|
-
"attachments": [
|
|
73
|
-
{ "url": "https://...", "contentType": "image/png", "filename": "mockup.png" }
|
|
74
|
-
]
|
|
75
|
-
}
|
|
76
65
|
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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)
|
|
87
82
|
```
|
|
88
83
|
|
|
89
|
-
**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
##
|
|
112
|
-
|
|
113
|
-
For server-to-server dispatch where you don't need real-time streaming. Returns `202 Accepted` immediately and processes in the background. Results are delivered via Convex callbacks.
|
|
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
|
|
114
107
|
|
|
115
108
|
```bash
|
|
116
109
|
curl -X POST http://localhost:3000/message/async \
|
|
@@ -119,18 +112,19 @@ curl -X POST http://localhost:3000/message/async \
|
|
|
119
112
|
-d '{"threadId": "thread_abc123", "prompt": "Analyze the campaign performance"}'
|
|
120
113
|
```
|
|
121
114
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- `POST /api/chat/complete` — final callback with `{ threadId, isError, costUsd, errors? }`
|
|
125
|
-
- `POST /api/chat/input-request` — sets the thread to `awaiting_input` when the agent calls `AskUserQuestion`
|
|
126
|
-
- `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.
|
|
127
117
|
|
|
128
118
|
## Architecture
|
|
129
119
|
|
|
130
|
-
- **One session per thread** — `getOrCreateSession(threadId)` ensures
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- **
|
|
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.
|
|
134
128
|
|
|
135
129
|
## Development
|
|
136
130
|
|
|
@@ -151,36 +145,27 @@ pnpm --filter @koda-sl/baker-bridge unlink:local # Remove link, restore registr
|
|
|
151
145
|
|
|
152
146
|
### Auto-publish (CI)
|
|
153
147
|
|
|
154
|
-
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`.
|
|
155
151
|
|
|
156
152
|
### Manual publish
|
|
157
153
|
|
|
158
154
|
From the monorepo root:
|
|
159
155
|
|
|
160
156
|
```bash
|
|
161
|
-
#
|
|
162
|
-
./scripts/publish-package.sh bridge
|
|
163
|
-
|
|
164
|
-
# Publish as @next (pre-release)
|
|
165
|
-
./scripts/publish-package.sh bridge next
|
|
157
|
+
./scripts/publish-package.sh bridge # @latest
|
|
158
|
+
./scripts/publish-package.sh bridge next # @next pre-release
|
|
166
159
|
```
|
|
167
160
|
|
|
168
|
-
|
|
161
|
+
Bump `version` in `package.json` (and the `VERSION` const in `src/cli.ts`)
|
|
162
|
+
before publishing.
|
|
169
163
|
|
|
170
164
|
### Testing a pre-release in sandboxes
|
|
171
165
|
|
|
172
|
-
When you publish a `@next` version, sandboxes won't use it automatically — they use the version baked into the template. To override:
|
|
173
|
-
|
|
174
166
|
```bash
|
|
175
|
-
# Point sandboxes to the pre-release version
|
|
176
167
|
npx convex env set BAKER_BRIDGE_VERSION <version>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
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.
|
|
180
|
-
|
|
181
|
-
After testing, always clean up to avoid running pre-release in production:
|
|
182
|
-
|
|
183
|
-
```bash
|
|
168
|
+
# … test …
|
|
184
169
|
npx convex env remove BAKER_BRIDGE_VERSION
|
|
185
170
|
```
|
|
186
171
|
|
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,34 +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;
|
|
19
13
|
private activeAbort;
|
|
20
|
-
private
|
|
14
|
+
private streamingPromise;
|
|
15
|
+
private replacingStream;
|
|
16
|
+
private currentTurnTools;
|
|
17
|
+
private model;
|
|
18
|
+
lastUsedAt: number;
|
|
21
19
|
constructor(threadId: string);
|
|
20
|
+
/** Cancel any in-flight run. The next status check inside runStream will trip. */
|
|
22
21
|
abort(): void;
|
|
23
|
-
/** Restore a previously persisted session ID (e.g. loaded from disk after restart). */
|
|
24
22
|
setSessionId(id: string): void;
|
|
25
|
-
private preparePrompt;
|
|
26
|
-
private handleStreamError;
|
|
27
|
-
sendAndStream(content: string, callbacks: StreamCallbacks, attachments?: ChatAttachment[]): Promise<void>;
|
|
28
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;
|
|
29
32
|
}
|
|
30
|
-
export declare function getSlashCommands(): Promise<SlashCommand[]>;
|
|
31
|
-
export declare function abortSession(threadId: string): boolean;
|
|
32
33
|
export declare function getOrCreateSession(threadId: string): Promise<AgentSession>;
|
|
34
|
+
export declare function abortSession(threadId: string): boolean;
|
|
35
|
+
export declare function getSlashCommands(): Promise<SlashCommand[]>;
|
|
33
36
|
export {};
|
|
34
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"}
|