@koda-sl/baker-bridge 0.49.0 → 0.50.0-dev.36aef87f5

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.
Files changed (41) hide show
  1. package/README.md +6 -1
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +1 -1
  4. package/dist/hono/agent-session.d.ts +3 -0
  5. package/dist/hono/agent-session.d.ts.map +1 -1
  6. package/dist/hono/agent-session.js +15 -0
  7. package/dist/hono/agent-session.js.map +1 -1
  8. package/dist/hono/agent.d.ts +3 -0
  9. package/dist/hono/agent.d.ts.map +1 -1
  10. package/dist/hono/agent.js +33 -2
  11. package/dist/hono/agent.js.map +1 -1
  12. package/dist/hono/agent.test.js +26 -1
  13. package/dist/hono/agent.test.js.map +1 -1
  14. package/dist/hono/bridge-job.d.ts +21 -0
  15. package/dist/hono/bridge-job.d.ts.map +1 -1
  16. package/dist/hono/bridge-job.js +98 -2
  17. package/dist/hono/bridge-job.js.map +1 -1
  18. package/dist/hono/bridge-job.test.js +131 -5
  19. package/dist/hono/bridge-job.test.js.map +1 -1
  20. package/dist/hono/convex.d.ts +2 -0
  21. package/dist/hono/convex.d.ts.map +1 -1
  22. package/dist/hono/convex.js +8 -0
  23. package/dist/hono/convex.js.map +1 -1
  24. package/dist/hono/relay.d.ts +7 -1
  25. package/dist/hono/relay.d.ts.map +1 -1
  26. package/dist/hono/relay.js +6 -0
  27. package/dist/hono/relay.js.map +1 -1
  28. package/dist/hono/server.d.ts.map +1 -1
  29. package/dist/hono/server.js +21 -0
  30. package/dist/hono/server.js.map +1 -1
  31. package/dist/hono/session-continuity.integration.test.js +4 -0
  32. package/dist/hono/session-continuity.integration.test.js.map +1 -1
  33. package/dist/hono/tag-input.d.ts +68 -0
  34. package/dist/hono/tag-input.d.ts.map +1 -0
  35. package/dist/hono/tag-input.js +167 -0
  36. package/dist/hono/tag-input.js.map +1 -0
  37. package/dist/hono/tag-input.test.d.ts +2 -0
  38. package/dist/hono/tag-input.test.d.ts.map +1 -0
  39. package/dist/hono/tag-input.test.js +96 -0
  40. package/dist/hono/tag-input.test.js.map +1 -0
  41. package/package.json +1 -1
@@ -0,0 +1,167 @@
1
+ /**
2
+ * `baker_ui` — in-process SDK MCP server hosting interactive dashboard tools.
3
+ *
4
+ * `request_tag_input` is the blocking tag-change approval form. EVERY tag change
5
+ * (create / edit / delete) goes through it — the agent proposes one or more
6
+ * changes, and each becomes a tab in a single blocking form (mirroring
7
+ * AskUserQuestion's multi-question bundling). The bridge intercepts the call in
8
+ * `canUseTool`, parks the turn on a pending promise, and the dashboard renders
9
+ * the tabbed form. The user reviews/edits, fills any secrets (which the model
10
+ * must never see — API keys, OAuth connections), and approves or declines each
11
+ * tab. Submissions write straight into the chat's staged tag draft in Convex;
12
+ * the bridge receives only the REDACTED per-tab results, injects them into
13
+ * `updatedInput.resolution`, and this tool's trivial handler echoes them back.
14
+ */
15
+ import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
16
+ import { z } from "zod";
17
+ export const BAKER_UI_SERVER_NAME = "baker_ui";
18
+ export const TAG_INPUT_TOOL_NAME = "mcp__baker_ui__request_tag_input";
19
+ // Mirror of `TAG_SECRET_FIELDS` in packages/api/src/tags.ts (the bridge is
20
+ // published standalone and cannot depend on @baker/api). The backend strips
21
+ // secret keys again at /api/chat/tag-input-request — this mirror is UX-side
22
+ // defense in depth, not the enforcement point.
23
+ const SECRET_FIELDS_BY_TYPE = {
24
+ meta: ["accessToken"],
25
+ amplitude: [],
26
+ googleAds: ["oauthProviderId", "customerAccountId", "customerId", "loginCustomerId"],
27
+ tiktok: ["accessToken"],
28
+ vwo: [],
29
+ hotjar: [],
30
+ clarity: [],
31
+ pinterest: ["conversionToken"],
32
+ code: [],
33
+ googleAnalytics: ["apiSecret"],
34
+ googleTagManager: ["authorizationToken"],
35
+ hubspot: [],
36
+ linkedinInsightTag: ["oauthProviderId"],
37
+ onetrust: [],
38
+ posthog: [],
39
+ datafast: ["apiKey"],
40
+ recaptcha: [],
41
+ twitterAds: ["oauthProviderId"],
42
+ };
43
+ export const TAG_TYPES = Object.keys(SECRET_FIELDS_BY_TYPE);
44
+ const tagChangeShape = z.object({
45
+ mode: z.enum(["create", "edit", "delete"]).describe("create a new tag, edit an existing one, or delete one"),
46
+ tagType: z.enum(TAG_TYPES).describe("Marketing tag type (matches `baker tags list` types)"),
47
+ ref: z
48
+ .string()
49
+ .optional()
50
+ .describe("Edit/delete: the tag id or tag_temp_* ref from `baker tags list`. Omit for a fresh create."),
51
+ prefilledConfig: z
52
+ .record(z.string(), z.string())
53
+ .optional()
54
+ .describe("Non-secret config values to prefill this tab with. Never place secret values here."),
55
+ requestedSecretFields: z
56
+ .array(z.string())
57
+ .optional()
58
+ .describe("Secret field names the user should provide (e.g. accessToken, apiSecret, oauthProviderId)"),
59
+ message: z.string().optional().describe("One short sentence shown on this tab explaining the change"),
60
+ });
61
+ const tagInputRequestShape = {
62
+ changes: z
63
+ .array(tagChangeShape)
64
+ .min(1)
65
+ .max(8)
66
+ .describe("One or more tag changes; each becomes a tab in a single approval form the user resolves together"),
67
+ resolution: z
68
+ .record(z.string(), z.unknown())
69
+ .optional()
70
+ .describe("Internal — injected by the bridge after the user resolves the form. Never set this yourself."),
71
+ };
72
+ export const tagInputResultSchema = z.discriminatedUnion("status", [
73
+ z.object({
74
+ status: z.literal("submitted"),
75
+ ref: z.string(),
76
+ type: z.string(),
77
+ identifier: z.record(z.string(), z.string()).optional(),
78
+ secretFieldsSet: z.array(z.string()),
79
+ note: z.string().optional(),
80
+ }),
81
+ z.object({
82
+ status: z.literal("declined"),
83
+ reason: z.string().optional(),
84
+ }),
85
+ ]);
86
+ /** The redacted resolution the bridge injects and echoes back: one result per requested change. */
87
+ export const tagChangeResolutionSchema = z.object({ results: z.array(tagInputResultSchema) });
88
+ /** Parse-at-boundary for the bridge's `/tag-input-decision` endpoint. Returns null on any mismatch. */
89
+ export function parseTagChangeResolution(value) {
90
+ const parsed = tagChangeResolutionSchema.safeParse(value);
91
+ return parsed.success ? parsed.data : null;
92
+ }
93
+ /**
94
+ * Normalize the raw tool input into a validated change list. `canUseTool` fires
95
+ * with the model's RAW input — before any MCP schema validation — so a model
96
+ * that ignores the schema (e.g. emits the legacy single-change shape with
97
+ * mode/tagType at the top level) reaches the bridge unchecked. Wrap the legacy
98
+ * shape, drop invalid entries, and return [] for garbage so the caller can
99
+ * DENY with an actionable message instead of parking the turn on a form the
100
+ * dashboard will never render.
101
+ */
102
+ export function normalizeTagInputToolInput(input) {
103
+ const raw = Array.isArray(input.changes)
104
+ ? input.changes
105
+ : typeof input.tagType === "string"
106
+ ? [input]
107
+ : [];
108
+ return raw.flatMap((entry) => {
109
+ const parsed = tagChangeShape.safeParse(entry);
110
+ return parsed.success ? [parsed.data] : [];
111
+ });
112
+ }
113
+ /**
114
+ * Strip secret-field values a model may have (incorrectly) placed into any
115
+ * change's `prefilledConfig`, moving their NAMES into `requestedSecretFields` so
116
+ * the user is still asked — just without the leaked value travelling on.
117
+ */
118
+ export function sanitizeTagChanges(changes) {
119
+ return changes.map((change) => {
120
+ const secretFields = SECRET_FIELDS_BY_TYPE[change.tagType] ?? [];
121
+ const prefilled = change.prefilledConfig ?? {};
122
+ const cleaned = {};
123
+ const leaked = [];
124
+ for (const [key, value] of Object.entries(prefilled)) {
125
+ if (secretFields.includes(key)) {
126
+ leaked.push(key);
127
+ }
128
+ else {
129
+ cleaned[key] = value;
130
+ }
131
+ }
132
+ const requested = [...new Set([...(change.requestedSecretFields ?? []), ...leaked])];
133
+ return {
134
+ mode: change.mode,
135
+ tagType: change.tagType,
136
+ ...(change.ref !== undefined ? { ref: change.ref } : {}),
137
+ ...(Object.keys(cleaned).length > 0 ? { prefilledConfig: cleaned } : {}),
138
+ ...(requested.length > 0 ? { requestedSecretFields: requested } : {}),
139
+ ...(change.message !== undefined ? { message: change.message } : {}),
140
+ };
141
+ });
142
+ }
143
+ const TAG_INPUT_TOOL_DESCRIPTION = [
144
+ "Propose one or more marketing-tag changes (create / edit / delete) for the user to approve in the dashboard chat. BLOCKS until the user resolves the form.",
145
+ "This is the ONLY way to change tags — every create, edit, and delete goes through this approval, so never stage tag changes silently.",
146
+ "Batch related changes into one call: each entry in `changes` becomes a tab the user reviews together.",
147
+ "Prefill every non-secret field you already know via prefilledConfig, and list needed secret fields (API keys, access tokens, OAuth connections) in requestedSecretFields — never put secret values in prefilledConfig and never ask for secrets in plain chat.",
148
+ "The user's secret values go straight into the staged tag draft; the results you receive are redacted (field names only). Staged changes apply to production only when the chat is published.",
149
+ ].join(" ");
150
+ /**
151
+ * The handler runs only AFTER `canUseTool` resolved (the bridge blocks there
152
+ * and injects `resolution` into the input) — so it just echoes the redacted
153
+ * resolution as the tool result.
154
+ */
155
+ export function createBakerUiServer() {
156
+ return createSdkMcpServer({
157
+ name: BAKER_UI_SERVER_NAME,
158
+ version: "1.0.0",
159
+ tools: [
160
+ tool("request_tag_input", TAG_INPUT_TOOL_DESCRIPTION, tagInputRequestShape, (args) => {
161
+ const resolution = args.resolution ?? { results: [] };
162
+ return Promise.resolve({ content: [{ type: "text", text: JSON.stringify(resolution) }] });
163
+ }),
164
+ ],
165
+ });
166
+ }
167
+ //# sourceMappingURL=tag-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-input.js","sourceRoot":"","sources":["../../src/hono/tag-input.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tag-input.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-input.test.d.ts","sourceRoot":"","sources":["../../src/hono/tag-input.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { normalizeTagInputToolInput, parseTagChangeResolution, sanitizeTagChanges } from "./tag-input.js";
3
+ describe("normalizeTagInputToolInput", () => {
4
+ it("passes a valid changes array through", () => {
5
+ const changes = normalizeTagInputToolInput({
6
+ changes: [{ mode: "create", tagType: "meta", prefilledConfig: { pixelId: "123" } }],
7
+ });
8
+ expect(changes).toHaveLength(1);
9
+ expect(changes[0]).toMatchObject({ mode: "create", tagType: "meta" });
10
+ });
11
+ it("wraps the legacy single-change shape (mode/tagType at top level) instead of hanging the turn", () => {
12
+ const changes = normalizeTagInputToolInput({
13
+ mode: "create",
14
+ tagType: "meta",
15
+ prefilledConfig: { pixelId: "555000111" },
16
+ requestedSecretFields: ["accessToken"],
17
+ message: "Adding Meta pixel",
18
+ });
19
+ expect(changes).toHaveLength(1);
20
+ expect(changes[0]).toMatchObject({ mode: "create", tagType: "meta", prefilledConfig: { pixelId: "555000111" } });
21
+ });
22
+ it("drops invalid entries and returns [] for garbage so the caller can deny with a clear error", () => {
23
+ expect(normalizeTagInputToolInput({ changes: [{ mode: "explode", tagType: "meta" }] })).toEqual([]);
24
+ expect(normalizeTagInputToolInput({ changes: "nope" })).toEqual([]);
25
+ expect(normalizeTagInputToolInput({})).toEqual([]);
26
+ });
27
+ });
28
+ describe("sanitizeTagChanges", () => {
29
+ it("moves leaked secret prefill values into requested fields, dropping the value", () => {
30
+ const [sanitized] = sanitizeTagChanges([
31
+ { mode: "create", tagType: "meta", prefilledConfig: { pixelId: "123", accessToken: "EAAB-leak" } },
32
+ ]);
33
+ expect(sanitized.prefilledConfig).toEqual({ pixelId: "123" });
34
+ expect(sanitized.requestedSecretFields).toEqual(["accessToken"]);
35
+ expect(JSON.stringify(sanitized)).not.toContain("EAAB-leak");
36
+ });
37
+ it("keeps non-secret prefill and merges requested fields without duplicates", () => {
38
+ const [sanitized] = sanitizeTagChanges([
39
+ {
40
+ mode: "edit",
41
+ tagType: "googleAnalytics",
42
+ ref: "jd7tag",
43
+ prefilledConfig: { measurementId: "G-ABC", apiSecret: "shhh" },
44
+ requestedSecretFields: ["apiSecret"],
45
+ message: "Rotate the secret",
46
+ },
47
+ ]);
48
+ expect(sanitized.prefilledConfig).toEqual({ measurementId: "G-ABC" });
49
+ expect(sanitized.requestedSecretFields).toEqual(["apiSecret"]);
50
+ expect(sanitized.message).toBe("Rotate the secret");
51
+ });
52
+ it("sanitizes every change in a batch independently", () => {
53
+ const sanitized = sanitizeTagChanges([
54
+ { mode: "create", tagType: "meta", prefilledConfig: { pixelId: "1", accessToken: "leak-1" } },
55
+ { mode: "delete", tagType: "clarity", ref: "abc123" },
56
+ ]);
57
+ expect(sanitized).toHaveLength(2);
58
+ expect(sanitized[0]?.prefilledConfig).toEqual({ pixelId: "1" });
59
+ expect(JSON.stringify(sanitized)).not.toContain("leak-1");
60
+ expect(sanitized[1]).toMatchObject({ mode: "delete", tagType: "clarity", ref: "abc123" });
61
+ });
62
+ it("leaves unknown tag types untouched (backend validates)", () => {
63
+ const [sanitized] = sanitizeTagChanges([
64
+ { mode: "create", tagType: "not-a-type", prefilledConfig: { anything: "goes" } },
65
+ ]);
66
+ expect(sanitized.prefilledConfig).toEqual({ anything: "goes" });
67
+ });
68
+ });
69
+ describe("parseTagChangeResolution", () => {
70
+ it("accepts a per-tab results array", () => {
71
+ const resolution = parseTagChangeResolution({
72
+ results: [
73
+ {
74
+ status: "submitted",
75
+ ref: "tag_temp_x",
76
+ type: "meta",
77
+ identifier: { pixelId: "123" },
78
+ secretFieldsSet: ["accessToken"],
79
+ },
80
+ { status: "declined", reason: "skipped" },
81
+ ],
82
+ });
83
+ expect(resolution?.results).toHaveLength(2);
84
+ expect(resolution?.results[0]).toMatchObject({ status: "submitted", ref: "tag_temp_x" });
85
+ expect(resolution?.results[1]).toEqual({ status: "declined", reason: "skipped" });
86
+ });
87
+ it("accepts an empty results array (all declined / nothing resolved)", () => {
88
+ expect(parseTagChangeResolution({ results: [] })).toEqual({ results: [] });
89
+ });
90
+ it("rejects malformed payloads", () => {
91
+ expect(parseTagChangeResolution({ results: [{ status: "submitted" }] })).toBeNull();
92
+ expect(parseTagChangeResolution({ results: "nope" })).toBeNull();
93
+ expect(parseTagChangeResolution(null)).toBeNull();
94
+ });
95
+ });
96
+ //# sourceMappingURL=tag-input.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-input.test.js","sourceRoot":"","sources":["../../src/hono/tag-input.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAE1G,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,OAAO,GAAG,0BAA0B,CAAC;YACzC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;SACpF,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8FAA8F,EAAE,GAAG,EAAE;QACtG,MAAM,OAAO,GAAG,0BAA0B,CAAC;YACzC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,MAAM;YACf,eAAe,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;YACzC,qBAAqB,EAAE,CAAC,aAAa,CAAC;YACtC,OAAO,EAAE,mBAAmB;SAC7B,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IACnH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,MAAM,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,MAAe,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7E,MAAM,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,CAAC,SAAS,CAAC,GAAG,kBAAkB,CAAC;YACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE;SACnG,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,SAAS,CAAC,GAAG,kBAAkB,CAAC;YACrC;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,iBAAiB;gBAC1B,GAAG,EAAE,QAAQ;gBACb,eAAe,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC9D,qBAAqB,EAAE,CAAC,WAAW,CAAC;gBACpC,OAAO,EAAE,mBAAmB;aAC7B;SACF,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACtE,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;QAC/D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,SAAS,GAAG,kBAAkB,CAAC;YACnC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE;YAC7F,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE;SACtD,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,SAAS,CAAC,GAAG,kBAAkB,CAAC;YACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;SACjF,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,UAAU,GAAG,wBAAwB,CAAC;YAC1C,OAAO,EAAE;gBACP;oBACE,MAAM,EAAE,WAAW;oBACnB,GAAG,EAAE,YAAY;oBACjB,IAAI,EAAE,MAAM;oBACZ,UAAU,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;oBAC9B,eAAe,EAAE,CAAC,aAAa,CAAC;iBACjC;gBACD,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE;aAC1C;SACF,CAAC,CAAC;QACH,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;QACzF,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,wBAAwB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,wBAAwB,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACpF,MAAM,CAAC,wBAAwB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjE,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koda-sl/baker-bridge",
3
- "version": "0.49.0",
3
+ "version": "0.50.0-dev.36aef87f5",
4
4
  "description": "HTTP server wrapping the Claude Agent SDK. Persists chat events to Convex; the dashboard subscribes via Convex realtime",
5
5
  "license": "MIT",
6
6
  "type": "module",