@koda-sl/baker-bridge 0.165.0 → 0.171.0-dev.289ff1b9d
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 +49 -21
- package/dist/hono/agent.d.ts.map +1 -1
- package/dist/hono/agent.js +3 -1
- package/dist/hono/agent.js.map +1 -1
- package/dist/hono/agent.test.js +2 -0
- package/dist/hono/agent.test.js.map +1 -1
- package/dist/hono/bridge-job.d.ts +23 -0
- package/dist/hono/bridge-job.d.ts.map +1 -1
- package/dist/hono/bridge-job.js +160 -25
- package/dist/hono/bridge-job.js.map +1 -1
- package/dist/hono/bridge-job.test.js +233 -13
- package/dist/hono/bridge-job.test.js.map +1 -1
- package/dist/hono/model-capabilities.d.ts +1 -0
- package/dist/hono/model-capabilities.d.ts.map +1 -1
- package/dist/hono/model-capabilities.js +4 -0
- package/dist/hono/model-capabilities.js.map +1 -1
- package/dist/hono/openrouter-session.d.ts +19 -13
- package/dist/hono/openrouter-session.d.ts.map +1 -1
- package/dist/hono/openrouter-session.js +19 -13
- package/dist/hono/openrouter-session.js.map +1 -1
- package/dist/hono/relay.d.ts +8 -0
- package/dist/hono/relay.d.ts.map +1 -1
- package/dist/hono/relay.js +2 -0
- package/dist/hono/relay.js.map +1 -1
- package/dist/hono/relay.test.js +30 -0
- package/dist/hono/relay.test.js.map +1 -1
- package/dist/hono/runtime-files.d.ts +1 -0
- package/dist/hono/runtime-files.d.ts.map +1 -1
- package/dist/hono/runtime-files.js +3 -0
- package/dist/hono/runtime-files.js.map +1 -1
- package/dist/hono/served-model.d.ts +8 -0
- package/dist/hono/served-model.d.ts.map +1 -1
- package/dist/hono/served-model.js +41 -0
- package/dist/hono/served-model.js.map +1 -1
- package/dist/hono/served-model.test.js +19 -1
- package/dist/hono/served-model.test.js.map +1 -1
- package/dist/hono/session-continuity.integration.test.js +2 -0
- package/dist/hono/session-continuity.integration.test.js.map +1 -1
- package/dist/hono/tool-result-ledger.d.ts +32 -0
- package/dist/hono/tool-result-ledger.d.ts.map +1 -0
- package/dist/hono/tool-result-ledger.js +67 -0
- package/dist/hono/tool-result-ledger.js.map +1 -0
- package/dist/hono/tool-result-ledger.test.d.ts +2 -0
- package/dist/hono/tool-result-ledger.test.d.ts.map +1 -0
- package/dist/hono/tool-result-ledger.test.js +61 -0
- package/dist/hono/tool-result-ledger.test.js.map +1 -0
- package/dist/hono/turn-route.d.ts +71 -0
- package/dist/hono/turn-route.d.ts.map +1 -0
- package/dist/hono/turn-route.js +112 -0
- package/dist/hono/turn-route.js.map +1 -0
- package/dist/hono/turn-route.test.d.ts +2 -0
- package/dist/hono/turn-route.test.d.ts.map +1 -0
- package/dist/hono/turn-route.test.js +134 -0
- package/dist/hono/turn-route.test.js.map +1 -0
- package/dist/hono/workspace-instructions.d.ts +43 -0
- package/dist/hono/workspace-instructions.d.ts.map +1 -0
- package/dist/hono/workspace-instructions.js +68 -0
- package/dist/hono/workspace-instructions.js.map +1 -0
- package/dist/hono/workspace-instructions.test.d.ts +2 -0
- package/dist/hono/workspace-instructions.test.d.ts.map +1 -0
- package/dist/hono/workspace-instructions.test.js +68 -0
- package/dist/hono/workspace-instructions.test.js.map +1 -0
- package/dist/review/digest.d.ts +13 -0
- package/dist/review/digest.d.ts.map +1 -1
- package/dist/review/digest.js +1 -1
- package/dist/review/digest.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { mkdir, mkdtemp, rm, utimes, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, utimes, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
@@ -9,11 +9,19 @@ vi.mock("./env.ts", () => ({
|
|
|
9
9
|
// The skills allowlist is read off the workspace, so the project root has to be
|
|
10
10
|
// steerable to a fixture directory. Defaults to the real root for every other test.
|
|
11
11
|
const projectRootMock = vi.hoisted(() => ({ value: process.cwd() }));
|
|
12
|
+
// Where the per-company instructions import is written; kept out of the real ~/.baker.
|
|
13
|
+
const instructionsDirMock = vi.hoisted(() => ({
|
|
14
|
+
value: `${process.env.TMPDIR ?? "/tmp"}/baker-job-instructions-${process.pid}`,
|
|
15
|
+
}));
|
|
12
16
|
vi.mock("./runtime-files.ts", async (importOriginal) => {
|
|
13
17
|
const actual = await importOriginal();
|
|
14
18
|
return {
|
|
15
19
|
...actual,
|
|
16
|
-
bridgeRuntimeFiles: {
|
|
20
|
+
bridgeRuntimeFiles: {
|
|
21
|
+
...actual.bridgeRuntimeFiles,
|
|
22
|
+
projectRoot: () => projectRootMock.value,
|
|
23
|
+
instructionsDir: () => instructionsDirMock.value,
|
|
24
|
+
},
|
|
17
25
|
};
|
|
18
26
|
});
|
|
19
27
|
import { AVATAR_CASTING_TOOL_NAME } from "./avatar-casting.js";
|
|
@@ -254,6 +262,9 @@ function createExecutor(options) {
|
|
|
254
262
|
...(options.catchUpWithMain ? { catchUpWithMain: options.catchUpWithMain } : {}),
|
|
255
263
|
...(options.keepCliInStep ? { keepCliInStep: options.keepCliInStep } : {}),
|
|
256
264
|
...(options.holdsNoWork ? { holdsNoWork: options.holdsNoWork } : {}),
|
|
265
|
+
// Never the network: a turn nobody judges runs on the default model and effort.
|
|
266
|
+
routeJudge: options.routeJudge ?? (async () => null),
|
|
267
|
+
...(options.routeModels ? { routeModels: true } : {}),
|
|
257
268
|
});
|
|
258
269
|
}
|
|
259
270
|
function memoryInFlightTurns(initial) {
|
|
@@ -1006,6 +1017,39 @@ describe("BridgeJobExecutor", () => {
|
|
|
1006
1017
|
],
|
|
1007
1018
|
});
|
|
1008
1019
|
});
|
|
1020
|
+
// Tool results are most of what a turn writes to the prompt cache, and nothing
|
|
1021
|
+
// recorded their size: the turn now reports it next to its model usage.
|
|
1022
|
+
it("reports how much each tool's results put in the context this turn", async () => {
|
|
1023
|
+
const executor = createExecutor({
|
|
1024
|
+
queryResults: [
|
|
1025
|
+
scriptedMessages({
|
|
1026
|
+
type: "assistant",
|
|
1027
|
+
parent_tool_use_id: null,
|
|
1028
|
+
uuid: "assistant_tool",
|
|
1029
|
+
session_id: "session_tools_size",
|
|
1030
|
+
message: {
|
|
1031
|
+
id: "msg_tool",
|
|
1032
|
+
role: "assistant",
|
|
1033
|
+
content: [{ type: "tool_use", id: "tool_1", name: "Bash", input: { command: "baker ads meta ads" } }],
|
|
1034
|
+
},
|
|
1035
|
+
}, {
|
|
1036
|
+
type: "user",
|
|
1037
|
+
parent_tool_use_id: null,
|
|
1038
|
+
uuid: "user_tool_result",
|
|
1039
|
+
session_id: "session_tools_size",
|
|
1040
|
+
message: {
|
|
1041
|
+
role: "user",
|
|
1042
|
+
content: [{ type: "tool_result", tool_use_id: "tool_1", content: "r".repeat(800) }],
|
|
1043
|
+
},
|
|
1044
|
+
}, resultMessage(0.1, "session_tools_size")),
|
|
1045
|
+
],
|
|
1046
|
+
});
|
|
1047
|
+
const outcome = await executor.execute({ threadId: "thread_tools_size", turnId: "turn_tools_size", prompt: "How are the ads?", history: history([]) }, activeControls());
|
|
1048
|
+
expect(outcome).toMatchObject({
|
|
1049
|
+
kind: "completed",
|
|
1050
|
+
toolResults: [{ tool: "baker ads meta", agent: "main", calls: 1, bytes: 800, tokens: 200 }],
|
|
1051
|
+
});
|
|
1052
|
+
});
|
|
1009
1053
|
// OpenRouter answers under its own slug. A CLI resuming a transcript that names
|
|
1010
1054
|
// the slug drops every earlier thinking block from its first request, which
|
|
1011
1055
|
// breaks the prompt cache right after the system prompt (BAKER-331).
|
|
@@ -1288,16 +1332,19 @@ describe("BridgeJobExecutor", () => {
|
|
|
1288
1332
|
"x-session-id: thread_env",
|
|
1289
1333
|
].join("\n"));
|
|
1290
1334
|
});
|
|
1291
|
-
//
|
|
1292
|
-
//
|
|
1293
|
-
|
|
1335
|
+
// With the capability on, the CLI restates the effort in its mid-conversation
|
|
1336
|
+
// system message, so a turn on another effort level keeps the conversation's
|
|
1337
|
+
// prompt cache. Without it, a changed top-level effort re-writes everything
|
|
1338
|
+
// after the first tool exchange. Pinned on rather than left to the CLI's own
|
|
1339
|
+
// default, which a remote feature gate can switch off.
|
|
1340
|
+
it("turns on per-turn effort, so a turn on another effort level keeps the prompt cache", async () => {
|
|
1294
1341
|
const queryInputs = [];
|
|
1295
1342
|
const executor = createExecutor({
|
|
1296
1343
|
queryInputs,
|
|
1297
1344
|
queryResults: [scriptedMessages(resultMessage(0, "session_bedrock"))],
|
|
1298
1345
|
});
|
|
1299
1346
|
await executor.execute({ threadId: "thread_bedrock", turnId: "turn_bedrock", prompt: "Build a landing", history: history([]) }, activeControls());
|
|
1300
|
-
expect(queryInputs[0]?.options.env).toMatchObject({ CLAUDE_CODE_MODEL_CAPABILITIES: "
|
|
1347
|
+
expect(queryInputs[0]?.options.env).toMatchObject({ CLAUDE_CODE_MODEL_CAPABILITIES: "per_turn_effort" });
|
|
1301
1348
|
});
|
|
1302
1349
|
// The CLI names each session with a structured-output request OpenRouter's Bedrock
|
|
1303
1350
|
// cannot serve, so it went to Azure on OpenRouter credits; nothing reads the name.
|
|
@@ -1408,6 +1455,62 @@ describe("BridgeJobExecutor", () => {
|
|
|
1408
1455
|
expect(disallowed).not.toContain("Task");
|
|
1409
1456
|
expect(disallowed).not.toContain("SendMessage");
|
|
1410
1457
|
});
|
|
1458
|
+
// Developer tools a marketer's chat never calls (0 calls across 358
|
|
1459
|
+
// production chats, Oct 2026). Each definition rides every request a session
|
|
1460
|
+
// makes — DesignSync's alone is ~9k characters — whenever the CLI does not
|
|
1461
|
+
// defer it.
|
|
1462
|
+
it("removes the developer tools a marketer's chat never calls", async () => {
|
|
1463
|
+
const queryInputs = [];
|
|
1464
|
+
const executor = createExecutor({
|
|
1465
|
+
queryInputs,
|
|
1466
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_dev_tools"))],
|
|
1467
|
+
});
|
|
1468
|
+
await executor.execute({ threadId: "thread_dev_tools", turnId: "turn_dev_tools", prompt: "Build a landing", history: history([]) }, activeControls());
|
|
1469
|
+
const disallowed = queryInputs[0]?.options.disallowedTools ?? [];
|
|
1470
|
+
expect(disallowed).toEqual(expect.arrayContaining(["DesignSync", "ReportFindings", "NotebookEdit"]));
|
|
1471
|
+
});
|
|
1472
|
+
// The scaffold's CLAUDE.md is the same text for every company on a scaffold
|
|
1473
|
+
// version, but loaded as a memory file it lands in the first user message with
|
|
1474
|
+
// no cache breakpoint after it, so every chat wrote its ~24k tokens cold. In the
|
|
1475
|
+
// system prompt it ends on a breakpoint and one chat reads another's copy.
|
|
1476
|
+
it("sends the workspace's shared instructions as the system prompt and loads COMPANY.md on its own", async () => {
|
|
1477
|
+
const workspace = await mkdtemp(join(tmpdir(), "baker-job-instructions-"));
|
|
1478
|
+
await writeFile(join(workspace, "CLAUDE.md"), "# Project Instructions\n\n## Client Context\n\nOne client per repo. Everything known about them:\n\n@COMPANY.md\n\n## Rules\n\nStage, don't ask.\n");
|
|
1479
|
+
projectRootMock.value = workspace;
|
|
1480
|
+
const queryInputs = [];
|
|
1481
|
+
const executor = createExecutor({
|
|
1482
|
+
queryInputs,
|
|
1483
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_instructions"))],
|
|
1484
|
+
});
|
|
1485
|
+
await executor.execute({ threadId: "thread_instructions", turnId: "turn_instructions", prompt: "Build a landing", history: history([]) }, activeControls());
|
|
1486
|
+
const options = queryInputs[0]?.options;
|
|
1487
|
+
expect(options.systemPrompt).toEqual({
|
|
1488
|
+
type: "preset",
|
|
1489
|
+
preset: "claude_code",
|
|
1490
|
+
append: "# Project Instructions\n\n## Rules\n\nStage, don't ask.\n",
|
|
1491
|
+
snapshot: true,
|
|
1492
|
+
});
|
|
1493
|
+
expect(options.settings).toMatchObject({ claudeMdExcludes: [join(workspace, "CLAUDE.md")] });
|
|
1494
|
+
expect(options.additionalDirectories).toEqual([instructionsDirMock.value]);
|
|
1495
|
+
expect(options.env?.CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD).toBe("1");
|
|
1496
|
+
expect(await readFile(join(instructionsDirMock.value, "CLAUDE.md"), "utf8")).toBe(`@${join(workspace, "COMPANY.md")}\n`);
|
|
1497
|
+
});
|
|
1498
|
+
it("lets the CLI load a CLAUDE.md it cannot split, exactly as before", async () => {
|
|
1499
|
+
const workspace = await mkdtemp(join(tmpdir(), "baker-job-instructions-old-"));
|
|
1500
|
+
await writeFile(join(workspace, "CLAUDE.md"), "# Project Instructions\n\nNo company import.\n");
|
|
1501
|
+
projectRootMock.value = workspace;
|
|
1502
|
+
const queryInputs = [];
|
|
1503
|
+
const executor = createExecutor({
|
|
1504
|
+
queryInputs,
|
|
1505
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_instructions_old"))],
|
|
1506
|
+
});
|
|
1507
|
+
await executor.execute({ threadId: "thread_instructions_old", turnId: "turn_instructions_old", prompt: "Hi", history: history([]) }, activeControls());
|
|
1508
|
+
const options = queryInputs[0]?.options;
|
|
1509
|
+
expect(options.systemPrompt).toBeUndefined();
|
|
1510
|
+
expect(options.additionalDirectories).toBeUndefined();
|
|
1511
|
+
expect(options.settings ?? {}).not.toHaveProperty("claudeMdExcludes");
|
|
1512
|
+
expect(options.env?.CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD).toBeUndefined();
|
|
1513
|
+
});
|
|
1411
1514
|
it("allowlists the workspace's own skills so the CLI's built-in developer skills stay out of the session", async () => {
|
|
1412
1515
|
const workspace = await mkdtemp(join(tmpdir(), "baker-job-skills-"));
|
|
1413
1516
|
for (const skill of ["landing", "brand-build", "docx"]) {
|
|
@@ -1586,11 +1689,10 @@ describe("BridgeJobExecutor", () => {
|
|
|
1586
1689
|
await executor.execute({ threadId: "thread_nobeta", turnId: "turn_nobeta", prompt: "Hi", history: history([]) }, activeControls());
|
|
1587
1690
|
expect(queryInputs[0]?.options).not.toHaveProperty("betas");
|
|
1588
1691
|
});
|
|
1589
|
-
// Effort governs thinking depth, preamble and tool-call count
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1592
|
-
|
|
1593
|
-
it("sends a single constant effort so the prompt cache is never invalidated by it", async () => {
|
|
1692
|
+
// Effort governs thinking depth, preamble and tool-call count. A turn nobody
|
|
1693
|
+
// could judge runs at Opus 5.5's own default, set explicitly so an SDK or
|
|
1694
|
+
// model default change cannot move it.
|
|
1695
|
+
it("runs a turn Jev could not judge at medium effort", async () => {
|
|
1594
1696
|
const queryInputs = [];
|
|
1595
1697
|
const executor = createExecutor({
|
|
1596
1698
|
queryInputs,
|
|
@@ -1603,8 +1705,105 @@ describe("BridgeJobExecutor", () => {
|
|
|
1603
1705
|
await executor.execute({ threadId: "thread_effort", turnId: "turn_effort_1", prompt: "Hi", history: history([]) }, activeControls());
|
|
1604
1706
|
await executor.execute({ threadId: "thread_effort", turnId: "turn_effort_2", prompt: "Again", history: history([]) }, activeControls());
|
|
1605
1707
|
expect(queryInputs[0]?.options.effort).toBe("medium");
|
|
1606
|
-
|
|
1607
|
-
|
|
1708
|
+
expect(queryInputs[1]?.options.effort).toBe("medium");
|
|
1709
|
+
});
|
|
1710
|
+
it("runs a turn Jev clearly reads as small at low effort, and reports why", async () => {
|
|
1711
|
+
const queryInputs = [];
|
|
1712
|
+
const asked = [];
|
|
1713
|
+
const executor = createExecutor({
|
|
1714
|
+
queryInputs,
|
|
1715
|
+
model: "claude-opus-5-5",
|
|
1716
|
+
// small, complex, read-only, deliverable
|
|
1717
|
+
routeJudge: (questions) => {
|
|
1718
|
+
asked.push(...questions);
|
|
1719
|
+
return Promise.resolve([0.92, 0.05, 0.6, 0.1]);
|
|
1720
|
+
},
|
|
1721
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_effort_low"))],
|
|
1722
|
+
});
|
|
1723
|
+
const outcome = await executor.execute({
|
|
1724
|
+
threadId: "thread_effort_low",
|
|
1725
|
+
turnId: "turn_effort_low",
|
|
1726
|
+
prompt: "¿Cuál fue el CPL de ayer?",
|
|
1727
|
+
history: history([
|
|
1728
|
+
{ role: "user", text: "Revisa la campaña de verano", createdAt: 1 },
|
|
1729
|
+
{ role: "assistant", text: "Listo: la campaña de verano va bien.", createdAt: 2 },
|
|
1730
|
+
]),
|
|
1731
|
+
}, activeControls());
|
|
1732
|
+
expect(queryInputs[0]?.options.effort).toBe("low");
|
|
1733
|
+
expect(asked[0]).toMatchObject({
|
|
1734
|
+
instructions: { message: "¿Cuál fue el CPL de ayer?", previous_reply: "Listo: la campaña de verano va bien." },
|
|
1735
|
+
});
|
|
1736
|
+
expect(outcome).toMatchObject({
|
|
1737
|
+
kind: "completed",
|
|
1738
|
+
turnRoute: { model: "claude-opus-5-5", modelReason: "fixed", effort: "low", reason: "small" },
|
|
1739
|
+
});
|
|
1740
|
+
});
|
|
1741
|
+
it("starts a chat that only reads on Sonnet, thinking harder, when the Runtime routes models", async () => {
|
|
1742
|
+
const queryInputs = [];
|
|
1743
|
+
const executor = createExecutor({
|
|
1744
|
+
queryInputs,
|
|
1745
|
+
model: "claude-opus-5-5",
|
|
1746
|
+
routeModels: true,
|
|
1747
|
+
routeJudge: () => Promise.resolve([0.2, 0.9, 0.85, 0.1]),
|
|
1748
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_reading"))],
|
|
1749
|
+
});
|
|
1750
|
+
const outcome = await executor.execute({ threadId: "thread_reading", turnId: "turn_reading", prompt: "¿Cómo van los anuncios?", history: history([]) }, activeControls());
|
|
1751
|
+
expect(queryInputs[0]?.options).toMatchObject({ model: "claude-sonnet-5-5", effort: "high" });
|
|
1752
|
+
expect(outcome).toMatchObject({
|
|
1753
|
+
model: "claude-sonnet-5-5",
|
|
1754
|
+
turnRoute: { model: "claude-sonnet-5-5", modelReason: "read_only", effort: "high" },
|
|
1755
|
+
});
|
|
1756
|
+
});
|
|
1757
|
+
it("moves a chat that ran on Sonnet to Opus once a turn asks for finished work", async () => {
|
|
1758
|
+
const configDir = await mkdtemp(join(tmpdir(), "bridge-turn-route-"));
|
|
1759
|
+
try {
|
|
1760
|
+
await mkdir(join(configDir, "projects", "-home-user-repo"), { recursive: true });
|
|
1761
|
+
await writeFile(join(configDir, "projects", "-home-user-repo", "session_sonnet.jsonl"), `${JSON.stringify({ type: "assistant", requestedModel: "claude-sonnet-5-5", message: { id: "m", model: "claude-sonnet-5-5", content: [] } })}\n`);
|
|
1762
|
+
vi.stubEnv("CLAUDE_CONFIG_DIR", configDir);
|
|
1763
|
+
const queryInputs = [];
|
|
1764
|
+
const executor = createExecutor({
|
|
1765
|
+
queryInputs,
|
|
1766
|
+
model: "claude-opus-5-5",
|
|
1767
|
+
routeModels: true,
|
|
1768
|
+
loadedBoundary: cleanBoundary("session_sonnet"),
|
|
1769
|
+
routeJudge: () => Promise.resolve([0.1, 0.95, 0.2, 0.9]),
|
|
1770
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_sonnet"))],
|
|
1771
|
+
});
|
|
1772
|
+
await executor.execute({
|
|
1773
|
+
threadId: "thread_upgrade",
|
|
1774
|
+
turnId: "turn_upgrade",
|
|
1775
|
+
prompt: "Perfecto, créame una landing con esto",
|
|
1776
|
+
history: history([
|
|
1777
|
+
{ role: "user", text: "¿Cómo van los anuncios?", createdAt: 1 },
|
|
1778
|
+
{ role: "assistant", text: "Van bien: el CPL bajó un 12%.", createdAt: 2 },
|
|
1779
|
+
]),
|
|
1780
|
+
}, activeControls());
|
|
1781
|
+
expect(queryInputs[0]?.options).toMatchObject({ model: "claude-opus-5-5", effort: "medium" });
|
|
1782
|
+
}
|
|
1783
|
+
finally {
|
|
1784
|
+
await rm(configDir, { recursive: true, force: true });
|
|
1785
|
+
}
|
|
1786
|
+
});
|
|
1787
|
+
it("keeps a plan-mode turn at medium without asking Jev", async () => {
|
|
1788
|
+
const queryInputs = [];
|
|
1789
|
+
let asked = false;
|
|
1790
|
+
const executor = createExecutor({
|
|
1791
|
+
queryInputs,
|
|
1792
|
+
routeJudge: () => {
|
|
1793
|
+
asked = true;
|
|
1794
|
+
return Promise.resolve([0.99, 0, 1, 0]);
|
|
1795
|
+
},
|
|
1796
|
+
queryResults: [scriptedMessages(resultMessage(0, "session_effort_plan"))],
|
|
1797
|
+
});
|
|
1798
|
+
await executor.execute({
|
|
1799
|
+
threadId: "thread_effort_plan",
|
|
1800
|
+
turnId: "turn_effort_plan",
|
|
1801
|
+
prompt: "Plan Q4",
|
|
1802
|
+
planMode: true,
|
|
1803
|
+
history: history([]),
|
|
1804
|
+
}, activeControls());
|
|
1805
|
+
expect(queryInputs[0]?.options.effort).toBe("medium");
|
|
1806
|
+
expect(asked).toBe(false);
|
|
1608
1807
|
});
|
|
1609
1808
|
it("pins the auto-compact window to the context window it resolved for the model", async () => {
|
|
1610
1809
|
const queryInputs = [];
|
|
@@ -3891,6 +4090,8 @@ describe("BridgeJobExecutor stall recovery", () => {
|
|
|
3891
4090
|
kind: "completed",
|
|
3892
4091
|
costUsd: 0.05,
|
|
3893
4092
|
model: "claude-test",
|
|
4093
|
+
// The tool result the stalled query had already received still counts.
|
|
4094
|
+
toolResults: [expect.objectContaining({ calls: 1 })],
|
|
3894
4095
|
cleanBoundary: expect.objectContaining({ sessionId: "session_a" }),
|
|
3895
4096
|
});
|
|
3896
4097
|
expect(queryInputs).toHaveLength(2);
|
|
@@ -4629,6 +4830,9 @@ describe("BridgeJobExecutor turn end with background tasks", () => {
|
|
|
4629
4830
|
});
|
|
4630
4831
|
});
|
|
4631
4832
|
describe("BridgeJobExecutor review mode", () => {
|
|
4833
|
+
afterEach(() => {
|
|
4834
|
+
projectRootMock.value = process.cwd();
|
|
4835
|
+
});
|
|
4632
4836
|
const review = {
|
|
4633
4837
|
reviewId: "rev_1",
|
|
4634
4838
|
apiKey: "bk_review_key",
|
|
@@ -4686,6 +4890,21 @@ describe("BridgeJobExecutor review mode", () => {
|
|
|
4686
4890
|
await executor.execute(reviewJob(), activeControls());
|
|
4687
4891
|
expect(queryInputs[0]?.options.settings).toMatchObject({ permissions: { ask: ["Bash"] } });
|
|
4688
4892
|
});
|
|
4893
|
+
// The brief has the reviewer read the rulebook as the Session had it
|
|
4894
|
+
// (`git show <sha>:CLAUDE.md`); the working tree's copy may already be newer.
|
|
4895
|
+
// Auto-loading it put ~24k tokens of the marketer's instructions in front of
|
|
4896
|
+
// every review request, written cold and re-read on each one.
|
|
4897
|
+
it("does not load the workspace's CLAUDE.md into the reviewer, as a memory file or as the system prompt", async () => {
|
|
4898
|
+
const workspace = await mkdtemp(join(tmpdir(), "baker-review-instructions-"));
|
|
4899
|
+
await writeFile(join(workspace, "CLAUDE.md"), "# Project Instructions\n\n## Client Context\n\nOne client per repo. Everything known about them:\n\n@COMPANY.md\n\n## Rules\n");
|
|
4900
|
+
projectRootMock.value = workspace;
|
|
4901
|
+
const queryInputs = [];
|
|
4902
|
+
const executor = createExecutor({ queryInputs });
|
|
4903
|
+
await executor.execute(reviewJob(), activeControls());
|
|
4904
|
+
expect(queryInputs[0]?.options.settings).toMatchObject({ claudeMdExcludes: ["**/CLAUDE.md"] });
|
|
4905
|
+
expect(queryInputs[0]?.options.systemPrompt).toBeUndefined();
|
|
4906
|
+
expect(queryInputs[0]?.options.additionalDirectories).toBeUndefined();
|
|
4907
|
+
});
|
|
4689
4908
|
it("removes every editing tool on top of the list an ordinary turn is denied", async () => {
|
|
4690
4909
|
const queryInputs = [];
|
|
4691
4910
|
const executor = createExecutor({ queryInputs });
|
|
@@ -4749,6 +4968,7 @@ describe("BridgeJobExecutor review mode", () => {
|
|
|
4749
4968
|
expect(options.env?.BAKER_DEBUG_LOG).toBeUndefined();
|
|
4750
4969
|
expect(options.settings ?? {}).not.toHaveProperty("disableAllHooks");
|
|
4751
4970
|
expect(options.settings ?? {}).not.toHaveProperty("permissions");
|
|
4971
|
+
expect(options.settings ?? {}).not.toHaveProperty("claudeMdExcludes");
|
|
4752
4972
|
});
|
|
4753
4973
|
// BAKER-3462: after an English skill body, the next line went out in English to a Spanish chat.
|
|
4754
4974
|
const spanishTurn = "Prepara cuatro anuncios nuevos para la campaña de verano y súbelos hoy mismo";
|