@koda-sl/baker-bridge 0.172.0-dev.b570aa3f2 → 0.172.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 (110) hide show
  1. package/README.md +51 -67
  2. package/dist/builder/builder-turn.d.ts +28 -0
  3. package/dist/builder/builder-turn.d.ts.map +1 -0
  4. package/dist/builder/builder-turn.js +17 -0
  5. package/dist/builder/builder-turn.js.map +1 -0
  6. package/dist/hono/agent-session.d.ts.map +1 -1
  7. package/dist/hono/agent-session.js +4 -3
  8. package/dist/hono/agent-session.js.map +1 -1
  9. package/dist/hono/agent-session.test.js +4 -3
  10. package/dist/hono/agent-session.test.js.map +1 -1
  11. package/dist/hono/agent.d.ts.map +1 -1
  12. package/dist/hono/agent.js +5 -6
  13. package/dist/hono/agent.js.map +1 -1
  14. package/dist/hono/agent.test.js +91 -177
  15. package/dist/hono/agent.test.js.map +1 -1
  16. package/dist/hono/attachments.d.ts.map +1 -1
  17. package/dist/hono/attachments.js +9 -1
  18. package/dist/hono/attachments.js.map +1 -1
  19. package/dist/hono/attachments.test.js +22 -1
  20. package/dist/hono/attachments.test.js.map +1 -1
  21. package/dist/hono/bridge-job.d.ts +16 -28
  22. package/dist/hono/bridge-job.d.ts.map +1 -1
  23. package/dist/hono/bridge-job.js +60 -189
  24. package/dist/hono/bridge-job.js.map +1 -1
  25. package/dist/hono/bridge-job.test.js +99 -305
  26. package/dist/hono/bridge-job.test.js.map +1 -1
  27. package/dist/hono/flow-input-card-fields.test.js +1 -1
  28. package/dist/hono/flow-input.d.ts +1 -1
  29. package/dist/hono/flow-input.js +1 -1
  30. package/dist/hono/main-sync.d.ts +22 -0
  31. package/dist/hono/main-sync.d.ts.map +1 -1
  32. package/dist/hono/main-sync.js +110 -2
  33. package/dist/hono/main-sync.js.map +1 -1
  34. package/dist/hono/main-sync.test.js +127 -1
  35. package/dist/hono/main-sync.test.js.map +1 -1
  36. package/dist/hono/model-capabilities.d.ts +0 -1
  37. package/dist/hono/model-capabilities.d.ts.map +1 -1
  38. package/dist/hono/model-capabilities.js +0 -4
  39. package/dist/hono/model-capabilities.js.map +1 -1
  40. package/dist/hono/openrouter-session.d.ts +13 -19
  41. package/dist/hono/openrouter-session.d.ts.map +1 -1
  42. package/dist/hono/openrouter-session.js +13 -19
  43. package/dist/hono/openrouter-session.js.map +1 -1
  44. package/dist/hono/publish-handoff.d.ts +0 -1
  45. package/dist/hono/publish-handoff.d.ts.map +1 -1
  46. package/dist/hono/publish-handoff.js +0 -53
  47. package/dist/hono/publish-handoff.js.map +1 -1
  48. package/dist/hono/publish-handoff.test.js +0 -48
  49. package/dist/hono/publish-handoff.test.js.map +1 -1
  50. package/dist/hono/relay.d.ts +0 -8
  51. package/dist/hono/relay.d.ts.map +1 -1
  52. package/dist/hono/relay.js +0 -2
  53. package/dist/hono/relay.js.map +1 -1
  54. package/dist/hono/relay.test.js +0 -30
  55. package/dist/hono/relay.test.js.map +1 -1
  56. package/dist/hono/runtime-files.d.ts +0 -1
  57. package/dist/hono/runtime-files.d.ts.map +1 -1
  58. package/dist/hono/runtime-files.js +0 -3
  59. package/dist/hono/runtime-files.js.map +1 -1
  60. package/dist/hono/served-model.d.ts +0 -8
  61. package/dist/hono/served-model.d.ts.map +1 -1
  62. package/dist/hono/served-model.js +0 -41
  63. package/dist/hono/served-model.js.map +1 -1
  64. package/dist/hono/served-model.test.js +1 -19
  65. package/dist/hono/served-model.test.js.map +1 -1
  66. package/dist/hono/session-continuity.integration.test.js +0 -2
  67. package/dist/hono/session-continuity.integration.test.js.map +1 -1
  68. package/dist/hono/types.d.ts +7 -12
  69. package/dist/hono/types.d.ts.map +1 -1
  70. package/dist/review/digest.d.ts +0 -13
  71. package/dist/review/digest.d.ts.map +1 -1
  72. package/dist/review/digest.js +1 -1
  73. package/dist/review/digest.js.map +1 -1
  74. package/dist/review/structured-output-gate.d.ts +10 -0
  75. package/dist/review/structured-output-gate.d.ts.map +1 -1
  76. package/dist/review/structured-output-gate.js +15 -5
  77. package/dist/review/structured-output-gate.js.map +1 -1
  78. package/package.json +1 -2
  79. package/dist/hono/subagent-route.d.ts +0 -64
  80. package/dist/hono/subagent-route.d.ts.map +0 -1
  81. package/dist/hono/subagent-route.js +0 -147
  82. package/dist/hono/subagent-route.js.map +0 -1
  83. package/dist/hono/subagent-route.test.d.ts +0 -2
  84. package/dist/hono/subagent-route.test.d.ts.map +0 -1
  85. package/dist/hono/subagent-route.test.js +0 -68
  86. package/dist/hono/subagent-route.test.js.map +0 -1
  87. package/dist/hono/tool-result-ledger.d.ts +0 -32
  88. package/dist/hono/tool-result-ledger.d.ts.map +0 -1
  89. package/dist/hono/tool-result-ledger.js +0 -67
  90. package/dist/hono/tool-result-ledger.js.map +0 -1
  91. package/dist/hono/tool-result-ledger.test.d.ts +0 -2
  92. package/dist/hono/tool-result-ledger.test.d.ts.map +0 -1
  93. package/dist/hono/tool-result-ledger.test.js +0 -61
  94. package/dist/hono/tool-result-ledger.test.js.map +0 -1
  95. package/dist/hono/turn-route.d.ts +0 -84
  96. package/dist/hono/turn-route.d.ts.map +0 -1
  97. package/dist/hono/turn-route.js +0 -121
  98. package/dist/hono/turn-route.js.map +0 -1
  99. package/dist/hono/turn-route.test.d.ts +0 -2
  100. package/dist/hono/turn-route.test.d.ts.map +0 -1
  101. package/dist/hono/turn-route.test.js +0 -134
  102. package/dist/hono/turn-route.test.js.map +0 -1
  103. package/dist/hono/workspace-instructions.d.ts +0 -43
  104. package/dist/hono/workspace-instructions.d.ts.map +0 -1
  105. package/dist/hono/workspace-instructions.js +0 -68
  106. package/dist/hono/workspace-instructions.js.map +0 -1
  107. package/dist/hono/workspace-instructions.test.d.ts +0 -2
  108. package/dist/hono/workspace-instructions.test.d.ts.map +0 -1
  109. package/dist/hono/workspace-instructions.test.js +0 -68
  110. package/dist/hono/workspace-instructions.test.js.map +0 -1
@@ -1,68 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { parseAgentDefinition, routeSubagent, wantsInstructions } from "./subagent-route.js";
3
- const OPUS = "claude-opus-5-5";
4
- const SONNET = "claude-sonnet-5-5";
5
- const reading = { small: 0.2, complex: 0.8, readOnly: 0.85, deliverable: 0.1 };
6
- const building = { small: 0.1, complex: 0.95, readOnly: 0.1, deliverable: 0.9 };
7
- const scaffold = new Map([
8
- ["research", { model: SONNET, omitClaudeMd: false }],
9
- ["claim-checker", { model: SONNET, omitClaudeMd: true }],
10
- ]);
11
- describe("parseAgentDefinition", () => {
12
- it("reads the name, model and whether the agent skips CLAUDE.md from its frontmatter", () => {
13
- const file = "---\nname: claim-checker\ntools: Bash, Read\nmodel: claude-sonnet-5-5\neffort: medium\nomitClaudeMd: true\n---\n\nCheck claims.";
14
- expect(parseAgentDefinition(file)).toEqual({ name: "claim-checker", model: SONNET, omitClaudeMd: true });
15
- });
16
- it("is null for a file with no name", () => {
17
- expect(parseAgentDefinition("---\nmodel: x\n---\nbody")).toBeNull();
18
- expect(parseAgentDefinition("no frontmatter")).toBeNull();
19
- });
20
- });
21
- describe("wantsInstructions", () => {
22
- it("gives the workspace's instructions to every subagent that loaded CLAUDE.md before they moved to the system prompt", () => {
23
- expect(wantsInstructions("general-purpose", scaffold)).toBe(true);
24
- expect(wantsInstructions("claude", scaffold)).toBe(true);
25
- expect(wantsInstructions("research", scaffold)).toBe(true);
26
- });
27
- it("leaves out an agent that opts out, the built-in readers, and a fork, which already carries the parent's", () => {
28
- for (const agentType of ["claim-checker", "Explore", "Plan", "fork"]) {
29
- expect(wantsInstructions(agentType, scaffold)).toBe(false);
30
- }
31
- });
32
- });
33
- describe("routeSubagent", () => {
34
- const spawn = { agentType: "general-purpose", parentModel: OPUS, definitions: scaffold };
35
- it("runs a built-in subagent that only reads or analyses on Sonnet", () => {
36
- expect(routeSubagent({ ...spawn, signals: reading })).toEqual({
37
- model: "sonnet",
38
- effort: "high",
39
- reason: "read_only",
40
- });
41
- });
42
- it("leaves a built-in subagent doing finished work on the parent's Opus", () => {
43
- expect(routeSubagent({ ...spawn, signals: building })).toBeNull();
44
- });
45
- it("moves finished work to Opus when the parent chat runs on Sonnet", () => {
46
- expect(routeSubagent({ ...spawn, parentModel: SONNET, signals: building })).toEqual({
47
- model: "opus",
48
- effort: "medium",
49
- reason: "deliverable",
50
- });
51
- });
52
- it("runs Explore, which only searches, on Sonnet without asking", () => {
53
- expect(routeSubagent({ ...spawn, agentType: "Explore", signals: null })).toEqual({
54
- model: "sonnet",
55
- effort: "medium",
56
- reason: "explore",
57
- });
58
- });
59
- it.each([
60
- ["a fork, which must share the parent's cache", { agentType: "fork" }],
61
- ["a scaffold agent, which pins its own model", { agentType: "research" }],
62
- ["a call that already names a model", { explicitModel: "opus" }],
63
- ["no answer from Jev", { signals: null }],
64
- ])("leaves %s alone", (_label, overrides) => {
65
- expect(routeSubagent({ ...spawn, signals: reading, ...overrides })).toBeNull();
66
- });
67
- });
68
- //# sourceMappingURL=subagent-route.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"subagent-route.test.js","sourceRoot":"","sources":["../../src/hono/subagent-route.test.ts"],"names":[],"mappings":"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"}
@@ -1,32 +0,0 @@
1
- /**
2
- * What one tool's results put in a context during one turn.
3
- *
4
- * Tool results are most of what a turn writes to the prompt cache — on warm
5
- * turns ~93% of the tokens written are not the model's own output — and every
6
- * one is written once at the 1h-write price and re-read on each later request.
7
- * Production recorded none of it (the review digest measures it, inside the
8
- * sandbox only), so which command families to make compact was guesswork.
9
- *
10
- * A `baker` call is keyed by its command (`baker ads google query`), anything
11
- * else by its tool name. A subagent's results land in its own context, so they
12
- * are kept apart from the main agent's.
13
- */
14
- export type ToolResultSize = {
15
- tool: string;
16
- agent: "main" | "subagent";
17
- calls: number;
18
- bytes: number;
19
- /** Text at bytes/4, images by their pixels — the review digest's estimate. */
20
- tokens: number;
21
- };
22
- export declare class ToolResultLedger {
23
- private readonly keys;
24
- private readonly totals;
25
- /** Feed every SDK message of the turn, in order. */
26
- observe(message: unknown): void;
27
- private recordCalls;
28
- private recordResults;
29
- /** The largest entries first. */
30
- summary(limit?: number): ToolResultSize[];
31
- }
32
- //# sourceMappingURL=tool-result-ledger.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-result-ledger.d.ts","sourceRoot":"","sources":["../../src/hono/tool-result-ledger.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAgCF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAE5D,oDAAoD;IACpD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAW9B;IAED,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,aAAa;IAgBrB,iCAAiC;IACjC,OAAO,CAAC,KAAK,SAAgB,GAAG,cAAc,EAAE,CAK/C;CACF"}
@@ -1,67 +0,0 @@
1
- import { bakerVerbOf, measureToolResult } from "../review/digest.js";
2
- /** Enough to name every family that matters without growing the completion payload. */
3
- const DEFAULT_LIMIT = 30;
4
- function blocksOf(message) {
5
- if (typeof message !== "object" || message === null) {
6
- return [];
7
- }
8
- const content = message.message?.content;
9
- return Array.isArray(content) ? content : [];
10
- }
11
- function keyOf(name, input) {
12
- if (name === "Bash" && typeof input === "object" && input !== null) {
13
- const command = input.command;
14
- if (typeof command === "string") {
15
- return bakerVerbOf(command) ?? "Bash";
16
- }
17
- }
18
- return name;
19
- }
20
- export class ToolResultLedger {
21
- keys = new Map();
22
- totals = new Map();
23
- /** Feed every SDK message of the turn, in order. */
24
- observe(message) {
25
- if (typeof message !== "object" || message === null) {
26
- return;
27
- }
28
- const { type, parent_tool_use_id: parent } = message;
29
- const agent = typeof parent === "string" && parent.length > 0 ? "subagent" : "main";
30
- if (type === "assistant") {
31
- this.recordCalls(message);
32
- }
33
- else if (type === "user") {
34
- this.recordResults(message, agent);
35
- }
36
- }
37
- recordCalls(message) {
38
- for (const block of blocksOf(message)) {
39
- if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
40
- this.keys.set(block.id, keyOf(block.name, block.input));
41
- }
42
- }
43
- }
44
- recordResults(message, agent) {
45
- for (const block of blocksOf(message)) {
46
- if (block.type !== "tool_result") {
47
- continue;
48
- }
49
- const tool = (typeof block.tool_use_id === "string" && this.keys.get(block.tool_use_id)) || "unknown";
50
- const { bytes, tokens } = measureToolResult(block.content);
51
- const entryKey = `${agent}\u0000${tool}`;
52
- const entry = this.totals.get(entryKey) ?? { tool, agent, calls: 0, bytes: 0, tokens: 0 };
53
- entry.calls += 1;
54
- entry.bytes += bytes;
55
- entry.tokens += tokens;
56
- this.totals.set(entryKey, entry);
57
- }
58
- }
59
- /** The largest entries first. */
60
- summary(limit = DEFAULT_LIMIT) {
61
- return [...this.totals.values()]
62
- .sort((a, b) => b.tokens - a.tokens || b.bytes - a.bytes)
63
- .slice(0, limit)
64
- .map((entry) => ({ ...entry }));
65
- }
66
- }
67
- //# sourceMappingURL=tool-result-ledger.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-result-ledger.js","sourceRoot":"","sources":["../../src/hono/tool-result-ledger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAwBrE,uFAAuF;AACvF,MAAM,aAAa,GAAG,EAAE,CAAC;AAWzB,SAAS,QAAQ,CAAC,OAAgB;IAChC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,OAAO,GAAI,OAA+C,CAAC,OAAO,EAAE,OAAO,CAAC;IAClF,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAE,OAA8B,CAAC,CAAC,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,KAAc;IACzC,IAAI,IAAI,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnE,MAAM,OAAO,GAAI,KAA+B,CAAC,OAAO,CAAC;QACzD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,WAAW,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,gBAAgB;IACV,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjC,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE5D,oDAAoD;IACpD,OAAO,CAAC,OAAgB;QACtB,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACpD,OAAO;QACT,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,OAA2D,CAAC;QACzG,MAAM,KAAK,GAAG,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;QACpF,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;aAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,OAAe;QACjC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAChG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,OAAe,EAAE,KAA8B;QACnE,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACjC,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,SAAS,CAAC;YACtG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3D,MAAM,QAAQ,GAAG,GAAG,KAAK,SAAS,IAAI,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YAC1F,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YACjB,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC;YACrB,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC;YACvB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,OAAO,CAAC,KAAK,GAAG,aAAa;QAC3B,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;aAC7B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;aACxD,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;aACf,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;IACpC,CAAC;CACF"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=tool-result-ledger.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-result-ledger.test.d.ts","sourceRoot":"","sources":["../../src/hono/tool-result-ledger.test.ts"],"names":[],"mappings":""}
@@ -1,61 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { ToolResultLedger } from "./tool-result-ledger.js";
3
- function toolUse(id, name, input, parent = null) {
4
- return {
5
- type: "assistant",
6
- parent_tool_use_id: parent,
7
- message: { content: [{ type: "tool_use", id, name, input }] },
8
- };
9
- }
10
- function toolResult(id, content, parent = null) {
11
- return {
12
- type: "user",
13
- parent_tool_use_id: parent,
14
- message: { content: [{ type: "tool_result", tool_use_id: id, content }] },
15
- };
16
- }
17
- describe("ToolResultLedger", () => {
18
- it("sums what each tool's results put in the context, a baker call by its family and verb", () => {
19
- const ledger = new ToolResultLedger();
20
- ledger.observe(toolUse("a", "Bash", { command: "baker ads google query --limit 5" }));
21
- ledger.observe(toolResult("a", "x".repeat(4000)));
22
- ledger.observe(toolUse("b", "Bash", { command: "cd /repo && baker ads google query --all" }));
23
- ledger.observe(toolResult("b", [{ type: "text", text: "y".repeat(2000) }]));
24
- ledger.observe(toolUse("c", "Read", { file_path: "/repo/CLAUDE.md" }));
25
- ledger.observe(toolResult("c", "z".repeat(400)));
26
- expect(ledger.summary()).toEqual([
27
- { tool: "baker ads google", agent: "main", calls: 2, bytes: 6000, tokens: 1500 },
28
- { tool: "Read", agent: "main", calls: 1, bytes: 400, tokens: 100 },
29
- ]);
30
- });
31
- it("keeps a subagent's results apart from the main agent's, since they land in another context", () => {
32
- const ledger = new ToolResultLedger();
33
- ledger.observe(toolUse("s1", "WebFetch", { url: "https://example.com" }, "agent_1"));
34
- ledger.observe(toolResult("s1", "w".repeat(800), "agent_1"));
35
- ledger.observe(toolUse("m1", "WebFetch", { url: "https://example.com" }));
36
- ledger.observe(toolResult("m1", "w".repeat(80)));
37
- expect(ledger.summary()).toEqual([
38
- { tool: "WebFetch", agent: "subagent", calls: 1, bytes: 800, tokens: 200 },
39
- { tool: "WebFetch", agent: "main", calls: 1, bytes: 80, tokens: 20 },
40
- ]);
41
- });
42
- it("counts a plain shell command as Bash and a result whose call it never saw as unknown", () => {
43
- const ledger = new ToolResultLedger();
44
- ledger.observe(toolUse("a", "Bash", { command: "ls -la" }));
45
- ledger.observe(toolResult("a", "abcd"));
46
- ledger.observe(toolResult("orphan", "abcdefgh"));
47
- expect(ledger.summary()).toEqual([
48
- { tool: "unknown", agent: "main", calls: 1, bytes: 8, tokens: 2 },
49
- { tool: "Bash", agent: "main", calls: 1, bytes: 4, tokens: 1 },
50
- ]);
51
- });
52
- it("keeps the largest entries when there are more than the limit", () => {
53
- const ledger = new ToolResultLedger();
54
- for (let i = 0; i < 5; i++) {
55
- ledger.observe(toolUse(`t${i}`, `mcp__tool_${i}`, {}));
56
- ledger.observe(toolResult(`t${i}`, "q".repeat((i + 1) * 40)));
57
- }
58
- expect(ledger.summary(2).map((entry) => entry.tool)).toEqual(["mcp__tool_4", "mcp__tool_3"]);
59
- });
60
- });
61
- //# sourceMappingURL=tool-result-ledger.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-result-ledger.test.js","sourceRoot":"","sources":["../../src/hono/tool-result-ledger.test.ts"],"names":[],"mappings":"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"}
@@ -1,84 +0,0 @@
1
- import type { Judge } from "./jev.ts";
2
- /**
3
- * Which model a turn runs on and how hard it works, decided per turn from what
4
- * Jev reads in the marketer's message and the agent's last reply.
5
- *
6
- * **The model is chosen once, when a chat starts, and only ever moves up.** Each
7
- * model keeps its own prompt cache, and the CLI drops thinking written by
8
- * another model, so a switch re-writes the whole conversation. A chat starts on
9
- * Sonnet only when Jev clearly reads it as reading, checking or analysing —
10
- * no landing, creative, report, document or other finished work. The first turn
11
- * that asks for such work moves it to Opus, for good; nothing moves a chat back.
12
- * The output's quality is what the product sells, so the band is narrow on
13
- * purpose: on 204 human chats (Oct 2026) it took 17 (7% of their spend), all
14
- * of them "how are the ads doing / analyse this campaign / look at this lead".
15
- *
16
- * **Effort changes freely per turn** — the CLI restates it per message
17
- * (`PER_TURN_EFFORT_CAPABILITIES`), which keeps the cache. Each model has its
18
- * own range: Opus works at low or medium (above that it costs more than
19
- * Sonnet's best for little); Sonnet may go to high on analysis that needs
20
- * judgment (its xhigh writes 3.9× medium's output and costs what Opus-medium
21
- * does); Haiku never goes below medium.
22
- *
23
- * Every decision is recorded with the turn (`thread.complete`), so both bands
24
- * can be widened against what those turns did rather than guessed.
25
- */
26
- export type TurnEffort = "low" | "medium" | "high";
27
- /** Jev's probability that each statement holds for the turn. */
28
- export type TurnSignals = {
29
- small: number;
30
- complex: number;
31
- readOnly: number;
32
- deliverable: number;
33
- };
34
- export type ModelReason = "fixed" | "default" | "kept" | "read_only" | "deliverable" | "plan";
35
- export type EffortReason = "small" | "not_small" | "complex" | "unjudged" | "plan" | "command" | "unattended" | "attachments";
36
- export type TurnRoute = {
37
- model: string;
38
- modelReason: ModelReason;
39
- effort: TurnEffort;
40
- reason: EffortReason;
41
- signals?: TurnSignals;
42
- /** The subagents this turn spawned that were moved to another model (`subagent-route.ts`). */
43
- subagents?: {
44
- agentType: string;
45
- model: string;
46
- effort: string;
47
- reason: string;
48
- }[];
49
- };
50
- export type TurnFacts = {
51
- /** Null when Jev was not asked or did not answer. */
52
- signals: TurnSignals | null;
53
- /** The Runtime's model: Opus 5.5 in production, Haiku on a development deployment. */
54
- defaultModel: string;
55
- /** False when the Runtime pins every turn to one model (`BAKER_AGENT_MODEL`). */
56
- routeModels: boolean;
57
- /** The model the conversation being resumed ran on; its cache is on that model. */
58
- sessionModel?: string;
59
- /** Nobody has replied in this chat yet. */
60
- startedChat: boolean;
61
- planMode?: boolean;
62
- /** The turn opens with a command the SDK runs (publish, compact, a skill). */
63
- leadingCommand?: boolean;
64
- /** A Scheduled agent's run. */
65
- unattended?: boolean;
66
- /** Jev reads text only; what is attached is unseen. */
67
- hasAttachments?: boolean;
68
- };
69
- /** Whether Jev has anything to read: plan mode, a leading command and attachments decide on their own. */
70
- export declare function needsSignals(facts: Pick<TurnFacts, "planMode" | "leadingCommand" | "hasAttachments">): boolean;
71
- export declare function askTurnSignals({ message, previousReply, judge, }: {
72
- message: string;
73
- /** The agent's last message in the chat, empty on its first turn. */
74
- previousReply: string;
75
- judge: Judge;
76
- }): Promise<TurnSignals | null>;
77
- /** Jev reads the work as asking for, or approving, finished work others will see. */
78
- export declare function asksForFinishedWork(signals: TurnSignals): boolean;
79
- /** Jev reads the work as needing judgment: Sonnet thinks harder on it. */
80
- export declare function needsJudgment(signals: TurnSignals): boolean;
81
- export declare function routeTurn(facts: TurnFacts): TurnRoute;
82
- /** Jev clearly reads the work as reading, checking or analysing, with no finished work. */
83
- export declare function isOnlyReading(signals: TurnSignals): boolean;
84
- //# sourceMappingURL=turn-route.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"turn-route.d.ts","sourceRoot":"","sources":["../../src/hono/turn-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,UAAU,CAAC;AAGnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnD,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,aAAa,GAAG,MAAM,CAAC;AAC9F,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,WAAW,GACX,SAAS,GACT,UAAU,GACV,MAAM,GACN,SAAS,GACT,YAAY,GACZ,aAAa,CAAC;AAElB,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,8FAA8F;IAC9F,SAAS,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACpF,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,qDAAqD;IACrD,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,sFAAsF;IACtF,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,WAAW,EAAE,OAAO,CAAC;IACrB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+BAA+B;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uDAAuD;IACvD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAsCF,0GAA0G;AAC1G,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,gBAAgB,GAAG,gBAAgB,CAAC,GAAG,OAAO,CAE9G;AAED,wBAAsB,cAAc,CAAC,EACnC,OAAO,EACP,aAAa,EACb,KAAK,GACN,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC;CACd,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAc9B;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAEjE;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAIrD;AAyBD,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE3D"}
@@ -1,121 +0,0 @@
1
- import { normalizeModel, READING_MODEL } from "./model-capabilities.js";
2
- const OPUS = "claude-opus-5-5";
3
- /** Jev's probability that the turn is small must be at least this… */
4
- const LOW_EFFORT_MIN_SMALL = 0.75;
5
- /** …and its probability that the turn needs judgment or several steps at most this. */
6
- const LOW_EFFORT_MAX_COMPLEX = 0.3;
7
- /** Sonnet works at high effort from this probability that the turn needs judgment. */
8
- const HIGH_EFFORT_MIN_COMPLEX = 0.7;
9
- /** A chat starts on Sonnet when it is at least this likely to be only reading… */
10
- const READING_MIN_READ_ONLY = 0.7;
11
- /** …and at most this likely to ask for finished work. */
12
- const READING_MAX_DELIVERABLE = 0.3;
13
- /** A Sonnet chat moves to Opus from this probability that the turn asks for finished work. */
14
- const UPGRADE_MIN_DELIVERABLE = 0.5;
15
- const EFFORT_ORDER = ["low", "medium", "high"];
16
- const EFFORT_RANGE = {
17
- [OPUS]: { floor: "low", ceiling: "medium" },
18
- [READING_MODEL]: { floor: "low", ceiling: "high" },
19
- "claude-haiku-5-5": { floor: "medium", ceiling: "medium" },
20
- };
21
- /** Text Jev reads, clipped: it sees 32k tokens, and the end of a reply is what the next message answers. */
22
- const MAX_MESSAGE_CHARS = 3_000;
23
- const MAX_REPLY_CHARS = 1_500;
24
- const PREAMBLE = "Read the agent's last reply `previous_reply` (empty when the chat just started) and the marketer's new `message`. ";
25
- const QUESTIONS = {
26
- small: `${PREAMBLE}Can the agent fully handle \`message\` with a short answer, a quick lookup of one thing, or one small change the marketer spelled out — with no new or rebuilt page, ad creative, campaign, form, report, document, audit, research or plan?`,
27
- complex: `${PREAMBLE}Does \`message\` ask for, or say yes to, work that needs judgment or several steps — building or changing a page, ad, campaign, form, report or document; diagnosing why results are poor; research; or a plan or strategy? A short 'yes', 'go ahead' or 'do it' approving something \`previous_reply\` proposed counts as asking for that work.`,
28
- readOnly: `${PREAMBLE}Is \`message\` only about reading, checking, analysing or exploring information — data, metrics, campaigns, accounts, files, meetings, messages or the web — and then answering, summarising or noting what was found, without creating or changing anything a client or the public will see?`,
29
- deliverable: `${PREAMBLE}Does \`message\` ask for, or say yes to, creating or changing a finished piece of work others will see — a landing page or website change, an ad or creative, a change to a live campaign, a form, a report, a document or PDF, an email or newsletter, or a published post? A short 'yes', 'go ahead' or 'do it' approving such work that \`previous_reply\` proposed counts.`,
30
- };
31
- const SIGNAL_KEYS = Object.keys(QUESTIONS);
32
- /** Whether Jev has anything to read: plan mode, a leading command and attachments decide on their own. */
33
- export function needsSignals(facts) {
34
- return !(facts.planMode || facts.leadingCommand || facts.hasAttachments);
35
- }
36
- export async function askTurnSignals({ message, previousReply, judge, }) {
37
- const context = {
38
- previous_reply: previousReply.slice(-MAX_REPLY_CHARS),
39
- message: message.slice(0, MAX_MESSAGE_CHARS),
40
- };
41
- const questions = SIGNAL_KEYS.map((key) => ({
42
- instructions: { question: QUESTIONS[key], ...context },
43
- }));
44
- const answers = await judge(questions);
45
- if (!answers || answers.length !== SIGNAL_KEYS.length) {
46
- return null;
47
- }
48
- const [small, complex, readOnly, deliverable] = answers;
49
- return { small, complex, readOnly, deliverable };
50
- }
51
- /** Jev reads the work as asking for, or approving, finished work others will see. */
52
- export function asksForFinishedWork(signals) {
53
- return signals.deliverable >= UPGRADE_MIN_DELIVERABLE;
54
- }
55
- /** Jev reads the work as needing judgment: Sonnet thinks harder on it. */
56
- export function needsJudgment(signals) {
57
- return signals.complex >= HIGH_EFFORT_MIN_COMPLEX;
58
- }
59
- export function routeTurn(facts) {
60
- const { model, modelReason } = modelFor(facts);
61
- const { effort, reason } = effortFor(facts, model);
62
- return { model, modelReason, effort, reason, ...(facts.signals ? { signals: facts.signals } : {}) };
63
- }
64
- function modelFor(facts) {
65
- if (!facts.routeModels || normalizeModel(facts.defaultModel) !== OPUS) {
66
- return { model: facts.defaultModel, modelReason: "fixed" };
67
- }
68
- const session = facts.sessionModel === undefined ? undefined : normalizeModel(facts.sessionModel);
69
- if (session === READING_MODEL) {
70
- if (facts.planMode) {
71
- return { model: OPUS, modelReason: "plan" };
72
- }
73
- if (facts.signals && asksForFinishedWork(facts.signals)) {
74
- return { model: OPUS, modelReason: "deliverable" };
75
- }
76
- return { model: READING_MODEL, modelReason: "kept" };
77
- }
78
- if (session === OPUS) {
79
- return { model: OPUS, modelReason: "kept" };
80
- }
81
- if (facts.startedChat && needsSignals(facts) && facts.signals && isOnlyReading(facts.signals)) {
82
- return { model: READING_MODEL, modelReason: "read_only" };
83
- }
84
- return { model: facts.defaultModel, modelReason: "default" };
85
- }
86
- /** Jev clearly reads the work as reading, checking or analysing, with no finished work. */
87
- export function isOnlyReading(signals) {
88
- return signals.readOnly >= READING_MIN_READ_ONLY && signals.deliverable <= READING_MAX_DELIVERABLE;
89
- }
90
- function effortFor(facts, model) {
91
- const wanted = wantedEffort(facts, model);
92
- return { effort: clampEffort(model, wanted.effort), reason: wanted.reason };
93
- }
94
- function wantedEffort(facts, model) {
95
- if (facts.planMode)
96
- return { effort: "medium", reason: "plan" };
97
- if (facts.leadingCommand)
98
- return { effort: "medium", reason: "command" };
99
- if (facts.hasAttachments)
100
- return { effort: "medium", reason: "attachments" };
101
- const signals = facts.signals;
102
- if (!signals)
103
- return { effort: "medium", reason: "unjudged" };
104
- if (normalizeModel(model) === READING_MODEL && needsJudgment(signals)) {
105
- return { effort: "high", reason: "complex" };
106
- }
107
- if (signals.small >= LOW_EFFORT_MIN_SMALL && signals.complex <= LOW_EFFORT_MAX_COMPLEX) {
108
- // A Scheduled agent's templated prompt reads as small and is not.
109
- return facts.unattended ? { effort: "medium", reason: "unattended" } : { effort: "low", reason: "small" };
110
- }
111
- return { effort: "medium", reason: "not_small" };
112
- }
113
- function clampEffort(model, wanted) {
114
- const range = EFFORT_RANGE[normalizeModel(model)];
115
- if (!range) {
116
- return "medium";
117
- }
118
- const index = Math.min(Math.max(EFFORT_ORDER.indexOf(wanted), EFFORT_ORDER.indexOf(range.floor)), EFFORT_ORDER.indexOf(range.ceiling));
119
- return EFFORT_ORDER[index] ?? "medium";
120
- }
121
- //# sourceMappingURL=turn-route.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"turn-route.js","sourceRoot":"","sources":["../../src/hono/turn-route.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=turn-route.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"turn-route.test.d.ts","sourceRoot":"","sources":["../../src/hono/turn-route.test.ts"],"names":[],"mappings":""}
@@ -1,134 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { askTurnSignals, routeTurn } from "./turn-route.js";
3
- const OPUS = "claude-opus-5-5";
4
- const SONNET = "claude-sonnet-5-5";
5
- const HAIKU = "claude-haiku-5-5";
6
- function signals(overrides = {}) {
7
- return { small: 0.2, complex: 0.9, readOnly: 0.3, deliverable: 0.8, ...overrides };
8
- }
9
- const reading = signals({ readOnly: 0.8, deliverable: 0.1 });
10
- function facts(overrides = {}) {
11
- return { signals: signals(), defaultModel: OPUS, routeModels: true, startedChat: true, ...overrides };
12
- }
13
- describe("routeTurn: the model", () => {
14
- it("runs a chat on Opus by default", () => {
15
- expect(routeTurn(facts())).toMatchObject({ model: OPUS, modelReason: "default" });
16
- });
17
- it("starts a chat Jev clearly reads as only reading or analysing on Sonnet", () => {
18
- expect(routeTurn(facts({ signals: reading }))).toMatchObject({ model: SONNET, modelReason: "read_only" });
19
- });
20
- it.each([
21
- ["it asks for a deliverable too", signals({ readOnly: 0.8, deliverable: 0.35 })],
22
- ["it is not clearly only reading", signals({ readOnly: 0.65, deliverable: 0.1 })],
23
- ])("keeps Opus for a new chat when %s", (_label, judged) => {
24
- expect(routeTurn(facts({ signals: judged })).model).toBe(OPUS);
25
- });
26
- it.each([
27
- ["plan mode", { planMode: true }],
28
- ["a leading command", { leadingCommand: true }],
29
- ["attachments Jev cannot read", { hasAttachments: true }],
30
- ["no answer from Jev", { signals: null }],
31
- ])("keeps Opus for a new chat with %s", (_label, flags) => {
32
- expect(routeTurn(facts({ signals: reading, ...flags })).model).toBe(OPUS);
33
- });
34
- it("starts a Scheduled agent's reading run on Sonnet too", () => {
35
- expect(routeTurn(facts({ signals: reading, unattended: true })).model).toBe(SONNET);
36
- });
37
- it("never moves a chat already on Opus to Sonnet", () => {
38
- expect(routeTurn(facts({ signals: reading, sessionModel: OPUS, startedChat: false }))).toMatchObject({
39
- model: OPUS,
40
- modelReason: "kept",
41
- });
42
- });
43
- it("keeps a chat that was rebuilt from its history on Opus", () => {
44
- // No conversation to resume, but the chat had replies: it may well have run on Opus.
45
- expect(routeTurn(facts({ signals: reading, startedChat: false })).model).toBe(OPUS);
46
- });
47
- it("keeps a Sonnet chat on Sonnet while it keeps reading", () => {
48
- const route = routeTurn(facts({ signals: signals({ readOnly: 0.6, deliverable: 0.2 }), sessionModel: SONNET }));
49
- expect(route).toMatchObject({ model: SONNET, modelReason: "kept" });
50
- });
51
- it("moves a Sonnet chat to Opus for good once a turn asks for a deliverable", () => {
52
- const route = routeTurn(facts({ signals: signals({ deliverable: 0.6 }), sessionModel: SONNET }));
53
- expect(route).toMatchObject({ model: OPUS, modelReason: "deliverable" });
54
- });
55
- it("moves a Sonnet chat to Opus for a plan", () => {
56
- expect(routeTurn(facts({ signals: null, planMode: true, sessionModel: SONNET }))).toMatchObject({
57
- model: OPUS,
58
- modelReason: "plan",
59
- });
60
- });
61
- it("keeps a Sonnet chat on Sonnet when Jev cannot answer", () => {
62
- expect(routeTurn(facts({ signals: null, sessionModel: SONNET })).model).toBe(SONNET);
63
- });
64
- it("runs every turn on the Runtime's model when routing is off", () => {
65
- expect(routeTurn(facts({ signals: reading, routeModels: false, defaultModel: HAIKU }))).toMatchObject({
66
- model: HAIKU,
67
- modelReason: "fixed",
68
- });
69
- });
70
- });
71
- describe("routeTurn: the effort", () => {
72
- it("runs a turn Jev clearly reads as small at low effort", () => {
73
- expect(routeTurn(facts({ signals: signals({ small: 0.9, complex: 0.1 }) }))).toMatchObject({
74
- effort: "low",
75
- reason: "small",
76
- });
77
- });
78
- it("keeps Opus at medium for work that needs judgment", () => {
79
- expect(routeTurn(facts())).toMatchObject({ model: OPUS, effort: "medium", reason: "not_small" });
80
- });
81
- it("lets Sonnet think harder than Opus on analysis that needs judgment", () => {
82
- expect(routeTurn(facts({ signals: { ...reading, complex: 0.8 } }))).toMatchObject({
83
- model: SONNET,
84
- effort: "high",
85
- reason: "complex",
86
- });
87
- });
88
- it("never runs Haiku below medium", () => {
89
- const route = routeTurn(facts({ signals: signals({ small: 0.9, complex: 0.1 }), routeModels: false, defaultModel: HAIKU }));
90
- expect(route.effort).toBe("medium");
91
- });
92
- it("never runs a Scheduled agent's run at low: its templated prompt reads as small and is not", () => {
93
- expect(routeTurn(facts({ signals: signals({ small: 0.9, complex: 0.1 }), unattended: true }))).toMatchObject({
94
- effort: "medium",
95
- reason: "unattended",
96
- });
97
- });
98
- it.each([
99
- ["plan mode", { planMode: true }, "plan"],
100
- ["a leading command", { leadingCommand: true }, "command"],
101
- ["attachments", { hasAttachments: true }, "attachments"],
102
- ["no answer from Jev", { signals: null }, "unjudged"],
103
- ])("keeps medium for %s", (_label, flags, reason) => {
104
- expect(routeTurn(facts(flags))).toMatchObject({ effort: "medium", reason });
105
- });
106
- });
107
- describe("askTurnSignals", () => {
108
- function judgeAnswering(answers, asked = []) {
109
- return (questions) => {
110
- asked.push([...questions]);
111
- return Promise.resolve(answers);
112
- };
113
- }
114
- it("reads the four answers in order", async () => {
115
- const answered = await askTurnSignals({ message: "hi", previousReply: "", judge: judgeAnswering([1, 2, 3, 4]) });
116
- expect(answered).toEqual({ small: 1, complex: 2, readOnly: 3, deliverable: 4 });
117
- });
118
- // A "sí, hazlo" is short, but it approves whatever the last reply proposed.
119
- it("asks about the message together with the agent's last reply", async () => {
120
- const asked = [];
121
- await askTurnSignals({
122
- message: "sí, hazlo",
123
- previousReply: "¿Creo la landing?",
124
- judge: judgeAnswering([0, 0, 0, 0], asked),
125
- });
126
- for (const question of asked[0] ?? []) {
127
- expect(question.instructions).toMatchObject({ message: "sí, hazlo", previous_reply: "¿Creo la landing?" });
128
- }
129
- });
130
- it("is null when Jev cannot answer", async () => {
131
- expect(await askTurnSignals({ message: "hi", previousReply: "", judge: () => Promise.resolve(null) })).toBeNull();
132
- });
133
- });
134
- //# sourceMappingURL=turn-route.test.js.map