@koda-sl/baker-bridge 0.168.0-dev.d78500250 → 0.172.0-dev.b570aa3f2
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 +35 -19
- 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 +19 -11
- package/dist/hono/bridge-job.d.ts.map +1 -1
- package/dist/hono/bridge-job.js +125 -50
- package/dist/hono/bridge-job.js.map +1 -1
- package/dist/hono/bridge-job.test.js +139 -7
- 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 +3 -0
- package/dist/hono/openrouter-session.d.ts.map +1 -1
- package/dist/hono/openrouter-session.js +3 -0
- package/dist/hono/openrouter-session.js.map +1 -1
- package/dist/hono/relay.d.ts +4 -4
- package/dist/hono/relay.d.ts.map +1 -1
- package/dist/hono/relay.js +1 -1
- package/dist/hono/relay.js.map +1 -1
- package/dist/hono/relay.test.js +10 -4
- package/dist/hono/relay.test.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/subagent-route.d.ts +64 -0
- package/dist/hono/subagent-route.d.ts.map +1 -0
- package/dist/hono/subagent-route.js +147 -0
- package/dist/hono/subagent-route.js.map +1 -0
- package/dist/hono/subagent-route.test.d.ts +2 -0
- package/dist/hono/subagent-route.test.d.ts.map +1 -0
- package/dist/hono/subagent-route.test.js +68 -0
- package/dist/hono/subagent-route.test.js.map +1 -0
- package/dist/hono/tool-result-ledger.d.ts +2 -0
- package/dist/hono/tool-result-ledger.d.ts.map +1 -1
- package/dist/hono/tool-result-ledger.js +12 -8
- package/dist/hono/tool-result-ledger.js.map +1 -1
- package/dist/hono/turn-route.d.ts +84 -0
- package/dist/hono/turn-route.d.ts.map +1 -0
- package/dist/hono/turn-route.js +121 -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/package.json +2 -1
- package/dist/hono/effort-route.d.ts +0 -43
- package/dist/hono/effort-route.d.ts.map +0 -1
- package/dist/hono/effort-route.js +0 -36
- package/dist/hono/effort-route.js.map +0 -1
- package/dist/hono/effort-route.test.d.ts +0 -2
- package/dist/hono/effort-route.test.d.ts.map +0 -1
- package/dist/hono/effort-route.test.js +0 -54
- package/dist/hono/effort-route.test.js.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-route.js","sourceRoot":"","sources":["../../src/hono/turn-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-route.test.d.ts","sourceRoot":"","sources":["../../src/hono/turn-route.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,134 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-route.test.js","sourceRoot":"","sources":["../../src/hono/turn-route.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koda-sl/baker-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.172.0-dev.b570aa3f2",
|
|
4
4
|
"description": "HTTP server wrapping the Claude Agent SDK. Persists chat events to Convex; the dashboard subscribes via Convex realtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"mockttp": "4.6.3",
|
|
52
52
|
"semver": "^7.8.5",
|
|
53
53
|
"unbash": "4.0.11",
|
|
54
|
+
"yaml": "^2.6.1",
|
|
54
55
|
"zod": "^4.3.6"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { Judge } from "./jev.ts";
|
|
2
|
-
/**
|
|
3
|
-
* How hard the agent works on one turn, decided per turn by Jev.
|
|
4
|
-
*
|
|
5
|
-
* `medium` (Opus 5.5's own default) stays the level for everything but turns
|
|
6
|
-
* Jev clearly reads as small — a short answer, one lookup, one change the
|
|
7
|
-
* marketer spelled out. Those run at `low`. The per-message effort the CLI
|
|
8
|
-
* restates (`PER_TURN_EFFORT_CAPABILITIES`) is what lets the level change from
|
|
9
|
-
* one turn to the next without re-writing the cached conversation.
|
|
10
|
-
*
|
|
11
|
-
* The thresholds were read off 648 production turns (Oct 2026): Jev's two
|
|
12
|
-
* answers track a turn's real output (Spearman ±0.55), and the band below
|
|
13
|
-
* routes 3% of turns — median 698 output tokens, against 4,658 for all turns.
|
|
14
|
-
* The band is deliberately narrow: a short message can approve big work ("sí,
|
|
15
|
-
* hazlo"), so the agent's last reply is read with it, and every decision is
|
|
16
|
-
* recorded with the turn (`thread.complete`) so the band can be widened against
|
|
17
|
-
* what those turns actually did, not guessed.
|
|
18
|
-
*/
|
|
19
|
-
export type TurnEffort = "low" | "medium";
|
|
20
|
-
export type EffortDecision = {
|
|
21
|
-
effort: TurnEffort;
|
|
22
|
-
reason: "small" | "not_small";
|
|
23
|
-
small: number;
|
|
24
|
-
complex: number;
|
|
25
|
-
} | {
|
|
26
|
-
effort: "medium";
|
|
27
|
-
reason: "unjudged" | "plan" | "command" | "unattended" | "attachments";
|
|
28
|
-
};
|
|
29
|
-
export declare const DEFAULT_TURN_EFFORT: "medium";
|
|
30
|
-
export declare function decideTurnEffort({ message, previousReply, judge, planMode, leadingCommand, unattended, hasAttachments, }: {
|
|
31
|
-
message: string;
|
|
32
|
-
/** The agent's last message in the chat, empty on its first turn. */
|
|
33
|
-
previousReply: string;
|
|
34
|
-
judge: Judge;
|
|
35
|
-
planMode?: boolean;
|
|
36
|
-
/** The turn opens with a command the SDK runs (publish, compact, a skill). */
|
|
37
|
-
leadingCommand?: boolean;
|
|
38
|
-
/** A Scheduled agent's run: its templated prompts read as small and are not. */
|
|
39
|
-
unattended?: boolean;
|
|
40
|
-
/** Jev reads text only; what is attached is unseen. */
|
|
41
|
-
hasAttachments?: boolean;
|
|
42
|
-
}): Promise<EffortDecision>;
|
|
43
|
-
//# sourceMappingURL=effort-route.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"effort-route.d.ts","sourceRoot":"","sources":["../../src/hono/effort-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,UAAU,CAAC;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE1C,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrF;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,aAAa,CAAA;CAAE,CAAC;AAEjG,eAAO,MAAM,mBAAmB,EAAE,QAAmB,CAAC;AAwBtD,wBAAsB,gBAAgB,CAAC,EACrC,OAAO,EACP,aAAa,EACb,KAAK,EACL,QAAgB,EAChB,cAAsB,EACtB,UAAkB,EAClB,cAAsB,GACvB,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gFAAgF;IAChF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uDAAuD;IACvD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAAC,cAAc,CAAC,CAa1B"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_TURN_EFFORT = "medium";
|
|
2
|
-
/** Jev's probability that the turn is small must be at least this… */
|
|
3
|
-
const LOW_EFFORT_MIN_SMALL = 0.75;
|
|
4
|
-
/** …and its probability that the turn needs judgment or several steps at most this. */
|
|
5
|
-
const LOW_EFFORT_MAX_COMPLEX = 0.3;
|
|
6
|
-
/** Text Jev reads, clipped: it sees 32k tokens, and the end of a reply is what the next message answers. */
|
|
7
|
-
const MAX_MESSAGE_CHARS = 3_000;
|
|
8
|
-
const MAX_REPLY_CHARS = 1_500;
|
|
9
|
-
const SMALL = "Read the agent's last reply `previous_reply` (empty when the chat just started) and the marketer's new `message`. 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?";
|
|
10
|
-
const COMPLEX = "Read the agent's last reply `previous_reply` (empty when the chat just started) and the marketer's new `message`. 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.";
|
|
11
|
-
function questionsFor(message, previousReply) {
|
|
12
|
-
const context = {
|
|
13
|
-
previous_reply: previousReply.slice(-MAX_REPLY_CHARS),
|
|
14
|
-
message: message.slice(0, MAX_MESSAGE_CHARS),
|
|
15
|
-
};
|
|
16
|
-
return [{ instructions: { question: SMALL, ...context } }, { instructions: { question: COMPLEX, ...context } }];
|
|
17
|
-
}
|
|
18
|
-
export async function decideTurnEffort({ message, previousReply, judge, planMode = false, leadingCommand = false, unattended = false, hasAttachments = false, }) {
|
|
19
|
-
if (planMode)
|
|
20
|
-
return { effort: DEFAULT_TURN_EFFORT, reason: "plan" };
|
|
21
|
-
if (leadingCommand)
|
|
22
|
-
return { effort: DEFAULT_TURN_EFFORT, reason: "command" };
|
|
23
|
-
if (unattended)
|
|
24
|
-
return { effort: DEFAULT_TURN_EFFORT, reason: "unattended" };
|
|
25
|
-
if (hasAttachments)
|
|
26
|
-
return { effort: DEFAULT_TURN_EFFORT, reason: "attachments" };
|
|
27
|
-
const answers = await judge(questionsFor(message, previousReply));
|
|
28
|
-
const [small, complex] = answers ?? [];
|
|
29
|
-
if (small === undefined || complex === undefined) {
|
|
30
|
-
return { effort: DEFAULT_TURN_EFFORT, reason: "unjudged" };
|
|
31
|
-
}
|
|
32
|
-
return small >= LOW_EFFORT_MIN_SMALL && complex <= LOW_EFFORT_MAX_COMPLEX
|
|
33
|
-
? { effort: "low", reason: "small", small, complex }
|
|
34
|
-
: { effort: DEFAULT_TURN_EFFORT, reason: "not_small", small, complex };
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=effort-route.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"effort-route.js","sourceRoot":"","sources":["../../src/hono/effort-route.ts"],"names":[],"mappings":"AAyBA,MAAM,CAAC,MAAM,mBAAmB,GAAa,QAAQ,CAAC;AAEtD,sEAAsE;AACtE,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAClC,uFAAuF;AACvF,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAEnC,4GAA4G;AAC5G,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAChC,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B,MAAM,KAAK,GACT,8VAA8V,CAAC;AACjW,MAAM,OAAO,GACX,gcAAgc,CAAC;AAEnc,SAAS,YAAY,CAAC,OAAe,EAAE,aAAqB;IAC1D,MAAM,OAAO,GAAG;QACd,cAAc,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC;QACrD,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC;KAC7C,CAAC;IACF,OAAO,CAAC,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;AAClH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EACrC,OAAO,EACP,aAAa,EACb,KAAK,EACL,QAAQ,GAAG,KAAK,EAChB,cAAc,GAAG,KAAK,EACtB,UAAU,GAAG,KAAK,EAClB,cAAc,GAAG,KAAK,GAavB;IACC,IAAI,QAAQ;QAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACrE,IAAI,cAAc;QAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC9E,IAAI,UAAU;QAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAC7E,IAAI,cAAc;QAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAClF,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,OAAO,IAAI,EAAE,CAAC;IACvC,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACjD,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,IAAI,oBAAoB,IAAI,OAAO,IAAI,sBAAsB;QACvE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;QACpD,CAAC,CAAC,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC3E,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"effort-route.test.d.ts","sourceRoot":"","sources":["../../src/hono/effort-route.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { decideTurnEffort } from "./effort-route.js";
|
|
3
|
-
function judgeAnswering(small, complex, asked = []) {
|
|
4
|
-
return async (questions) => {
|
|
5
|
-
asked.push([...questions]);
|
|
6
|
-
return [small, complex];
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
const turn = { message: "¿Cuál fue el CPL de ayer?", previousReply: "Listo, he revisado la campaña." };
|
|
10
|
-
describe("decideTurnEffort", () => {
|
|
11
|
-
it("runs a turn Jev clearly reads as small at low effort", async () => {
|
|
12
|
-
expect(await decideTurnEffort({ ...turn, judge: judgeAnswering(0.9, 0.1) })).toEqual({
|
|
13
|
-
effort: "low",
|
|
14
|
-
reason: "small",
|
|
15
|
-
small: 0.9,
|
|
16
|
-
complex: 0.1,
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
it("keeps medium when the turn might need judgment or several steps", async () => {
|
|
20
|
-
expect(await decideTurnEffort({ ...turn, judge: judgeAnswering(0.8, 0.45) })).toEqual({
|
|
21
|
-
effort: "medium",
|
|
22
|
-
reason: "not_small",
|
|
23
|
-
small: 0.8,
|
|
24
|
-
complex: 0.45,
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
it("keeps medium when Jev cannot answer", async () => {
|
|
28
|
-
expect(await decideTurnEffort({ ...turn, judge: async () => null })).toEqual({
|
|
29
|
-
effort: "medium",
|
|
30
|
-
reason: "unjudged",
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
// A "sí, hazlo" is short, but it approves whatever the last reply proposed.
|
|
34
|
-
it("asks about the message together with the agent's last reply", async () => {
|
|
35
|
-
const asked = [];
|
|
36
|
-
await decideTurnEffort({ ...turn, judge: judgeAnswering(0.9, 0.1, asked) });
|
|
37
|
-
expect(asked[0]).toHaveLength(2);
|
|
38
|
-
for (const question of asked[0] ?? []) {
|
|
39
|
-
expect(question.instructions).toMatchObject({ message: turn.message, previous_reply: turn.previousReply });
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
it.each([
|
|
43
|
-
["plan mode", { planMode: true }, "plan"],
|
|
44
|
-
["a leading command", { leadingCommand: true }, "command"],
|
|
45
|
-
["an unattended run", { unattended: true }, "unattended"],
|
|
46
|
-
["attachments", { hasAttachments: true }, "attachments"],
|
|
47
|
-
])("keeps medium without asking for %s", async (_label, flags, reason) => {
|
|
48
|
-
const asked = [];
|
|
49
|
-
const decision = await decideTurnEffort({ ...turn, ...flags, judge: judgeAnswering(0.99, 0, asked) });
|
|
50
|
-
expect(decision).toEqual({ effort: "medium", reason });
|
|
51
|
-
expect(asked).toHaveLength(0);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
//# sourceMappingURL=effort-route.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"effort-route.test.js","sourceRoot":"","sources":["../../src/hono/effort-route.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,SAAS,cAAc,CAAC,KAAa,EAAE,OAAe,EAAE,KAAK,GAAoB,EAAE;IACjF,OAAO,KAAK,EAAE,SAAS,EAAE,EAAE;QACzB,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,2BAA2B,EAAE,aAAa,EAAE,gCAAgC,EAAE,CAAC;AAEvG,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,CAAC,MAAM,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACnF,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,GAAG;SACb,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,CAAC,MAAM,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACpF,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,CAAC,MAAM,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3E,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,UAAU;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,4EAA4E;IAC5E,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAoB,EAAE,CAAC;QAElC,MAAM,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAE5E,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7G,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,MAAM,CAAC;QACzC,CAAC,mBAAmB,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,SAAS,CAAC;QAC1D,CAAC,mBAAmB,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,YAAY,CAAC;QACzD,CAAC,aAAa,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,EAAE,aAAa,CAAC;KACzD,CAAC,CAAC,oCAAoC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACvE,MAAM,KAAK,GAAoB,EAAE,CAAC;QAElC,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAEtG,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|