anchor-ai-sdk 0.1.9 → 0.2.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.
- package/README.md +82 -42
- package/dist/agents/coding/index.d.ts +1 -1
- package/dist/agents/coding/index.d.ts.map +1 -1
- package/dist/agents/coding/index.js +1 -1
- package/dist/agents/coding/index.js.map +1 -1
- package/dist/agents/coding/prompts/index.d.ts +11 -0
- package/dist/agents/coding/prompts/index.d.ts.map +1 -1
- package/dist/agents/coding/prompts/index.js +11 -0
- package/dist/agents/coding/prompts/index.js.map +1 -1
- package/dist/anchor.d.ts +35 -0
- package/dist/anchor.d.ts.map +1 -0
- package/dist/anchor.js +57 -0
- package/dist/anchor.js.map +1 -0
- package/dist/engine/events.d.ts +20 -2
- package/dist/engine/events.d.ts.map +1 -1
- package/dist/engine/inbox.d.ts +7 -9
- package/dist/engine/inbox.d.ts.map +1 -1
- package/dist/engine/inbox.js +10 -13
- package/dist/engine/inbox.js.map +1 -1
- package/dist/engine/loop.d.ts +4 -3
- package/dist/engine/loop.d.ts.map +1 -1
- package/dist/engine/loop.js +470 -225
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/strategy.d.ts +0 -1
- package/dist/engine/strategy.d.ts.map +1 -1
- package/dist/engine/types.d.ts +19 -11
- package/dist/engine/types.d.ts.map +1 -1
- package/dist/engine/types.js +13 -1
- package/dist/engine/types.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ledger/alignment.d.ts +1 -1
- package/dist/ledger/alignment.d.ts.map +1 -1
- package/dist/ledger/alignment.js +1 -1
- package/dist/ledger/alignment.js.map +1 -1
- package/dist/ledger/compiler.d.ts +1 -1
- package/dist/ledger/compiler.d.ts.map +1 -1
- package/dist/ledger/compiler.js +11 -5
- package/dist/ledger/compiler.js.map +1 -1
- package/dist/ledger/config.d.ts +10 -3
- package/dist/ledger/config.d.ts.map +1 -1
- package/dist/ledger/index.d.ts +1 -0
- package/dist/ledger/index.d.ts.map +1 -1
- package/dist/ledger/index.js +1 -0
- package/dist/ledger/index.js.map +1 -1
- package/dist/ledger/ledger.d.ts +2 -1
- package/dist/ledger/ledger.d.ts.map +1 -1
- package/dist/ledger/ledger.js +10 -4
- package/dist/ledger/ledger.js.map +1 -1
- package/dist/ledger/types.d.ts +5 -0
- package/dist/ledger/types.d.ts.map +1 -1
- package/dist/llm/from-catalog.d.ts +2 -0
- package/dist/llm/from-catalog.d.ts.map +1 -1
- package/dist/llm/from-catalog.js +3 -0
- package/dist/llm/from-catalog.js.map +1 -1
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +1 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/openai-compatible.d.ts +7 -1
- package/dist/llm/openai-compatible.d.ts.map +1 -1
- package/dist/llm/openai-compatible.js +45 -21
- package/dist/llm/openai-compatible.js.map +1 -1
- package/dist/llm/types.d.ts +16 -0
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/lsp/index.d.ts +10 -5
- package/dist/lsp/index.d.ts.map +1 -1
- package/dist/lsp/index.js +6 -4
- package/dist/lsp/index.js.map +1 -1
- package/dist/lsp/python.d.ts +22 -0
- package/dist/lsp/python.d.ts.map +1 -0
- package/dist/lsp/python.js +33 -0
- package/dist/lsp/python.js.map +1 -0
- package/dist/lsp/server.d.ts +184 -0
- package/dist/lsp/server.d.ts.map +1 -0
- package/dist/lsp/server.js +687 -0
- package/dist/lsp/server.js.map +1 -0
- package/dist/lsp/tools.d.ts +16 -2
- package/dist/lsp/tools.d.ts.map +1 -1
- package/dist/lsp/tools.js +565 -27
- package/dist/lsp/tools.js.map +1 -1
- package/dist/lsp/typescript.d.ts +13 -54
- package/dist/lsp/typescript.d.ts.map +1 -1
- package/dist/lsp/typescript.js +37 -262
- package/dist/lsp/typescript.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/view-image.d.ts +20 -0
- package/dist/tools/view-image.d.ts.map +1 -0
- package/dist/tools/view-image.js +111 -0
- package/dist/tools/view-image.js.map +1 -0
- package/dist/verify/index.d.ts +3 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +2 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/judge.d.ts +42 -0
- package/dist/verify/judge.d.ts.map +1 -0
- package/dist/verify/judge.js +119 -0
- package/dist/verify/judge.js.map +1 -0
- package/package.json +1 -1
package/dist/engine/loop.js
CHANGED
|
@@ -2,23 +2,22 @@ import { createFinishTool } from "../tools/finish.js";
|
|
|
2
2
|
import { BudgetTracker, DEFAULT_BUDGET_LIMITS, DEFAULT_MAX_OUTPUT_TOKENS, } from "./budgets.js";
|
|
3
3
|
import { DEFAULT_TRUNCATED_REPLY_CHARS, DEFAULT_MAX_ASSISTANT_CHARS, elideReply, spillReply, } from "./elision.js";
|
|
4
4
|
import { compactConversation, compactConversationStructured, DEFAULT_COMPACTION, estimateTokens, renderCompactionHandoff, } from "./compaction.js";
|
|
5
|
-
import { assistantMessage, conversationHistory,
|
|
6
|
-
import {
|
|
5
|
+
import { assistantMessage, conversationHistory, } from "./context.js";
|
|
6
|
+
import { renderInboxTurn, } from "./inbox.js";
|
|
7
7
|
import { defineTool } from "../tools/types.js";
|
|
8
8
|
import { gitSnapshot } from "./snapshot.js";
|
|
9
9
|
import { TraceFanout } from "../trace/sink.js";
|
|
10
10
|
import { budgetWarning, revivalBlock, warnPercent as revivalWarnPercent, } from "./revival.js";
|
|
11
11
|
import { phaseDisciplineBlock } from "./phase-discipline.js";
|
|
12
12
|
import { renderPromptBlocks } from "./prompt.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { chatAgent } from "../agents/chat/index.js";
|
|
16
|
-
import { codingAgent } from "../agents/coding/index.js";
|
|
13
|
+
import { addRequirement, compileLedger, createLedger, ledgerGate, linkEvidence, recordEvidence, renderLedger, setRequirementStatus, } from "../ledger/index.js";
|
|
14
|
+
import { llmJudge } from "../verify/index.js";
|
|
17
15
|
import { dispatchToolCalls, } from "./dispatch.js";
|
|
18
16
|
import { mintOperationId, reconcilePendingOperations, settleFromResult, } from "./operations.js";
|
|
19
17
|
import { RepeatTracker, cappedReplyNudge, createCourseCorrect, emptyTurnNudge, proseCheckpoint as proseCheckpointMessage, repeatedCallNudge, silentNudge, verboseNudge, visibleOutputTokens, } from "./guardrails.js";
|
|
20
18
|
import { pollBackgroundJobs, renderBackgroundResult, renderLostJob, waitForBackgroundJobs, } from "./background.js";
|
|
21
|
-
|
|
19
|
+
export { userTurn } from "./types.js";
|
|
20
|
+
async function runOne(input, opts, chain = {}) {
|
|
22
21
|
const strategy = opts.strategy ?? opts.agent?.strategy;
|
|
23
22
|
if (!strategy)
|
|
24
23
|
throw new Error("runAgent needs an agent or a strategy — pass opts.agent (chatAgent, codingAgent, …) or opts.strategy");
|
|
@@ -115,7 +114,11 @@ async function runOne(task, opts, chain = {}) {
|
|
|
115
114
|
!strategy.phases.some((p) => p.id === opts.startPhase)) {
|
|
116
115
|
throw new Error(`startPhase "${opts.startPhase}" is not a phase of strategy "${strategy.name}"`);
|
|
117
116
|
}
|
|
118
|
-
const
|
|
117
|
+
const requestText = input.messages
|
|
118
|
+
.filter((message) => message.role === "user" && message.content?.trim())
|
|
119
|
+
.map((message) => message.content)
|
|
120
|
+
.join("\n\n");
|
|
121
|
+
const context = input.context ?? (await strategy.prepareContext?.(opts.sandbox, requestText));
|
|
119
122
|
const prior = await opts.thread?.store.load(opts.thread.id);
|
|
120
123
|
if (!opts.startPhase &&
|
|
121
124
|
prior?.phase &&
|
|
@@ -124,50 +127,53 @@ async function runOne(task, opts, chain = {}) {
|
|
|
124
127
|
let summary = prior?.summary;
|
|
125
128
|
let compaction = prior?.compaction;
|
|
126
129
|
const budget = chain.budget ?? new BudgetTracker(opts.budgets);
|
|
127
|
-
// ---- Task
|
|
130
|
+
// ---- Task contracts and the judge --------------------------------------
|
|
128
131
|
const verifierOn = opts.verifier !== undefined && opts.verifier !== false;
|
|
129
132
|
const verifierConfig = verifierOn && typeof opts.verifier === "object" ? opts.verifier : {};
|
|
130
|
-
const verifierLlm = verifierConfig.llm ?? opts.llm;
|
|
131
133
|
const verifierGates = new Set(verifierConfig.gates ?? ["phase", "finish"]);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
+
const judge = verifierOn
|
|
135
|
+
? (verifierConfig.judge ?? llmJudge(verifierConfig.llm ?? opts.llm))
|
|
136
|
+
: undefined;
|
|
137
|
+
const maxJudgeCalls = verifierConfig.maxCallsPerTask ?? 8;
|
|
138
|
+
const maxPhaseRejections = verifierConfig.maxRejectionsPerPhase ?? 3;
|
|
139
|
+
let judgeCalls = 0;
|
|
140
|
+
let phaseRejections = 0;
|
|
141
|
+
let phaseEvidence = [];
|
|
142
|
+
const contractTaskId = opts.taskId ?? opts.thread?.id ?? `task-${Date.now()}`;
|
|
134
143
|
let ledger = opts.ledger ?? (verifierOn ? prior?.ledger : undefined);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
ledger = compiled.ledger;
|
|
144
|
-
if (compiled.usage)
|
|
145
|
-
budget.record(compiled.usage);
|
|
146
|
-
}
|
|
147
|
-
const recentEvidence = () => {
|
|
148
|
-
if (!ledger)
|
|
149
|
-
return "(none)";
|
|
150
|
-
return Object.values(ledger.evidence)
|
|
151
|
-
.slice(-6)
|
|
152
|
-
.map((item) => `${item.id} ${item.tool}${item.summary ? `: ${item.summary}` : ""}${item.ok ? "" : " (failed)"}`)
|
|
153
|
-
.join("\n");
|
|
154
|
-
};
|
|
155
|
-
const callVerifier = async (phaseGoal) => {
|
|
156
|
-
if (!verifierOn || !ledger || verifierCalls >= maxVerifierCalls) {
|
|
157
|
-
return { allow: true };
|
|
158
|
-
}
|
|
159
|
-
verifierCalls += 1;
|
|
144
|
+
// A settled ledger is history, not an active contract.
|
|
145
|
+
if (ledger && !hasActiveLedger(ledger))
|
|
146
|
+
ledger = undefined;
|
|
147
|
+
const callJudge = async (phaseGoal, requirements, agentReason) => {
|
|
148
|
+
if (!judge || !ledger || judgeCalls >= maxJudgeCalls)
|
|
149
|
+
return undefined;
|
|
150
|
+
judgeCalls += 1;
|
|
160
151
|
try {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
task: task.task,
|
|
152
|
+
const verdict = await judge.judge({
|
|
153
|
+
request: requestText,
|
|
164
154
|
phase: phaseGoal,
|
|
165
155
|
ledger,
|
|
166
|
-
|
|
167
|
-
|
|
156
|
+
evidenceSincePhase: [...phaseEvidence],
|
|
157
|
+
...(agentReason ? { agentReason } : {}),
|
|
158
|
+
}, signal);
|
|
159
|
+
if (verdict.usage)
|
|
160
|
+
budget.record(verdict.usage);
|
|
161
|
+
const wanted = new Set(requirements.map((item) => item.id));
|
|
162
|
+
const judged = verdict.requirements.filter((item) => wanted.has(item.id));
|
|
163
|
+
emit({
|
|
164
|
+
type: "phase_verdict",
|
|
165
|
+
phase: phaseGoal.id,
|
|
166
|
+
cleared: verdict.cleared,
|
|
167
|
+
confidence: verdict.confidence,
|
|
168
|
+
judge: judge.name,
|
|
169
|
+
requirements: judged.map((item) => ({
|
|
170
|
+
id: item.id,
|
|
171
|
+
status: item.status,
|
|
172
|
+
...(item.probability !== undefined
|
|
173
|
+
? { probability: item.probability }
|
|
174
|
+
: {}),
|
|
175
|
+
})),
|
|
168
176
|
});
|
|
169
|
-
if (usage)
|
|
170
|
-
budget.record(usage);
|
|
171
177
|
return verdict;
|
|
172
178
|
}
|
|
173
179
|
catch (error) {
|
|
@@ -176,112 +182,23 @@ async function runOne(task, opts, chain = {}) {
|
|
|
176
182
|
kind: "verifier_failed",
|
|
177
183
|
detail: error instanceof Error ? error.message : String(error),
|
|
178
184
|
});
|
|
179
|
-
|
|
180
|
-
allow: verifierConfig.onError !== "block",
|
|
181
|
-
aligned: false,
|
|
182
|
-
justified: true,
|
|
183
|
-
reason: "verifier unavailable",
|
|
184
|
-
requiredAction: "none",
|
|
185
|
-
confidence: 0,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
if (verifierOn && ledger) {
|
|
190
|
-
completionGate = async (summaryText) => {
|
|
191
|
-
const structural = ledgerGate(ledger, {
|
|
192
|
-
allowWaived: verifierConfig.allowWaived,
|
|
193
|
-
});
|
|
194
|
-
if (!structural.allow) {
|
|
185
|
+
if (verifierConfig.onError === "block") {
|
|
195
186
|
return {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
"Attach evidence to each requirement with update_ledger, resolve failures, and try again.",
|
|
203
|
-
].join("\n"),
|
|
187
|
+
cleared: false,
|
|
188
|
+
requirements: requirements.map((item) => ({
|
|
189
|
+
id: item.id,
|
|
190
|
+
status: "unproven",
|
|
191
|
+
})),
|
|
192
|
+
confidence: 0,
|
|
204
193
|
};
|
|
205
194
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
id: "finish",
|
|
212
|
-
goal: "Deliver the task outcome with evidence.",
|
|
213
|
-
});
|
|
214
|
-
if (verdict.allow) {
|
|
215
|
-
verified = true;
|
|
216
|
-
return { ok: true, content: "" };
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
ok: false,
|
|
220
|
-
content: `Verification gate rejected completion: ${verdict.reason ?? "unsupported claim"}. ` +
|
|
221
|
-
`Required action: ${verdict.requiredAction ?? "gather_evidence"}. ` +
|
|
222
|
-
`Claim: ${summaryText.slice(0, 400)}`,
|
|
223
|
-
};
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
const currentRequest = userMessage(task.task, task.images);
|
|
227
|
-
const revivalOn = !!opts.revival;
|
|
228
|
-
const revivalWarnAt = revivalWarnPercent(opts.revival ? opts.revival.warnAtPercent : undefined);
|
|
229
|
-
let revivalWarned = false;
|
|
230
|
-
let revivalRequest;
|
|
231
|
-
const phaseOn = strategy.phases.length > 1 && opts.intrinsics?.phase !== false;
|
|
232
|
-
/**
|
|
233
|
-
* Stable blocks are the cacheable prefix; turn-scoped blocks are appended
|
|
234
|
-
* after the conversation. The ledger changes after every tool result, so it
|
|
235
|
-
* must never live in the system message.
|
|
236
|
-
*/
|
|
237
|
-
const promptBlocks = () => [
|
|
238
|
-
{
|
|
239
|
-
id: "agent",
|
|
240
|
-
text: strategy.systemPrompt({ task: task.task, context, summary }),
|
|
241
|
-
stability: "stable",
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
id: "phase-discipline",
|
|
245
|
-
text: phaseOn ? phaseDisciplineBlock(strategy.phases) : undefined,
|
|
246
|
-
stability: "stable",
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
id: "budget-revival",
|
|
250
|
-
text: revivalOn ? revivalBlock(revivalWarnAt) : undefined,
|
|
251
|
-
stability: "stable",
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
id: "ledger",
|
|
255
|
-
text: ledger
|
|
256
|
-
? [
|
|
257
|
-
"## Runtime task state",
|
|
258
|
-
"This is authoritative state, not a new user request.",
|
|
259
|
-
renderLedger(ledger),
|
|
260
|
-
"Use update_ledger to link evidence to each requirement and mark it verified. Do not claim completion while a required item is open or lacks evidence.",
|
|
261
|
-
].join("\n\n")
|
|
262
|
-
: undefined,
|
|
263
|
-
stability: "turn",
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
id: "compaction",
|
|
267
|
-
text: compaction ? renderCompactionHandoff(compaction) : undefined,
|
|
268
|
-
stability: "turn",
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
id: "phase-state",
|
|
272
|
-
text: phase ? `Current phase: ${phase}` : undefined,
|
|
273
|
-
stability: "turn",
|
|
274
|
-
},
|
|
275
|
-
];
|
|
276
|
-
const system = () => ({
|
|
277
|
-
role: "system",
|
|
278
|
-
content: renderPromptBlocks(promptBlocks(), "stable"),
|
|
279
|
-
});
|
|
280
|
-
const turnState = () => renderPromptBlocks(promptBlocks(), "turn");
|
|
281
|
-
if (ledger) {
|
|
282
|
-
const updateLedger = defineTool({
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const updateLedger = () => {
|
|
199
|
+
const tool = defineTool({
|
|
283
200
|
name: "update_ledger",
|
|
284
|
-
description: "Update the task contract.
|
|
201
|
+
description: "Update the task contract. Link a tool result to a requirement with a claim, verify it, record an open failure, or set the next step. Never invent evidence ids.",
|
|
285
202
|
parameters: {
|
|
286
203
|
type: "object",
|
|
287
204
|
properties: {
|
|
@@ -290,6 +207,7 @@ async function runOne(task, opts, chain = {}) {
|
|
|
290
207
|
enum: [
|
|
291
208
|
"add_requirement",
|
|
292
209
|
"link_evidence",
|
|
210
|
+
"verify",
|
|
293
211
|
"set_status",
|
|
294
212
|
"set_next_step",
|
|
295
213
|
"add_failure",
|
|
@@ -298,8 +216,10 @@ async function runOne(task, opts, chain = {}) {
|
|
|
298
216
|
},
|
|
299
217
|
text: { type: "string" },
|
|
300
218
|
required: { type: "boolean" },
|
|
219
|
+
phase: { type: "string" },
|
|
301
220
|
requirementId: { type: "string" },
|
|
302
221
|
evidenceId: { type: "string" },
|
|
222
|
+
claim: { type: "string" },
|
|
303
223
|
status: {
|
|
304
224
|
type: "string",
|
|
305
225
|
enum: ["open", "in_progress", "verified", "blocked", "waived"],
|
|
@@ -315,16 +235,42 @@ async function runOne(task, opts, chain = {}) {
|
|
|
315
235
|
const text = String(input.text ?? "").trim();
|
|
316
236
|
if (!text)
|
|
317
237
|
return { ok: false, content: "text is required" };
|
|
318
|
-
const requirement = addRequirement(ledger, {
|
|
238
|
+
const requirement = addRequirement(ledger, {
|
|
239
|
+
text,
|
|
240
|
+
required: input.required !== false,
|
|
241
|
+
...(typeof input.phase === "string" && input.phase
|
|
242
|
+
? { phase: input.phase }
|
|
243
|
+
: {}),
|
|
244
|
+
}, "agent");
|
|
319
245
|
return {
|
|
320
246
|
ok: true,
|
|
321
247
|
content: `added ${requirement.id}: ${requirement.text}`,
|
|
322
248
|
};
|
|
323
249
|
}
|
|
324
250
|
if (op === "link_evidence") {
|
|
325
|
-
|
|
251
|
+
const claim = String(input.claim ?? "").trim();
|
|
252
|
+
if (!claim) {
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
content: "claim is required: explain why this evidence establishes the requirement",
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
linkEvidence(ledger, String(input.requirementId ?? ""), String(input.evidenceId ?? ""), claim);
|
|
326
259
|
return { ok: true, content: "evidence linked" };
|
|
327
260
|
}
|
|
261
|
+
if (op === "verify") {
|
|
262
|
+
const claim = String(input.claim ?? "").trim();
|
|
263
|
+
if (!claim) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
content: "claim is required: explain why this evidence establishes the requirement",
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const requirementId = String(input.requirementId ?? "");
|
|
270
|
+
linkEvidence(ledger, requirementId, String(input.evidenceId ?? ""), claim);
|
|
271
|
+
setRequirementStatus(ledger, requirementId, "verified");
|
|
272
|
+
return { ok: true, content: "evidence linked and requirement verified" };
|
|
273
|
+
}
|
|
328
274
|
if (op === "set_status") {
|
|
329
275
|
const status = String(input.status ?? "");
|
|
330
276
|
if (status === "superseded") {
|
|
@@ -361,12 +307,167 @@ async function runOne(task, opts, chain = {}) {
|
|
|
361
307
|
}
|
|
362
308
|
return { ok: false, content: `unknown ledger op "${op}"` };
|
|
363
309
|
}, { verb: "Updating the progress ledger" });
|
|
364
|
-
available.set("update_ledger",
|
|
310
|
+
available.set("update_ledger", tool);
|
|
311
|
+
return tool;
|
|
312
|
+
};
|
|
313
|
+
const installCompletionGate = () => {
|
|
314
|
+
completionGate = async (summaryText, success) => {
|
|
315
|
+
const active = ledger;
|
|
316
|
+
const structural = ledgerGate(active, {
|
|
317
|
+
allowWaived: verifierConfig.allowWaived,
|
|
318
|
+
});
|
|
319
|
+
if (!structural.allow) {
|
|
320
|
+
if (!success) {
|
|
321
|
+
// The agent explicitly reported a blocked/unsuccessful outcome. End
|
|
322
|
+
// honestly instead of forcing it into the stalled path.
|
|
323
|
+
for (const id of [...structural.open, ...structural.missingEvidence]) {
|
|
324
|
+
const requirement = active.requirements.find((item) => item.id === id);
|
|
325
|
+
if (requirement) {
|
|
326
|
+
const note = `${id}: ${requirement.text}`;
|
|
327
|
+
if (!active.openFailures.includes(note))
|
|
328
|
+
active.openFailures.push(note);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
for (const failure of structural.failures) {
|
|
332
|
+
if (!active.openFailures.includes(failure)) {
|
|
333
|
+
active.openFailures.push(failure);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { ok: true, content: "" };
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
ok: false,
|
|
340
|
+
content: [
|
|
341
|
+
"Verification gate: required work is not complete.",
|
|
342
|
+
`open: ${structural.open.join(", ") || "none"}`,
|
|
343
|
+
`missingEvidence: ${structural.missingEvidence.join(", ") || "none"}`,
|
|
344
|
+
`failures: ${structural.failures.join(" | ") || "none"}`,
|
|
345
|
+
"Attach evidence to each requirement with update_ledger, resolve failures, and try again.",
|
|
346
|
+
].join("\n"),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
if (!success)
|
|
350
|
+
return { ok: true, content: "" };
|
|
351
|
+
if (!verifierGates.has("finish")) {
|
|
352
|
+
verified = true;
|
|
353
|
+
return { ok: true, content: "" };
|
|
354
|
+
}
|
|
355
|
+
const required = active.requirements.filter((item) => item.required && item.status !== "superseded");
|
|
356
|
+
const verdict = await callJudge({ id: "finish", goal: "Deliver the task outcome with evidence." }, required, summaryText);
|
|
357
|
+
if (!verdict) {
|
|
358
|
+
verified = verifierConfig.onError !== "block";
|
|
359
|
+
return verified
|
|
360
|
+
? { ok: true, content: "" }
|
|
361
|
+
: {
|
|
362
|
+
ok: false,
|
|
363
|
+
content: "Verification gate unavailable and onError is block; retry the judge before finishing.",
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
const wanted = new Set(required.map((item) => item.id));
|
|
367
|
+
const unmet = verdict.requirements.filter((item) => wanted.has(item.id) && item.status !== "cleared");
|
|
368
|
+
if (verdict.cleared && unmet.length === 0) {
|
|
369
|
+
verified = true;
|
|
370
|
+
return { ok: true, content: "" };
|
|
371
|
+
}
|
|
372
|
+
const details = unmet.map((item) => {
|
|
373
|
+
const requirement = active.requirements.find((r) => r.id === item.id);
|
|
374
|
+
const claim = requirement?.evidenceIds
|
|
375
|
+
.map((id) => active.evidence[id]?.claim)
|
|
376
|
+
.find(Boolean);
|
|
377
|
+
const probability = item.probability !== undefined
|
|
378
|
+
? ` p=${item.probability.toFixed(2)}`
|
|
379
|
+
: "";
|
|
380
|
+
return `${item.id}${probability} ${item.status}: ${requirement?.text ?? "unknown requirement"}${claim ? `; claim: ${claim}` : ""}`;
|
|
381
|
+
});
|
|
382
|
+
return {
|
|
383
|
+
ok: false,
|
|
384
|
+
content: `Verification gate rejected completion: ${verdict.rationale ?? "unsupported claim"}.\n` +
|
|
385
|
+
(details.length
|
|
386
|
+
? `Unmet requirements:\n${details.join("\n")}`
|
|
387
|
+
: "The judge did not clear the contract."),
|
|
388
|
+
};
|
|
389
|
+
};
|
|
390
|
+
};
|
|
391
|
+
const attachLedger = (next) => {
|
|
392
|
+
ledger = next;
|
|
393
|
+
phaseRejections = 0;
|
|
394
|
+
phaseEvidence = [];
|
|
395
|
+
updateLedger();
|
|
396
|
+
installCompletionGate();
|
|
397
|
+
};
|
|
398
|
+
if (verifierOn && ledger)
|
|
399
|
+
attachLedger(ledger);
|
|
400
|
+
else if (verifierOn && verifierConfig.create === "start") {
|
|
401
|
+
const compiled = await compileLedger({
|
|
402
|
+
llm: verifierConfig.llm ?? opts.llm,
|
|
403
|
+
taskId: contractTaskId,
|
|
404
|
+
source: requestText,
|
|
405
|
+
phases: strategy.phases,
|
|
406
|
+
reasoningEffort: "low",
|
|
407
|
+
});
|
|
408
|
+
if (compiled.usage)
|
|
409
|
+
budget.record(compiled.usage);
|
|
410
|
+
attachLedger(compiled.ledger);
|
|
365
411
|
}
|
|
412
|
+
const currentRequests = input.messages;
|
|
413
|
+
const revivalOn = !!opts.revival;
|
|
414
|
+
const revivalWarnAt = revivalWarnPercent(opts.revival ? opts.revival.warnAtPercent : undefined);
|
|
415
|
+
let revivalWarned = false;
|
|
416
|
+
let revivalRequest;
|
|
417
|
+
const phaseOn = strategy.phases.length > 1 && opts.intrinsics?.phase !== false;
|
|
418
|
+
/**
|
|
419
|
+
* Stable blocks are the cacheable prefix; turn-scoped blocks are appended
|
|
420
|
+
* after the conversation. The ledger changes after every tool result, so it
|
|
421
|
+
* must never live in the system message.
|
|
422
|
+
*/
|
|
423
|
+
const promptBlocks = () => [
|
|
424
|
+
{
|
|
425
|
+
id: "agent",
|
|
426
|
+
text: strategy.systemPrompt({ context, summary }),
|
|
427
|
+
stability: "stable",
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: "phase-discipline",
|
|
431
|
+
text: phaseOn ? phaseDisciplineBlock(strategy.phases) : undefined,
|
|
432
|
+
stability: "stable",
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
id: "budget-revival",
|
|
436
|
+
text: revivalOn ? revivalBlock(revivalWarnAt) : undefined,
|
|
437
|
+
stability: "stable",
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
id: "ledger",
|
|
441
|
+
text: ledger
|
|
442
|
+
? [
|
|
443
|
+
"## Runtime task state",
|
|
444
|
+
"This is authoritative state, not a new user request.",
|
|
445
|
+
renderLedger(ledger),
|
|
446
|
+
"Use update_ledger to link evidence to each requirement and mark it verified. Do not claim completion while a required item is open or lacks evidence.",
|
|
447
|
+
].join("\n\n")
|
|
448
|
+
: undefined,
|
|
449
|
+
stability: "turn",
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
id: "compaction",
|
|
453
|
+
text: compaction ? renderCompactionHandoff(compaction) : undefined,
|
|
454
|
+
stability: "turn",
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
id: "phase-state",
|
|
458
|
+
text: phase ? `Current phase: ${phase}` : undefined,
|
|
459
|
+
stability: "turn",
|
|
460
|
+
},
|
|
461
|
+
];
|
|
462
|
+
const system = () => ({
|
|
463
|
+
role: "system",
|
|
464
|
+
content: renderPromptBlocks(promptBlocks(), "stable"),
|
|
465
|
+
});
|
|
466
|
+
const turnState = () => renderPromptBlocks(promptBlocks(), "turn");
|
|
366
467
|
const messages = [
|
|
367
468
|
system(),
|
|
368
469
|
...conversationHistory(prior?.messages ?? []),
|
|
369
|
-
|
|
470
|
+
...currentRequests,
|
|
370
471
|
];
|
|
371
472
|
const compact = opts.compaction === false
|
|
372
473
|
? null
|
|
@@ -391,6 +492,29 @@ async function runOne(task, opts, chain = {}) {
|
|
|
391
492
|
}
|
|
392
493
|
return out;
|
|
393
494
|
};
|
|
495
|
+
/**
|
|
496
|
+
* Cache the stable prefix, never the turn-scoped block. The adapter only
|
|
497
|
+
* emits `cache_control` when the consumer enabled `promptCache`.
|
|
498
|
+
*/
|
|
499
|
+
const cacheBreakpointsFor = (modelMessages) => {
|
|
500
|
+
const markable = (message) => !!message &&
|
|
501
|
+
(message.role === "system" ||
|
|
502
|
+
message.role === "user" ||
|
|
503
|
+
(message.role === "assistant" && !!message.content));
|
|
504
|
+
const indexes = new Set();
|
|
505
|
+
if (markable(modelMessages[0]))
|
|
506
|
+
indexes.add(0);
|
|
507
|
+
const turn = turnState();
|
|
508
|
+
const turnIndex = turn
|
|
509
|
+
? modelMessages.findIndex((message) => message.role === "user" && message.content === turn)
|
|
510
|
+
: -1;
|
|
511
|
+
let end = turnIndex > 0 ? turnIndex - 1 : modelMessages.length - 1;
|
|
512
|
+
while (end >= 0 && !markable(modelMessages[end]))
|
|
513
|
+
end--;
|
|
514
|
+
if (end >= 0)
|
|
515
|
+
indexes.add(end);
|
|
516
|
+
return [...indexes];
|
|
517
|
+
};
|
|
394
518
|
const evidence = [];
|
|
395
519
|
// Commands the provider detached. The engine owns their results: it polls at
|
|
396
520
|
// every turn boundary and, before a normal finish, waits for what is left.
|
|
@@ -571,31 +695,76 @@ async function runOne(task, opts, chain = {}) {
|
|
|
571
695
|
content: `Unknown phase. Available: ${strategy.phases.map((item) => item.id).join(", ")}.`,
|
|
572
696
|
};
|
|
573
697
|
const previous = phase;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
return {
|
|
586
|
-
ok: false,
|
|
587
|
-
content: [
|
|
588
|
-
`Phase transition rejected: ${verdict.reason}`,
|
|
589
|
-
`Required action: ${verdict.requiredAction}`,
|
|
590
|
-
"Gather the missing evidence or update the ledger, then transition again.",
|
|
591
|
-
].join("\n"),
|
|
698
|
+
let forced = false;
|
|
699
|
+
if (target.id !== previous && ledger && verifierGates.has("phase")) {
|
|
700
|
+
const owned = ledger.requirements.filter((item) => item.required &&
|
|
701
|
+
item.phase === previous &&
|
|
702
|
+
item.status !== "verified" &&
|
|
703
|
+
item.status !== "waived" &&
|
|
704
|
+
item.status !== "superseded");
|
|
705
|
+
if (owned.length > 0) {
|
|
706
|
+
const outgoing = strategy.phases.find((item) => item.id === previous) ?? {
|
|
707
|
+
id: previous,
|
|
708
|
+
goal: previous,
|
|
592
709
|
};
|
|
710
|
+
const verdict = await callJudge(outgoing, owned, typeof input.reason === "string" ? input.reason : undefined);
|
|
711
|
+
const wanted = new Set(owned.map((item) => item.id));
|
|
712
|
+
const unmet = verdict
|
|
713
|
+
? verdict.requirements.filter((item) => wanted.has(item.id) && item.status !== "cleared")
|
|
714
|
+
: [];
|
|
715
|
+
if (verdict && (!verdict.cleared || unmet.length > 0)) {
|
|
716
|
+
phaseRejections += 1;
|
|
717
|
+
const details = unmet.map((item) => {
|
|
718
|
+
const requirement = ledger.requirements.find((row) => row.id === item.id);
|
|
719
|
+
const claim = requirement?.evidenceIds
|
|
720
|
+
.map((id) => ledger.evidence[id]?.claim)
|
|
721
|
+
.find(Boolean);
|
|
722
|
+
const probability = item.probability !== undefined
|
|
723
|
+
? ` p=${item.probability.toFixed(2)}`
|
|
724
|
+
: "";
|
|
725
|
+
return `${item.id}${probability} ${item.status}: ${requirement?.text ?? "unknown requirement"}${claim ? `; claim: ${claim}` : ""}`;
|
|
726
|
+
});
|
|
727
|
+
const detail = `Phase ${previous} has unmet requirements.` +
|
|
728
|
+
(details.length ? `\n${details.join("\n")}` : "");
|
|
729
|
+
emit({
|
|
730
|
+
type: "guardrail",
|
|
731
|
+
kind: "transition_rejected",
|
|
732
|
+
detail: `${previous} -> ${target.id}: ${detail}`,
|
|
733
|
+
});
|
|
734
|
+
if (phaseRejections <= maxPhaseRejections) {
|
|
735
|
+
return {
|
|
736
|
+
ok: false,
|
|
737
|
+
content: [
|
|
738
|
+
`Phase transition rejected: ${previous} is not complete.`,
|
|
739
|
+
...(details.length ? details : ["The judge did not clear the phase."]),
|
|
740
|
+
`Stay in ${previous}, finish the owned requirements, then transition again.`,
|
|
741
|
+
].join("\n"),
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
for (const item of owned) {
|
|
745
|
+
if (item.status !== "blocked" && item.status !== "verified") {
|
|
746
|
+
setRequirementStatus(ledger, item.id, "in_progress");
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
forced = true;
|
|
750
|
+
emit({
|
|
751
|
+
type: "guardrail",
|
|
752
|
+
kind: "transition_rejected",
|
|
753
|
+
detail: `forcing ${previous} -> ${target.id} after ${phaseRejections} rejection(s); requirements remain open and finish is still gated`,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
593
756
|
}
|
|
594
757
|
}
|
|
595
758
|
phase = target.id;
|
|
759
|
+
phaseRejections = 0;
|
|
760
|
+
phaseEvidence = [];
|
|
596
761
|
pauseRequested =
|
|
597
762
|
opts.pauseAfterPhase === previous && previous !== phase;
|
|
598
|
-
emit({
|
|
763
|
+
emit({
|
|
764
|
+
type: "phase_started",
|
|
765
|
+
phase,
|
|
766
|
+
...(forced ? { via: "advance_forced" } : {}),
|
|
767
|
+
});
|
|
599
768
|
return {
|
|
600
769
|
ok: true,
|
|
601
770
|
content: `Current approach: ${target.id} — ${target.goal}`,
|
|
@@ -650,6 +819,122 @@ async function runOne(task, opts, chain = {}) {
|
|
|
650
819
|
};
|
|
651
820
|
}, { verb: "Requesting budget for the remaining work" }));
|
|
652
821
|
}
|
|
822
|
+
if (verifierOn && opts.intrinsics?.task !== false) {
|
|
823
|
+
available.set("create_task", defineTool({
|
|
824
|
+
name: "create_task",
|
|
825
|
+
description: "Create the task contract for this turn when the user asked for real work. " +
|
|
826
|
+
"State a measurable goal and 1-12 independently checkable requirements, each assigned " +
|
|
827
|
+
"to one of the strategy's phases. Do not include a source field; the engine derives " +
|
|
828
|
+
"the authoritative request from the user's messages.",
|
|
829
|
+
parameters: {
|
|
830
|
+
type: "object",
|
|
831
|
+
properties: {
|
|
832
|
+
goal: { type: "string" },
|
|
833
|
+
requirements: {
|
|
834
|
+
type: "array",
|
|
835
|
+
minItems: 1,
|
|
836
|
+
maxItems: 12,
|
|
837
|
+
items: {
|
|
838
|
+
type: "object",
|
|
839
|
+
properties: {
|
|
840
|
+
text: { type: "string" },
|
|
841
|
+
required: { type: "boolean" },
|
|
842
|
+
phase: {
|
|
843
|
+
type: "string",
|
|
844
|
+
enum: strategy.phases.map((item) => item.id),
|
|
845
|
+
},
|
|
846
|
+
sourceQuote: { type: "string" },
|
|
847
|
+
},
|
|
848
|
+
required: ["text", "phase"],
|
|
849
|
+
additionalProperties: false,
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
nextStep: { type: "string" },
|
|
853
|
+
},
|
|
854
|
+
required: ["goal", "requirements"],
|
|
855
|
+
additionalProperties: false,
|
|
856
|
+
},
|
|
857
|
+
}, async (input) => {
|
|
858
|
+
if (ledger) {
|
|
859
|
+
return {
|
|
860
|
+
ok: false,
|
|
861
|
+
content: "the contract is already active; use update_ledger to change its state",
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
const goal = String(input.goal ?? "").trim();
|
|
865
|
+
if (!goal)
|
|
866
|
+
return { ok: false, content: "goal is required" };
|
|
867
|
+
const rows = Array.isArray(input.requirements)
|
|
868
|
+
? input.requirements
|
|
869
|
+
: [];
|
|
870
|
+
if (rows.length < 1 || rows.length > 12) {
|
|
871
|
+
return {
|
|
872
|
+
ok: false,
|
|
873
|
+
content: "requirements must contain 1-12 items",
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
const phaseIds = new Set(strategy.phases.map((item) => item.id));
|
|
877
|
+
const requirements = [];
|
|
878
|
+
for (const [index, row] of rows.entries()) {
|
|
879
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
880
|
+
return {
|
|
881
|
+
ok: false,
|
|
882
|
+
content: `requirement ${index} must be an object`,
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
const entry = row;
|
|
886
|
+
const text = String(entry.text ?? "").trim();
|
|
887
|
+
const phase = String(entry.phase ?? "");
|
|
888
|
+
if (!text) {
|
|
889
|
+
return { ok: false, content: `requirement ${index} needs text` };
|
|
890
|
+
}
|
|
891
|
+
if (!phaseIds.has(phase)) {
|
|
892
|
+
return {
|
|
893
|
+
ok: false,
|
|
894
|
+
content: `requirement ${index} has unknown phase "${phase}"`,
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
const sourceQuote = typeof entry.sourceQuote === "string"
|
|
898
|
+
? entry.sourceQuote
|
|
899
|
+
: undefined;
|
|
900
|
+
if (sourceQuote && !requestText.includes(sourceQuote)) {
|
|
901
|
+
return {
|
|
902
|
+
ok: false,
|
|
903
|
+
content: `requirement ${index} sourceQuote is not in the user's request`,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
requirements.push({
|
|
907
|
+
text,
|
|
908
|
+
required: entry.required !== false,
|
|
909
|
+
phase,
|
|
910
|
+
...(sourceQuote ? { sourceQuote } : {}),
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
const next = createLedger({
|
|
914
|
+
taskId: contractTaskId,
|
|
915
|
+
source: { kind: "user", text: requestText },
|
|
916
|
+
goal,
|
|
917
|
+
requirements: requirements.map((item) => ({
|
|
918
|
+
...item,
|
|
919
|
+
origin: "agent",
|
|
920
|
+
})),
|
|
921
|
+
});
|
|
922
|
+
if (typeof input.nextStep === "string" && input.nextStep.trim()) {
|
|
923
|
+
next.nextStep = input.nextStep.trim();
|
|
924
|
+
}
|
|
925
|
+
attachLedger(next);
|
|
926
|
+
emit({ type: "task_created", ledger: next });
|
|
927
|
+
return {
|
|
928
|
+
ok: true,
|
|
929
|
+
content: [
|
|
930
|
+
`Contract created: ${next.goal}`,
|
|
931
|
+
...next.requirements.map((item) => `${item.id}: ${item.text} (phase ${item.phase ?? "any"})`),
|
|
932
|
+
"Use update_ledger to link evidence with a claim and verify each requirement.",
|
|
933
|
+
].join("\n"),
|
|
934
|
+
meta: { ledger: next },
|
|
935
|
+
};
|
|
936
|
+
}, { verb: "Creating the task contract" }));
|
|
937
|
+
}
|
|
653
938
|
/**
|
|
654
939
|
* Settle operations only AFTER the conversation that contains their results
|
|
655
940
|
* is durable. The opposite order opens the window this whole layer exists to
|
|
@@ -848,7 +1133,8 @@ async function runOne(task, opts, chain = {}) {
|
|
|
848
1133
|
}
|
|
849
1134
|
emit({
|
|
850
1135
|
type: "run_started",
|
|
851
|
-
|
|
1136
|
+
text: requestText,
|
|
1137
|
+
...(input.messages.length ? { messages: input.messages } : {}),
|
|
852
1138
|
phases: strategy.phases.map((item) => item.id),
|
|
853
1139
|
});
|
|
854
1140
|
if (chain.delivered?.length)
|
|
@@ -1041,7 +1327,7 @@ async function runOne(task, opts, chain = {}) {
|
|
|
1041
1327
|
};
|
|
1042
1328
|
// Preserve the actual current request even if its earlier position
|
|
1043
1329
|
// was folded. Do not replace it with a generic "Begin" instruction.
|
|
1044
|
-
messages.splice(0, messages.length, system(), ...(tail.includes(
|
|
1330
|
+
messages.splice(0, messages.length, system(), ...currentRequests.filter((message) => !tail.includes(message)), foldNote, ...tail);
|
|
1045
1331
|
emit({
|
|
1046
1332
|
type: "compacted",
|
|
1047
1333
|
removedMessages: folded.removedCount,
|
|
@@ -1067,8 +1353,10 @@ async function runOne(task, opts, chain = {}) {
|
|
|
1067
1353
|
await pollJobs();
|
|
1068
1354
|
steps++;
|
|
1069
1355
|
emit({ type: "llm_call_started", phase, step: steps });
|
|
1356
|
+
const modelMessages = forModel();
|
|
1070
1357
|
const response = await opts.llm.stream({
|
|
1071
|
-
messages:
|
|
1358
|
+
messages: modelMessages,
|
|
1359
|
+
cacheBreakpoints: cacheBreakpointsFor(modelMessages),
|
|
1072
1360
|
tools: [...available.values()].map((tool) => ({
|
|
1073
1361
|
type: "function",
|
|
1074
1362
|
function: tool.definition,
|
|
@@ -1257,13 +1545,14 @@ async function runOne(task, opts, chain = {}) {
|
|
|
1257
1545
|
call.name === "update_ledger") {
|
|
1258
1546
|
return;
|
|
1259
1547
|
}
|
|
1260
|
-
recordEvidence(ledger, {
|
|
1548
|
+
const evidenceRef = recordEvidence(ledger, {
|
|
1261
1549
|
tool: call.name,
|
|
1262
1550
|
ref: `call:${call.id}`,
|
|
1263
1551
|
ok: result.ok,
|
|
1264
1552
|
summary: result.content.replace(/\s+/g, " ").slice(0, 500),
|
|
1265
1553
|
...(result.meta ? { meta: result.meta } : {}),
|
|
1266
1554
|
});
|
|
1555
|
+
phaseEvidence.push(evidenceRef);
|
|
1267
1556
|
},
|
|
1268
1557
|
});
|
|
1269
1558
|
runaway = outcome.runaway;
|
|
@@ -1398,48 +1687,7 @@ function usageDelta(now, before) {
|
|
|
1398
1687
|
function hasActiveLedger(ledger) {
|
|
1399
1688
|
if (!ledger)
|
|
1400
1689
|
return false;
|
|
1401
|
-
|
|
1402
|
-
item.status !== "verified" &&
|
|
1403
|
-
item.status !== "waived" &&
|
|
1404
|
-
item.status !== "superseded");
|
|
1405
|
-
return open || ledger.openFailures.length > 0;
|
|
1406
|
-
}
|
|
1407
|
-
async function priorLedgerFor(opts, verifierOn) {
|
|
1408
|
-
if (!verifierOn)
|
|
1409
|
-
return undefined;
|
|
1410
|
-
if (opts.ledger)
|
|
1411
|
-
return opts.ledger;
|
|
1412
|
-
if (!opts.thread)
|
|
1413
|
-
return undefined;
|
|
1414
|
-
return (await opts.thread.store.load(opts.thread.id))?.ledger;
|
|
1415
|
-
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Resolve the route for one run. `intent: "auto"` is ledger-aware: an active
|
|
1418
|
-
* task contract outranks text heuristics. Explicit chat/clarify/task still
|
|
1419
|
-
* wins, and turns without verifier mode keep their existing behavior.
|
|
1420
|
-
*/
|
|
1421
|
-
function routeTurn(task, opts, priorLedger) {
|
|
1422
|
-
const verifierOn = opts.verifier !== undefined && opts.verifier !== false;
|
|
1423
|
-
if (!verifierOn)
|
|
1424
|
-
return { ...opts, agent: opts.agent ?? chatAgent };
|
|
1425
|
-
const explicit = opts.intent ?? "auto";
|
|
1426
|
-
const intent = explicit === "auto" && hasActiveLedger(priorLedger)
|
|
1427
|
-
? "task"
|
|
1428
|
-
: resolveTurnIntent(task.task, explicit);
|
|
1429
|
-
if (intent !== "task") {
|
|
1430
|
-
return {
|
|
1431
|
-
...opts,
|
|
1432
|
-
agent: chatAgent,
|
|
1433
|
-
verifier: false,
|
|
1434
|
-
intent: "chat",
|
|
1435
|
-
ledger: undefined,
|
|
1436
|
-
};
|
|
1437
|
-
}
|
|
1438
|
-
return {
|
|
1439
|
-
...opts,
|
|
1440
|
-
agent: opts.agent ?? codingAgent,
|
|
1441
|
-
intent: "task",
|
|
1442
|
-
};
|
|
1690
|
+
return !ledgerGate(ledger).allow;
|
|
1443
1691
|
}
|
|
1444
1692
|
/**
|
|
1445
1693
|
* A run chain (decision #82): one run at a time, with the consumer's inbox as
|
|
@@ -1451,7 +1699,7 @@ function routeTurn(task, opts, priorLedger) {
|
|
|
1451
1699
|
* cancellation, a caller-requested pause, or an exhausted budget — and returns
|
|
1452
1700
|
* the last run's result with every run summarized on `.runs`.
|
|
1453
1701
|
*/
|
|
1454
|
-
async function runChain(
|
|
1702
|
+
async function runChain(input, opts) {
|
|
1455
1703
|
const inbox = opts.inbox;
|
|
1456
1704
|
const { store, channel } = inbox;
|
|
1457
1705
|
if (!channel)
|
|
@@ -1470,14 +1718,13 @@ async function runChain(task, opts) {
|
|
|
1470
1718
|
};
|
|
1471
1719
|
let stepsBefore = 0;
|
|
1472
1720
|
let jobs;
|
|
1473
|
-
let current =
|
|
1721
|
+
let current = input;
|
|
1474
1722
|
let delivered;
|
|
1475
1723
|
let claimed = [];
|
|
1476
1724
|
let result;
|
|
1477
1725
|
for (;;) {
|
|
1478
1726
|
const budget = seededBudget(opts, spend);
|
|
1479
|
-
|
|
1480
|
-
result = await runOne(current, routed, {
|
|
1727
|
+
result = await runOne(current, opts, {
|
|
1481
1728
|
budget,
|
|
1482
1729
|
stepsBefore,
|
|
1483
1730
|
jobs,
|
|
@@ -1538,17 +1785,15 @@ async function runChain(task, opts) {
|
|
|
1538
1785
|
return { ...result, runs };
|
|
1539
1786
|
claimed = batch.map((message) => message.id);
|
|
1540
1787
|
delivered = batch;
|
|
1541
|
-
current = inbox.render ? inbox.render(batch) :
|
|
1788
|
+
current = inbox.render ? inbox.render(batch) : renderInboxTurn(batch);
|
|
1542
1789
|
}
|
|
1543
1790
|
// The zero-progress guard fired. Report the last run as it stands; `.runs`
|
|
1544
1791
|
// shows how many interruptions it took to get here.
|
|
1545
1792
|
return { ...result, runs };
|
|
1546
1793
|
}
|
|
1547
|
-
export async function runAgent(
|
|
1794
|
+
export async function runAgent(input, opts) {
|
|
1548
1795
|
if (opts.inbox)
|
|
1549
|
-
return runChain(
|
|
1550
|
-
|
|
1551
|
-
const priorLedger = await priorLedgerFor(opts, verifierOn);
|
|
1552
|
-
return runOne(task, routeTurn(task, opts, priorLedger));
|
|
1796
|
+
return runChain(input, opts);
|
|
1797
|
+
return runOne(input, opts);
|
|
1553
1798
|
}
|
|
1554
1799
|
//# sourceMappingURL=loop.js.map
|