@koda-sl/baker-bridge 0.148.0-dev.a87640a51 → 0.152.0-dev.1a19b1876
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 +34 -12
- package/dist/hono/agent.d.ts.map +1 -1
- package/dist/hono/agent.js +8 -1
- package/dist/hono/agent.js.map +1 -1
- package/dist/hono/bridge-job.d.ts +19 -2
- package/dist/hono/bridge-job.d.ts.map +1 -1
- package/dist/hono/bridge-job.js +25 -71
- package/dist/hono/bridge-job.js.map +1 -1
- package/dist/hono/bridge-job.test.js +140 -79
- package/dist/hono/bridge-job.test.js.map +1 -1
- package/dist/hono/cli-sync.d.ts +6 -0
- package/dist/hono/cli-sync.d.ts.map +1 -0
- package/dist/hono/cli-sync.js +108 -0
- package/dist/hono/cli-sync.js.map +1 -0
- package/dist/hono/cli-sync.test.d.ts +2 -0
- package/dist/hono/cli-sync.test.d.ts.map +1 -0
- package/dist/hono/cli-sync.test.js +122 -0
- package/dist/hono/cli-sync.test.js.map +1 -0
- package/dist/hono/main-sync.d.ts +20 -0
- package/dist/hono/main-sync.d.ts.map +1 -0
- package/dist/hono/main-sync.js +150 -0
- package/dist/hono/main-sync.js.map +1 -0
- package/dist/hono/main-sync.test.d.ts +2 -0
- package/dist/hono/main-sync.test.d.ts.map +1 -0
- package/dist/hono/main-sync.test.js +133 -0
- package/dist/hono/main-sync.test.js.map +1 -0
- package/dist/hono/turn-events.d.ts +1 -1
- package/dist/hono/turn-events.d.ts.map +1 -1
- package/dist/hono/turn-events.js.map +1 -1
- package/dist/hono/types.d.ts +6 -0
- package/dist/hono/types.d.ts.map +1 -1
- package/package.json +3 -1
|
@@ -241,6 +241,8 @@ function createExecutor(options) {
|
|
|
241
241
|
stalledCliExitMs: 0,
|
|
242
242
|
prepareAttachments: options.prepareAttachments,
|
|
243
243
|
...(options.inFlightTurns ? { inFlightTurns: options.inFlightTurns } : {}),
|
|
244
|
+
...(options.catchUpWithMain ? { catchUpWithMain: options.catchUpWithMain } : {}),
|
|
245
|
+
...(options.keepCliInStep ? { keepCliInStep: options.keepCliInStep } : {}),
|
|
244
246
|
});
|
|
245
247
|
}
|
|
246
248
|
function memoryInFlightTurns(initial) {
|
|
@@ -260,24 +262,6 @@ function memoryInFlightTurns(initial) {
|
|
|
260
262
|
},
|
|
261
263
|
};
|
|
262
264
|
}
|
|
263
|
-
// The CLI's own report of a failed model request, written as an assistant
|
|
264
|
-
// message — the shape production stores (`model: "<synthetic>"`, `error`).
|
|
265
|
-
function apiErrorMessage(uuid, text, error) {
|
|
266
|
-
return {
|
|
267
|
-
type: "assistant",
|
|
268
|
-
uuid,
|
|
269
|
-
session_id: "session_api_error",
|
|
270
|
-
parent_tool_use_id: null,
|
|
271
|
-
error,
|
|
272
|
-
is_api_error_message: true,
|
|
273
|
-
message: {
|
|
274
|
-
role: "assistant",
|
|
275
|
-
model: "<synthetic>",
|
|
276
|
-
content: [{ type: "text", text }],
|
|
277
|
-
stop_reason: "stop_sequence",
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
265
|
function resultMessage(costUsd, sessionId, modelUsage) {
|
|
282
266
|
return {
|
|
283
267
|
type: "result",
|
|
@@ -1345,6 +1329,38 @@ describe("BridgeJobExecutor", () => {
|
|
|
1345
1329
|
}, activeControls());
|
|
1346
1330
|
expect(queryInputs[0]?.prompt).toBe("/audit-google-ads-account 3865083115");
|
|
1347
1331
|
});
|
|
1332
|
+
it("keeps a skill command leading when catching up with main is what brought the skill in", async () => {
|
|
1333
|
+
const workspace = await mkdtemp(join(tmpdir(), "baker-job-skill-caught-up-"));
|
|
1334
|
+
await mkdir(join(workspace, ".claude", "skills", "landing"), { recursive: true });
|
|
1335
|
+
projectRootMock.value = workspace;
|
|
1336
|
+
const queryInputs = [];
|
|
1337
|
+
let told = 0;
|
|
1338
|
+
const executor = createExecutor({
|
|
1339
|
+
queryInputs,
|
|
1340
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_skill_caught_up"))],
|
|
1341
|
+
catchUpWithMain: async () => {
|
|
1342
|
+
await mkdir(join(workspace, ".claude", "skills", "audit-google-ads-account"), { recursive: true });
|
|
1343
|
+
return {
|
|
1344
|
+
inWorkspace: true,
|
|
1345
|
+
changed: [".claude/skills/audit-google-ads-account/SKILL.md"],
|
|
1346
|
+
told: () => {
|
|
1347
|
+
told += 1;
|
|
1348
|
+
return Promise.resolve();
|
|
1349
|
+
},
|
|
1350
|
+
};
|
|
1351
|
+
},
|
|
1352
|
+
});
|
|
1353
|
+
await executor.execute({
|
|
1354
|
+
threadId: "thread_skill_caught_up",
|
|
1355
|
+
turnId: "turn_skill_caught_up",
|
|
1356
|
+
prompt: "/audit-google-ads-account 3865083115",
|
|
1357
|
+
history: history([]),
|
|
1358
|
+
currentSender: { name: "Dana", userId: "user_dana" },
|
|
1359
|
+
}, activeControls());
|
|
1360
|
+
expect(queryInputs[0]?.prompt).toBe("/audit-google-ads-account 3865083115");
|
|
1361
|
+
expect(queryInputs[0]?.reminder).toBeUndefined();
|
|
1362
|
+
expect(told).toBe(0);
|
|
1363
|
+
});
|
|
1348
1364
|
it("passes per-turn MCP servers into Agent SDK query options with the acting user", async () => {
|
|
1349
1365
|
const queryInputs = [];
|
|
1350
1366
|
const calls = [];
|
|
@@ -1668,6 +1684,110 @@ describe("BridgeJobExecutor", () => {
|
|
|
1668
1684
|
expect(queryInputs[0]?.prompt).toBe("/publish");
|
|
1669
1685
|
expect(queryInputs[0]?.reminder).toBeUndefined();
|
|
1670
1686
|
});
|
|
1687
|
+
it("tells the agent which files another chat's publish just brought into its workspace", async () => {
|
|
1688
|
+
const queryInputs = [];
|
|
1689
|
+
let told = 0;
|
|
1690
|
+
const executor = createExecutor({
|
|
1691
|
+
queryInputs,
|
|
1692
|
+
catchUpWithMain: async () => ({
|
|
1693
|
+
inWorkspace: true,
|
|
1694
|
+
changed: ["src/pages/home/hero.md"],
|
|
1695
|
+
told: () => {
|
|
1696
|
+
told += 1;
|
|
1697
|
+
return Promise.resolve();
|
|
1698
|
+
},
|
|
1699
|
+
}),
|
|
1700
|
+
});
|
|
1701
|
+
await executor.execute({
|
|
1702
|
+
threadId: "thread_caught_up",
|
|
1703
|
+
turnId: "turn_caught_up",
|
|
1704
|
+
prompt: "Cambia el subtítulo del hero a «Sin entrada, paga en 24 meses».",
|
|
1705
|
+
history: history([
|
|
1706
|
+
{ role: "user", text: "¿Qué pone ahora el hero de la home?", createdAt: 1 },
|
|
1707
|
+
{
|
|
1708
|
+
role: "assistant",
|
|
1709
|
+
text: "El titular dice «Paneles solares para tu casa» y el subtítulo «Instala hoy y paga en 12 meses».",
|
|
1710
|
+
createdAt: 2,
|
|
1711
|
+
},
|
|
1712
|
+
]),
|
|
1713
|
+
}, activeControls());
|
|
1714
|
+
expect(queryInputs[0]?.prompt).toContain("Sin entrada, paga en 24 meses");
|
|
1715
|
+
expect(queryInputs[0]?.reminder).toContain("Read again any of them you work on");
|
|
1716
|
+
expect(queryInputs[0]?.reminder).toContain("- src/pages/home/hero.md");
|
|
1717
|
+
expect(told).toBe(1);
|
|
1718
|
+
});
|
|
1719
|
+
it("keeps the note for the next turn when the user stops this one while the workspace catches up", async () => {
|
|
1720
|
+
const queryInputs = [];
|
|
1721
|
+
const controls = activeControls();
|
|
1722
|
+
let told = 0;
|
|
1723
|
+
const executor = createExecutor({
|
|
1724
|
+
queryInputs,
|
|
1725
|
+
catchUpWithMain: () => {
|
|
1726
|
+
controls.deactivate();
|
|
1727
|
+
return Promise.resolve({
|
|
1728
|
+
inWorkspace: false,
|
|
1729
|
+
changed: ["src/pages/home/hero.md"],
|
|
1730
|
+
told: () => {
|
|
1731
|
+
told += 1;
|
|
1732
|
+
return Promise.resolve();
|
|
1733
|
+
},
|
|
1734
|
+
});
|
|
1735
|
+
},
|
|
1736
|
+
});
|
|
1737
|
+
const outcome = await executor.execute({ threadId: "thread_stopped", turnId: "turn_stopped", prompt: "Cambia el hero", history: history([]) }, controls);
|
|
1738
|
+
expect(outcome.kind).toBe("cancelled");
|
|
1739
|
+
expect(queryInputs).toHaveLength(0);
|
|
1740
|
+
expect(told).toBe(0);
|
|
1741
|
+
});
|
|
1742
|
+
it("leaves the workspace of a finished Session alone while it is reviewed", async () => {
|
|
1743
|
+
let caughtUp = false;
|
|
1744
|
+
const executor = createExecutor({
|
|
1745
|
+
catchUpWithMain: () => {
|
|
1746
|
+
caughtUp = true;
|
|
1747
|
+
return Promise.resolve(undefined);
|
|
1748
|
+
},
|
|
1749
|
+
});
|
|
1750
|
+
await executor.execute({
|
|
1751
|
+
threadId: "thread_review_sync",
|
|
1752
|
+
turnId: "turn_review_sync",
|
|
1753
|
+
prompt: "review",
|
|
1754
|
+
history: history([]),
|
|
1755
|
+
review: { threadIds: [], chatFacts: { chatId: "chat_1" }, outputRepair: true },
|
|
1756
|
+
}, activeControls());
|
|
1757
|
+
expect(caughtUp).toBe(false);
|
|
1758
|
+
});
|
|
1759
|
+
it("brings the CLI to the release Convex sent before the turn starts", async () => {
|
|
1760
|
+
const queryInputs = [];
|
|
1761
|
+
const requested = [];
|
|
1762
|
+
const executor = createExecutor({
|
|
1763
|
+
queryInputs,
|
|
1764
|
+
keepCliInStep: (wanted) => {
|
|
1765
|
+
requested.push({ wanted, turnStarted: queryInputs.length > 0 });
|
|
1766
|
+
return Promise.resolve();
|
|
1767
|
+
},
|
|
1768
|
+
});
|
|
1769
|
+
await executor.execute({ threadId: "thread_cli", turnId: "turn_cli", prompt: "Hola", cliVersion: "0.412.0", history: history([]) }, activeControls());
|
|
1770
|
+
expect(requested).toEqual([{ wanted: "0.412.0", turnStarted: false }]);
|
|
1771
|
+
expect(queryInputs).toHaveLength(1);
|
|
1772
|
+
});
|
|
1773
|
+
it("keeps the Session's CLI while it is reviewed", async () => {
|
|
1774
|
+
const wanted = [];
|
|
1775
|
+
const executor = createExecutor({
|
|
1776
|
+
keepCliInStep: (version) => {
|
|
1777
|
+
wanted.push(version);
|
|
1778
|
+
return Promise.resolve();
|
|
1779
|
+
},
|
|
1780
|
+
});
|
|
1781
|
+
await executor.execute({
|
|
1782
|
+
threadId: "thread_review_cli",
|
|
1783
|
+
turnId: "turn_review_cli",
|
|
1784
|
+
prompt: "review",
|
|
1785
|
+
cliVersion: "0.412.0",
|
|
1786
|
+
history: history([]),
|
|
1787
|
+
review: { threadIds: [], chatFacts: { chatId: "chat_1" }, outputRepair: true },
|
|
1788
|
+
}, activeControls());
|
|
1789
|
+
expect(wanted).toEqual([]);
|
|
1790
|
+
});
|
|
1671
1791
|
it("keeps the viewing-reference context beside the message when a stale session falls back to rehydration", async () => {
|
|
1672
1792
|
const queryInputs = [];
|
|
1673
1793
|
const executor = createExecutor({
|
|
@@ -2079,7 +2199,7 @@ describe("BridgeJobExecutor", () => {
|
|
|
2079
2199
|
expect(savedBoundaries).toEqual([]);
|
|
2080
2200
|
expect(clearCalls).toContainEqual({ threadId: "thread_result_error", reason: "result_error" });
|
|
2081
2201
|
});
|
|
2082
|
-
it("
|
|
2202
|
+
it("preserves provider status details when an SDK result error omits errors", async () => {
|
|
2083
2203
|
const clearCalls = [];
|
|
2084
2204
|
const executor = createExecutor({
|
|
2085
2205
|
clearCalls,
|
|
@@ -2105,69 +2225,10 @@ describe("BridgeJobExecutor", () => {
|
|
|
2105
2225
|
kind: "errored",
|
|
2106
2226
|
costUsd: 0.17,
|
|
2107
2227
|
model: "claude-test",
|
|
2108
|
-
errors: [
|
|
2228
|
+
errors: ["Claude Code returned an error result: API Error: 500 Internal Server Error; apiErrorStatus 500"],
|
|
2109
2229
|
});
|
|
2110
2230
|
expect(clearCalls).toContainEqual({ threadId: "thread_provider_result_error", reason: "result_error" });
|
|
2111
2231
|
});
|
|
2112
|
-
it("never shows the marketer a model provider's billing error, as a reply or as the turn's error", async () => {
|
|
2113
|
-
const providerError = "API Error: 402 Insufficient credits. Add more using https://openrouter.ai/settings/credits";
|
|
2114
|
-
const relay = recordingRelay();
|
|
2115
|
-
const executor = createExecutor({
|
|
2116
|
-
relay,
|
|
2117
|
-
queryResults: [
|
|
2118
|
-
scriptedMessages(apiErrorMessage("assistant_402", providerError, "unknown"), {
|
|
2119
|
-
type: "result",
|
|
2120
|
-
subtype: "success",
|
|
2121
|
-
is_error: true,
|
|
2122
|
-
result: providerError,
|
|
2123
|
-
api_error_status: 402,
|
|
2124
|
-
total_cost_usd: 0,
|
|
2125
|
-
session_id: "session_402",
|
|
2126
|
-
}),
|
|
2127
|
-
],
|
|
2128
|
-
});
|
|
2129
|
-
const outcome = await executor.execute({ threadId: "thread_402", turnId: "turn_402", prompt: "Build the creatives", history: history([]) }, activeControls());
|
|
2130
|
-
const shown = JSON.stringify([relay.assistantEvents, outcome.kind === "errored" ? outcome.errors : []]);
|
|
2131
|
-
expect(outcome.kind).toBe("errored");
|
|
2132
|
-
expect(shown).not.toMatch(/API Error|openrouter|402/i);
|
|
2133
|
-
});
|
|
2134
|
-
it("tells the marketer how to fix an image the provider could not read, not to send the same message again", async () => {
|
|
2135
|
-
const imageError = 'API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"Could not process image"}}';
|
|
2136
|
-
const executor = createExecutor({
|
|
2137
|
-
queryResults: [
|
|
2138
|
-
scriptedMessages({
|
|
2139
|
-
type: "result",
|
|
2140
|
-
subtype: "success",
|
|
2141
|
-
is_error: true,
|
|
2142
|
-
result: imageError,
|
|
2143
|
-
api_error_status: 400,
|
|
2144
|
-
total_cost_usd: 0,
|
|
2145
|
-
session_id: "session_bad_image",
|
|
2146
|
-
}),
|
|
2147
|
-
],
|
|
2148
|
-
});
|
|
2149
|
-
const outcome = await executor.execute({ threadId: "thread_bad_image", turnId: "turn_bad_image", prompt: "Use this photo", history: history([]) }, activeControls());
|
|
2150
|
-
expect(outcome.kind === "errored" ? outcome.errors : []).toEqual([
|
|
2151
|
-
expect.stringContaining("extension lies about its format"),
|
|
2152
|
-
]);
|
|
2153
|
-
});
|
|
2154
|
-
it("keeps a server error the turn recovered from out of the chat, and in the turn events", async () => {
|
|
2155
|
-
const providerError = "API Error: Server error mid-response. The response above may be incomplete.";
|
|
2156
|
-
const relay = recordingRelay();
|
|
2157
|
-
const turnEvents = [];
|
|
2158
|
-
const executor = createExecutor({
|
|
2159
|
-
relay,
|
|
2160
|
-
turnEvents,
|
|
2161
|
-
queryResults: [
|
|
2162
|
-
scriptedMessages(apiErrorMessage("assistant_server_error", providerError, "server_error"), assistantMessage("assistant_recovered", "Your three headlines are ready.", "session_recovered"), resultMessage(0.3, "session_recovered")),
|
|
2163
|
-
],
|
|
2164
|
-
});
|
|
2165
|
-
const outcome = await executor.execute({ threadId: "thread_recovered", turnId: "turn_recovered", prompt: "Write headlines", history: history([]) }, activeControls());
|
|
2166
|
-
expect(outcome.kind).toBe("completed");
|
|
2167
|
-
expect(JSON.stringify(relay.assistantEvents)).not.toContain("API Error");
|
|
2168
|
-
expect(JSON.stringify(relay.assistantEvents)).toContain("Your three headlines are ready.");
|
|
2169
|
-
expect(turnEvents).toContainEqual(expect.objectContaining({ event: "api_error", detail: { error: "server_error", text: providerError } }));
|
|
2170
|
-
});
|
|
2171
2232
|
it("closes SDK queries on error and inactive exits but not on natural clean completion", async () => {
|
|
2172
2233
|
const naturalCloseCalls = [];
|
|
2173
2234
|
const naturalExecutor = createExecutor({
|