@koda-sl/baker-bridge 0.118.0-dev.4a0a40cc5 → 0.118.0

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 (147) hide show
  1. package/README.md +17 -0
  2. package/dist/broker/baker-api.d.ts +23 -0
  3. package/dist/broker/baker-api.d.ts.map +1 -0
  4. package/dist/broker/baker-api.js +55 -0
  5. package/dist/broker/baker-api.js.map +1 -0
  6. package/dist/broker/broker-client.d.ts +10 -0
  7. package/dist/broker/broker-client.d.ts.map +1 -0
  8. package/dist/broker/broker-client.js +60 -0
  9. package/dist/broker/broker-client.js.map +1 -0
  10. package/dist/broker/ca.d.ts +38 -0
  11. package/dist/broker/ca.d.ts.map +1 -0
  12. package/dist/broker/ca.js +80 -0
  13. package/dist/broker/ca.js.map +1 -0
  14. package/dist/broker/ca.test.d.ts +2 -0
  15. package/dist/broker/ca.test.d.ts.map +1 -0
  16. package/dist/broker/ca.test.js +58 -0
  17. package/dist/broker/ca.test.js.map +1 -0
  18. package/dist/broker/credential-cache.d.ts +24 -0
  19. package/dist/broker/credential-cache.d.ts.map +1 -0
  20. package/dist/broker/credential-cache.js +69 -0
  21. package/dist/broker/credential-cache.js.map +1 -0
  22. package/dist/broker/credential-cache.test.d.ts +2 -0
  23. package/dist/broker/credential-cache.test.d.ts.map +1 -0
  24. package/dist/broker/credential-cache.test.js +120 -0
  25. package/dist/broker/credential-cache.test.js.map +1 -0
  26. package/dist/broker/credential-text.d.ts +45 -0
  27. package/dist/broker/credential-text.d.ts.map +1 -0
  28. package/dist/broker/credential-text.js +103 -0
  29. package/dist/broker/credential-text.js.map +1 -0
  30. package/dist/broker/credential-text.test.d.ts +2 -0
  31. package/dist/broker/credential-text.test.d.ts.map +1 -0
  32. package/dist/broker/credential-text.test.js +54 -0
  33. package/dist/broker/credential-text.test.js.map +1 -0
  34. package/dist/broker/env.d.ts +19 -0
  35. package/dist/broker/env.d.ts.map +1 -0
  36. package/dist/broker/env.js +16 -0
  37. package/dist/broker/env.js.map +1 -0
  38. package/dist/broker/main.d.ts +3 -0
  39. package/dist/broker/main.d.ts.map +1 -0
  40. package/dist/broker/main.js +51 -0
  41. package/dist/broker/main.js.map +1 -0
  42. package/dist/broker/proxy.d.ts +24 -0
  43. package/dist/broker/proxy.d.ts.map +1 -0
  44. package/dist/broker/proxy.integration.test.d.ts +2 -0
  45. package/dist/broker/proxy.integration.test.d.ts.map +1 -0
  46. package/dist/broker/proxy.integration.test.js +201 -0
  47. package/dist/broker/proxy.integration.test.js.map +1 -0
  48. package/dist/broker/proxy.js +147 -0
  49. package/dist/broker/proxy.js.map +1 -0
  50. package/dist/broker/rewrite.d.ts +44 -0
  51. package/dist/broker/rewrite.d.ts.map +1 -0
  52. package/dist/broker/rewrite.js +291 -0
  53. package/dist/broker/rewrite.js.map +1 -0
  54. package/dist/broker/rewrite.test.d.ts +2 -0
  55. package/dist/broker/rewrite.test.d.ts.map +1 -0
  56. package/dist/broker/rewrite.test.js +171 -0
  57. package/dist/broker/rewrite.test.js.map +1 -0
  58. package/dist/broker/socket-service.d.ts +18 -0
  59. package/dist/broker/socket-service.d.ts.map +1 -0
  60. package/dist/broker/socket-service.js +103 -0
  61. package/dist/broker/socket-service.js.map +1 -0
  62. package/dist/broker/socket-service.test.d.ts +2 -0
  63. package/dist/broker/socket-service.test.d.ts.map +1 -0
  64. package/dist/broker/socket-service.test.js +73 -0
  65. package/dist/broker/socket-service.test.js.map +1 -0
  66. package/dist/broker/token-bucket.d.ts +9 -0
  67. package/dist/broker/token-bucket.d.ts.map +1 -0
  68. package/dist/broker/token-bucket.js +18 -0
  69. package/dist/broker/token-bucket.js.map +1 -0
  70. package/dist/broker/usage-reporter.d.ts +18 -0
  71. package/dist/broker/usage-reporter.d.ts.map +1 -0
  72. package/dist/broker/usage-reporter.js +31 -0
  73. package/dist/broker/usage-reporter.js.map +1 -0
  74. package/dist/broker/usage-reporter.test.d.ts +2 -0
  75. package/dist/broker/usage-reporter.test.d.ts.map +1 -0
  76. package/dist/broker/usage-reporter.test.js +20 -0
  77. package/dist/broker/usage-reporter.test.js.map +1 -0
  78. package/dist/hono/agent-session.d.ts +3 -0
  79. package/dist/hono/agent-session.d.ts.map +1 -1
  80. package/dist/hono/agent-session.js +15 -0
  81. package/dist/hono/agent-session.js.map +1 -1
  82. package/dist/hono/agent.d.ts +2 -0
  83. package/dist/hono/agent.d.ts.map +1 -1
  84. package/dist/hono/agent.js +10 -2
  85. package/dist/hono/agent.js.map +1 -1
  86. package/dist/hono/avatar-casting.d.ts.map +1 -1
  87. package/dist/hono/avatar-casting.js +3 -2
  88. package/dist/hono/avatar-casting.js.map +1 -1
  89. package/dist/hono/baker-ui-tool-schemas.test.js +30 -4
  90. package/dist/hono/baker-ui-tool-schemas.test.js.map +1 -1
  91. package/dist/hono/blocking-card.d.ts +12 -0
  92. package/dist/hono/blocking-card.d.ts.map +1 -0
  93. package/dist/hono/blocking-card.js +12 -0
  94. package/dist/hono/blocking-card.js.map +1 -0
  95. package/dist/hono/bridge-job.d.ts +15 -2
  96. package/dist/hono/bridge-job.d.ts.map +1 -1
  97. package/dist/hono/bridge-job.js +80 -10
  98. package/dist/hono/bridge-job.js.map +1 -1
  99. package/dist/hono/bridge-job.test.js +143 -1
  100. package/dist/hono/bridge-job.test.js.map +1 -1
  101. package/dist/hono/connection-input.d.ts +15 -1
  102. package/dist/hono/connection-input.d.ts.map +1 -1
  103. package/dist/hono/connection-input.js +24 -5
  104. package/dist/hono/connection-input.js.map +1 -1
  105. package/dist/hono/connection-input.test.js +17 -8
  106. package/dist/hono/connection-input.test.js.map +1 -1
  107. package/dist/hono/convex.d.ts +8 -0
  108. package/dist/hono/convex.d.ts.map +1 -1
  109. package/dist/hono/convex.js +8 -0
  110. package/dist/hono/convex.js.map +1 -1
  111. package/dist/hono/credential-input.d.ts +39 -0
  112. package/dist/hono/credential-input.d.ts.map +1 -0
  113. package/dist/hono/credential-input.js +78 -0
  114. package/dist/hono/credential-input.js.map +1 -0
  115. package/dist/hono/credential-scrub.d.ts +41 -0
  116. package/dist/hono/credential-scrub.d.ts.map +1 -0
  117. package/dist/hono/credential-scrub.js +152 -0
  118. package/dist/hono/credential-scrub.js.map +1 -0
  119. package/dist/hono/credential-scrub.test.d.ts +2 -0
  120. package/dist/hono/credential-scrub.test.d.ts.map +1 -0
  121. package/dist/hono/credential-scrub.test.js +105 -0
  122. package/dist/hono/credential-scrub.test.js.map +1 -0
  123. package/dist/hono/flow-input.d.ts.map +1 -1
  124. package/dist/hono/flow-input.js +3 -2
  125. package/dist/hono/flow-input.js.map +1 -1
  126. package/dist/hono/prompt-context.d.ts +8 -1
  127. package/dist/hono/prompt-context.d.ts.map +1 -1
  128. package/dist/hono/prompt-context.js +45 -7
  129. package/dist/hono/prompt-context.js.map +1 -1
  130. package/dist/hono/prompt-context.test.js +35 -1
  131. package/dist/hono/prompt-context.test.js.map +1 -1
  132. package/dist/hono/relay.d.ts +10 -1
  133. package/dist/hono/relay.d.ts.map +1 -1
  134. package/dist/hono/relay.js +6 -0
  135. package/dist/hono/relay.js.map +1 -1
  136. package/dist/hono/server.d.ts.map +1 -1
  137. package/dist/hono/server.js +22 -0
  138. package/dist/hono/server.js.map +1 -1
  139. package/dist/hono/tag-input.d.ts +122 -3
  140. package/dist/hono/tag-input.d.ts.map +1 -1
  141. package/dist/hono/tag-input.js +27 -17
  142. package/dist/hono/tag-input.js.map +1 -1
  143. package/dist/hono/tag-input.test.js +19 -1
  144. package/dist/hono/tag-input.test.js.map +1 -1
  145. package/dist/hono/types.d.ts +8 -0
  146. package/dist/hono/types.d.ts.map +1 -1
  147. package/package.json +5 -2
@@ -18,6 +18,7 @@ vi.mock("./runtime-files.ts", async (importOriginal) => {
18
18
  import { AVATAR_CASTING_TOOL_NAME } from "./avatar-casting.js";
19
19
  import { BridgeJobExecutor, createStallDetector, isContextOverflowRefusal, isControlChannelDeny, parsePlanDecision, remapAnswersToQuestionText, } from "./bridge-job.js";
20
20
  import { CONNECTION_INPUT_TOOL_NAME } from "./connection-input.js";
21
+ import { CREDENTIAL_INPUT_TOOL_NAME } from "./credential-input.js";
21
22
  import { FLOW_INPUT_TOOL_NAME } from "./flow-input.js";
22
23
  import { bridgeRuntimeFiles } from "./runtime-files.js";
23
24
  import { TAG_INPUT_TOOL_NAME } from "./tag-input.js";
@@ -49,6 +50,7 @@ function activeControls() {
49
50
  pendingTagInputs: new Map(),
50
51
  pendingFlowInputs: new Map(),
51
52
  pendingConnectionInputs: new Map(),
53
+ pendingCredentialInputs: new Map(),
52
54
  pendingVoiceCasts: new Map(),
53
55
  pendingAvatarCastings: new Map(),
54
56
  isActive: () => active,
@@ -82,6 +84,8 @@ function recordingRelay() {
82
84
  flowInputResolved: () => Promise.resolve(),
83
85
  connectionInputRequest: () => Promise.resolve(true),
84
86
  connectionInputResolved: () => Promise.resolve(),
87
+ credentialInputRequest: () => Promise.resolve(true),
88
+ credentialInputResolved: () => Promise.resolve(),
85
89
  avatarCastingRequest: () => Promise.resolve(true),
86
90
  avatarCastingResolved: () => Promise.resolve(),
87
91
  assistantEvent: (target, event, model) => {
@@ -1443,6 +1447,24 @@ describe("BridgeJobExecutor", () => {
1443
1447
  expect(queryInputs[0]?.prompt).toBe("make the headline bigger");
1444
1448
  expect(queryInputs[0]?.reminder).toContain('landing "Homepage" (slug: homepage)');
1445
1449
  });
1450
+ it("explains a credential the message names beside it, together with the viewing reference", async () => {
1451
+ const queryInputs = [];
1452
+ const executor = createExecutor({
1453
+ queryInputs,
1454
+ queryResults: [scriptedMessages(resultMessage(0, "session_credential"))],
1455
+ });
1456
+ await executor.execute({
1457
+ threadId: "thread_credential",
1458
+ turnId: "turn_credential",
1459
+ prompt: "log in with user admin and password {{credential:password}}",
1460
+ history: history([]),
1461
+ currentReference: { type: "landing", slug: "homepage", title: "Homepage" },
1462
+ credentials: [{ name: "password", label: "Login password", allowedHosts: [] }],
1463
+ }, activeControls());
1464
+ expect(queryInputs[0]?.prompt).toBe("log in with user admin and password {{credential:password}}");
1465
+ expect(queryInputs[0]?.reminder).toContain('landing "Homepage"');
1466
+ expect(queryInputs[0]?.reminder).toContain("`{{credential:password}}` is a credential the user saved");
1467
+ });
1446
1468
  it("strips newlines from the viewing reference so a crafted title can't inject instructions", async () => {
1447
1469
  const queryInputs = [];
1448
1470
  const executor = createExecutor({
@@ -2184,6 +2206,36 @@ describe("BridgeJobExecutor", () => {
2184
2206
  updatedInput: { resolution: { toolsReady: false, failedToLoad: ["acme"] } },
2185
2207
  });
2186
2208
  });
2209
+ it("does not call an app ready when the reloaded tools do not include it", async () => {
2210
+ const relay = recordingRelay();
2211
+ relay.connectionInputRequest = () => Promise.resolve(true);
2212
+ const controls = activeControls();
2213
+ let decision;
2214
+ const executor = new BridgeJobExecutor({
2215
+ sdk: {
2216
+ query: (input) => ({
2217
+ async *[Symbol.asyncIterator]() {
2218
+ const asked = input.options.canUseTool?.(CONNECTION_INPUT_TOOL_NAME, { connections: [{ app: "notion", reason: "r" }] }, { toolUseID: "t1", requestId: "req_t1", signal: new AbortController().signal });
2219
+ await new Promise((resolve) => setTimeout(resolve, 10));
2220
+ for (const [, pending] of controls.pendingConnectionInputs) {
2221
+ pending.resolve({ results: [{ status: "connected", app: "notion", scope: "company" }] });
2222
+ }
2223
+ decision = await asked;
2224
+ yield resultMessage(0, "session_after_missing_app");
2225
+ },
2226
+ setMcpServers: () => Promise.resolve({ added: [], removed: [], errors: {} }),
2227
+ }),
2228
+ },
2229
+ model: "claude-test",
2230
+ sessionStore: { load: async () => undefined, save: async () => undefined, clear: async () => undefined },
2231
+ relay,
2232
+ mcpServersProvider: () => Promise.resolve({}),
2233
+ });
2234
+ await executor.execute({ threadId: "thread_app", turnId: "turn_app", prompt: "Connect Notion" }, controls);
2235
+ expect(decision).toMatchObject({
2236
+ updatedInput: { resolution: { toolsReady: false, failedToLoad: ["notion"] } },
2237
+ });
2238
+ });
2187
2239
  it("leaves the turn's tools alone when only a platform was connected", async () => {
2188
2240
  const relay = recordingRelay();
2189
2241
  relay.connectionInputRequest = () => Promise.resolve(true);
@@ -2214,6 +2266,94 @@ describe("BridgeJobExecutor", () => {
2214
2266
  await executor.execute({ threadId: "thread_pl", turnId: "turn_pl", prompt: "Connect HubSpot" }, controls);
2215
2267
  expect(reloads).toBe(0);
2216
2268
  });
2269
+ it("turns request_credential away in a Runtime without Credentials", async () => {
2270
+ const relay = recordingRelay();
2271
+ const requests = [];
2272
+ relay.credentialInputRequest = (_target, request) => {
2273
+ requests.push(request);
2274
+ return Promise.resolve(true);
2275
+ };
2276
+ let decision;
2277
+ const executor = new BridgeJobExecutor({
2278
+ sdk: {
2279
+ query: (input) => ({
2280
+ async *[Symbol.asyncIterator]() {
2281
+ decision = await input.options.canUseTool?.(CREDENTIAL_INPUT_TOOL_NAME, {
2282
+ label: "Shopify admin token",
2283
+ reason: "so I can update the theme",
2284
+ allowedHosts: ["acme.myshopify.com"],
2285
+ }, {
2286
+ toolUseID: "t1",
2287
+ requestId: "req_t1",
2288
+ signal: input.options.abortController?.signal ?? new AbortController().signal,
2289
+ });
2290
+ yield resultMessage(0, "session_credential_off");
2291
+ },
2292
+ }),
2293
+ },
2294
+ model: "claude-test",
2295
+ sessionStore: { load: async () => undefined, save: async () => undefined, clear: async () => undefined },
2296
+ relay,
2297
+ });
2298
+ await executor.execute({ threadId: "thread_co", turnId: "turn_co", prompt: "Fix my theme" }, activeControls());
2299
+ expect(requests).toEqual([]);
2300
+ expect(decision).toMatchObject({ behavior: "deny", message: expect.stringContaining("not available") });
2301
+ });
2302
+ it("parks a credential request and hands the agent only the placeholder the user's answer stored", async () => {
2303
+ vi.stubEnv("BAKER_CREDENTIALS", "1");
2304
+ const relay = recordingRelay();
2305
+ const requests = [];
2306
+ relay.credentialInputRequest = (_target, request) => {
2307
+ requests.push(request);
2308
+ return Promise.resolve(true);
2309
+ };
2310
+ const controls = activeControls();
2311
+ let decision;
2312
+ const executor = new BridgeJobExecutor({
2313
+ sdk: {
2314
+ query: (input) => ({
2315
+ async *[Symbol.asyncIterator]() {
2316
+ const asked = input.options.canUseTool?.(CREDENTIAL_INPUT_TOOL_NAME, {
2317
+ label: "Shopify admin token",
2318
+ reason: "so I can update the theme",
2319
+ allowedHosts: ["acme.myshopify.com"],
2320
+ }, {
2321
+ toolUseID: "t1",
2322
+ requestId: "req_t1",
2323
+ signal: input.options.abortController?.signal ?? new AbortController().signal,
2324
+ });
2325
+ await new Promise((resolve) => setTimeout(resolve, 10));
2326
+ for (const [, pending] of controls.pendingCredentialInputs) {
2327
+ pending.resolve({
2328
+ status: "stored",
2329
+ name: "shopify-admin-token",
2330
+ placeholder: "{{credential:shopify-admin-token}}",
2331
+ allowedHosts: ["acme.myshopify.com"],
2332
+ });
2333
+ }
2334
+ decision = await asked;
2335
+ yield resultMessage(0, "session_credential");
2336
+ },
2337
+ }),
2338
+ },
2339
+ model: "claude-test",
2340
+ sessionStore: { load: async () => undefined, save: async () => undefined, clear: async () => undefined },
2341
+ relay,
2342
+ });
2343
+ await executor.execute({ threadId: "thread_cr", turnId: "turn_cr", prompt: "Fix my theme" }, controls);
2344
+ expect(requests).toEqual([
2345
+ {
2346
+ toolUseId: "t1",
2347
+ label: "Shopify admin token",
2348
+ reason: "so I can update the theme",
2349
+ allowedHosts: ["acme.myshopify.com"],
2350
+ },
2351
+ ]);
2352
+ expect(decision).toMatchObject({
2353
+ behavior: "allow",
2354
+ updatedInput: { resolution: { status: "stored", placeholder: "{{credential:shopify-admin-token}}" } },
2355
+ });
2356
+ });
2217
2357
  it("refuses to ask for an avatar's face in a tool that renders only text", async () => {
2218
2358
  // Shipped three times, in both modes, after the agent had just read the doc saying
2219
2359
  // not to: "Elige el rostro para Clara, tu presentadora de placas solares" with
@@ -3626,7 +3766,9 @@ describe("BridgeJobExecutor review mode", () => {
3626
3766
  const options = queryInputs[0]?.options;
3627
3767
  expect(options.model).toBe("claude-session-model");
3628
3768
  expect(options.outputFormat).toBeUndefined();
3629
- expect(options.hooks).toBeUndefined();
3769
+ // Only the credential scrub, which every turn runs; none of the review's hooks.
3770
+ expect(Object.keys(options.hooks ?? {})).toEqual(["PostToolUse"]);
3771
+ expect(options.hooks?.PostToolUse).toHaveLength(1);
3630
3772
  expect(options.env?.MAX_STRUCTURED_OUTPUT_RETRIES).toBeUndefined();
3631
3773
  expect(options.disallowedTools).not.toContain("Write");
3632
3774
  expect(options.env?.BAKER_DEBUG_LOG).toBeUndefined();