@koda-sl/baker-bridge 0.154.0-dev.db7cf2f5d → 0.154.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 (67) hide show
  1. package/README.md +22 -2
  2. package/dist/hono/agent-session.d.ts +0 -1
  3. package/dist/hono/agent-session.d.ts.map +1 -1
  4. package/dist/hono/agent-session.js +0 -7
  5. package/dist/hono/agent-session.js.map +1 -1
  6. package/dist/hono/agent-session.test.js +1 -7
  7. package/dist/hono/agent-session.test.js.map +1 -1
  8. package/dist/hono/agent.d.ts.map +1 -1
  9. package/dist/hono/agent.js +7 -9
  10. package/dist/hono/agent.js.map +1 -1
  11. package/dist/hono/bridge-job.d.ts +15 -0
  12. package/dist/hono/bridge-job.d.ts.map +1 -1
  13. package/dist/hono/bridge-job.js +69 -5
  14. package/dist/hono/bridge-job.js.map +1 -1
  15. package/dist/hono/bridge-job.test.js +102 -0
  16. package/dist/hono/bridge-job.test.js.map +1 -1
  17. package/dist/hono/convex.d.ts +2 -1
  18. package/dist/hono/convex.d.ts.map +1 -1
  19. package/dist/hono/convex.js +4 -0
  20. package/dist/hono/convex.js.map +1 -1
  21. package/dist/hono/main-sync.d.ts +7 -0
  22. package/dist/hono/main-sync.d.ts.map +1 -1
  23. package/dist/hono/main-sync.js +39 -0
  24. package/dist/hono/main-sync.js.map +1 -1
  25. package/dist/hono/main-sync.test.js +29 -1
  26. package/dist/hono/main-sync.test.js.map +1 -1
  27. package/dist/hono/relay.d.ts +10 -1
  28. package/dist/hono/relay.d.ts.map +1 -1
  29. package/dist/hono/relay.js +4 -0
  30. package/dist/hono/relay.js.map +1 -1
  31. package/dist/hono/relay.test.js +11 -0
  32. package/dist/hono/relay.test.js.map +1 -1
  33. package/dist/hono/served-model.d.ts +23 -0
  34. package/dist/hono/served-model.d.ts.map +1 -0
  35. package/dist/hono/served-model.js +82 -0
  36. package/dist/hono/served-model.js.map +1 -0
  37. package/dist/hono/served-model.test.d.ts +2 -0
  38. package/dist/hono/served-model.test.d.ts.map +1 -0
  39. package/dist/hono/served-model.test.js +29 -0
  40. package/dist/hono/served-model.test.js.map +1 -0
  41. package/dist/hono/session-continuity.d.ts +24 -0
  42. package/dist/hono/session-continuity.d.ts.map +1 -1
  43. package/dist/hono/session-continuity.integration.test.js +201 -5
  44. package/dist/hono/session-continuity.integration.test.js.map +1 -1
  45. package/dist/hono/session-continuity.js +94 -7
  46. package/dist/hono/session-continuity.js.map +1 -1
  47. package/dist/hono/session-store.d.ts +2 -0
  48. package/dist/hono/session-store.d.ts.map +1 -1
  49. package/dist/hono/session-store.js +6 -0
  50. package/dist/hono/session-store.js.map +1 -1
  51. package/dist/hono/session-usage-baseline.d.ts +1 -0
  52. package/dist/hono/session-usage-baseline.d.ts.map +1 -1
  53. package/dist/hono/session-usage-baseline.js +17 -4
  54. package/dist/hono/session-usage-baseline.js.map +1 -1
  55. package/dist/hono/turn-events.d.ts +1 -1
  56. package/dist/hono/turn-events.d.ts.map +1 -1
  57. package/dist/hono/turn-events.js.map +1 -1
  58. package/dist/review/digest.d.ts +45 -1
  59. package/dist/review/digest.d.ts.map +1 -1
  60. package/dist/review/digest.js +394 -63
  61. package/dist/review/digest.js.map +1 -1
  62. package/dist/review/digest.test.js +239 -1
  63. package/dist/review/digest.test.js.map +1 -1
  64. package/dist/review/evidence.d.ts.map +1 -1
  65. package/dist/review/evidence.js +1 -1
  66. package/dist/review/evidence.js.map +1 -1
  67. package/package.json +1 -1
@@ -69,6 +69,7 @@ function recordingRelay() {
69
69
  const compactionUpdates = [];
70
70
  const reviewDigests = [];
71
71
  const retractedEvents = [];
72
+ const toolOutcomeBatches = [];
72
73
  const contextUsageUpdates = [];
73
74
  return {
74
75
  assistantEvents,
@@ -78,6 +79,7 @@ function recordingRelay() {
78
79
  contextUsageUpdates,
79
80
  reviewDigests,
80
81
  retractedEvents,
82
+ toolOutcomeBatches,
81
83
  startPreview: () => undefined,
82
84
  previewText: () => undefined,
83
85
  previewTools: () => undefined,
@@ -99,6 +101,10 @@ function recordingRelay() {
99
101
  retractedEvents.push({ target, eventIds });
100
102
  return Promise.resolve();
101
103
  },
104
+ toolOutcomes: (_target, outcomes) => {
105
+ toolOutcomeBatches.push(outcomes);
106
+ return Promise.resolve();
107
+ },
102
108
  complete: () => Promise.resolve(),
103
109
  inputRequest: () => Promise.resolve(true),
104
110
  inputResolved: () => Promise.resolve(),
@@ -243,6 +249,7 @@ function createExecutor(options) {
243
249
  ...(options.inFlightTurns ? { inFlightTurns: options.inFlightTurns } : {}),
244
250
  ...(options.catchUpWithMain ? { catchUpWithMain: options.catchUpWithMain } : {}),
245
251
  ...(options.keepCliInStep ? { keepCliInStep: options.keepCliInStep } : {}),
252
+ ...(options.holdsNoWork ? { holdsNoWork: options.holdsNoWork } : {}),
246
253
  });
247
254
  }
248
255
  function memoryInFlightTurns(initial) {
@@ -957,6 +964,40 @@ describe("BridgeJobExecutor", () => {
957
964
  ],
958
965
  });
959
966
  });
967
+ // OpenRouter answers under its own slug. A CLI resuming a transcript that names
968
+ // the slug drops every earlier thinking block from its first request, which
969
+ // breaks the prompt cache right after the system prompt (BAKER-331).
970
+ it("hands the CLI a transcript that names the model the conversation asked for", async () => {
971
+ const configDir = await mkdtemp(join(tmpdir(), "bridge-served-model-"));
972
+ try {
973
+ const transcriptPath = join(configDir, "projects", "-home-user-repo", "session_resume.jsonl");
974
+ await mkdir(join(configDir, "projects", "-home-user-repo"), { recursive: true });
975
+ await writeFile(transcriptPath, `${JSON.stringify({
976
+ type: "assistant",
977
+ requestedModel: "claude-opus-5-5",
978
+ message: { id: "msg_1", model: "anthropic/claude-opus-5.5", content: [{ type: "thinking", thinking: "t" }] },
979
+ })}\n`);
980
+ vi.stubEnv("CLAUDE_CONFIG_DIR", configDir);
981
+ const resumedFrom = [];
982
+ const executor = createExecutor({
983
+ loadedBoundary: cleanBoundary("session_resume"),
984
+ queryResults: [
985
+ {
986
+ async *[Symbol.asyncIterator]() {
987
+ await Promise.resolve();
988
+ resumedFrom.push(readFileSync(transcriptPath, "utf8"));
989
+ yield resultMessage(0.2, "session_next");
990
+ },
991
+ },
992
+ ],
993
+ });
994
+ await executor.execute({ threadId: "thread_resume", turnId: "turn_resume", prompt: "Continue", history: history([]) }, activeControls());
995
+ expect(JSON.parse(resumedFrom[0] ?? "{}").message.model).toBe("claude-opus-5-5");
996
+ }
997
+ finally {
998
+ await rm(configDir, { recursive: true, force: true });
999
+ }
1000
+ });
960
1001
  it("publishes without the conversation, and without summarizing it first", async () => {
961
1002
  const queryInputs = [];
962
1003
  const savedBoundaries = [];
@@ -1989,6 +2030,39 @@ describe("BridgeJobExecutor", () => {
1989
2030
  expect(clearCalls).toEqual([]);
1990
2031
  expect(relay.contextSources.map((entry) => entry.source)).toEqual(["interrupted_resume", "local_resume"]);
1991
2032
  });
2033
+ it("keeps the chat's conversation when an interrupted publish that cannot be resumed is run again", async () => {
2034
+ const executor = createExecutor({
2035
+ loadedBoundary: cleanBoundary("session_clean"),
2036
+ inFlightTurns: memoryInFlightTurns({
2037
+ version: 1,
2038
+ turnId: "turn_publish",
2039
+ sessionId: "session_never_written",
2040
+ updatedAtMs: 1,
2041
+ }),
2042
+ queryResults: [
2043
+ scriptedError(new Error("No conversation found with session ID: session_never_written")),
2044
+ scriptedMessages(assistantMessage("assistant_published", "Published", "session_publish_only"), resultMessage(0.2, "session_publish_only")),
2045
+ ],
2046
+ });
2047
+ const outcome = await executor.execute({ threadId: "thread_publish", turnId: "turn_publish", prompt: "/publish" }, activeControls());
2048
+ expect(outcome).toMatchObject({ kind: "completed", cleanBoundary: undefined });
2049
+ });
2050
+ it("tells Convex a turn that left the workspace with no work of its own", async () => {
2051
+ const executor = createExecutor({ holdsNoWork: async () => true });
2052
+ const outcome = await executor.execute({ threadId: "thread_run", turnId: "turn_run", prompt: "Post the daily report to Slack. Change nothing." }, activeControls());
2053
+ expect(outcome).toMatchObject({ kind: "completed", workspaceUnchanged: true });
2054
+ });
2055
+ it("claims nothing about the workspace after a publish turn or one that left work", async () => {
2056
+ const publish = createExecutor({
2057
+ holdsNoWork: async () => true,
2058
+ queryResults: [scriptedMessages(resultMessage(0.1, "session_publish_only"))],
2059
+ });
2060
+ const edited = createExecutor({ holdsNoWork: async () => false });
2061
+ const published = await publish.execute({ threadId: "thread_publish", turnId: "turn_publish", prompt: "/publish" }, activeControls());
2062
+ const worked = await edited.execute({ threadId: "thread_run", turnId: "turn_run", prompt: "Rewrite the hero" }, activeControls());
2063
+ expect(published).not.toHaveProperty("workspaceUnchanged");
2064
+ expect(worked).not.toHaveProperty("workspaceUnchanged");
2065
+ });
1992
2066
  it("keeps the running turn's session on record until the turn ends", async () => {
1993
2067
  const recorded = [];
1994
2068
  const inFlightTurns = memoryInFlightTurns();
@@ -4641,6 +4715,34 @@ describe("BridgeJobExecutor model refusals", () => {
4641
4715
  errors: ["I couldn't help with that request. Try rephrasing what you need."],
4642
4716
  });
4643
4717
  });
4718
+ // Tool results come back in user events, which are not stored as messages, so
4719
+ // their outcome is relayed on its own: a Session rebuilt from history needs it.
4720
+ it("relays how each tool call came out, bounded", async () => {
4721
+ const relay = recordingRelay();
4722
+ const toolResult = {
4723
+ type: "user",
4724
+ message: {
4725
+ role: "user",
4726
+ content: [
4727
+ { type: "tool_result", tool_use_id: "toolu_1", is_error: true, content: "Meta refused the rule" },
4728
+ { type: "tool_result", tool_use_id: "toolu_2", content: [{ type: "text", text: "x".repeat(5000) }] },
4729
+ ],
4730
+ },
4731
+ parent_tool_use_id: null,
4732
+ session_id: "session_tools",
4733
+ };
4734
+ const executor = createExecutor({
4735
+ relay,
4736
+ queryResults: [scriptedMessages(toolResult, resultMessage(0.01, "session_tools"))],
4737
+ });
4738
+ await executor.execute({ threadId: "thread_tools", turnId: "turn_tools", prompt: "Hi", history: history([]) }, activeControls());
4739
+ expect(relay.toolOutcomeBatches).toEqual([
4740
+ [
4741
+ { toolUseId: "toolu_1", isError: true, text: "Meta refused the rule" },
4742
+ { toolUseId: "toolu_2", isError: false, text: "x".repeat(2000) },
4743
+ ],
4744
+ ]);
4745
+ });
4644
4746
  // A fallback model retried the refused request. What the refused leg already
4645
4747
  // posted is superseded, so it leaves the chat instead of sitting above the
4646
4748
  // answer that replaced it.