anchor-ai-sdk 0.1.8 → 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.
Files changed (144) hide show
  1. package/README.md +186 -59
  2. package/dist/agents/coding/index.d.ts +1 -1
  3. package/dist/agents/coding/index.d.ts.map +1 -1
  4. package/dist/agents/coding/index.js +1 -1
  5. package/dist/agents/coding/index.js.map +1 -1
  6. package/dist/agents/coding/prompts/index.d.ts +11 -0
  7. package/dist/agents/coding/prompts/index.d.ts.map +1 -1
  8. package/dist/agents/coding/prompts/index.js +11 -0
  9. package/dist/agents/coding/prompts/index.js.map +1 -1
  10. package/dist/anchor.d.ts +35 -0
  11. package/dist/anchor.d.ts.map +1 -0
  12. package/dist/anchor.js +57 -0
  13. package/dist/anchor.js.map +1 -0
  14. package/dist/engine/compaction.d.ts +48 -1
  15. package/dist/engine/compaction.d.ts.map +1 -1
  16. package/dist/engine/compaction.js +210 -0
  17. package/dist/engine/compaction.js.map +1 -1
  18. package/dist/engine/context.d.ts +1 -1
  19. package/dist/engine/context.d.ts.map +1 -1
  20. package/dist/engine/context.js +6 -2
  21. package/dist/engine/context.js.map +1 -1
  22. package/dist/engine/events.d.ts +23 -2
  23. package/dist/engine/events.d.ts.map +1 -1
  24. package/dist/engine/inbox.d.ts +9 -8
  25. package/dist/engine/inbox.d.ts.map +1 -1
  26. package/dist/engine/inbox.js +13 -11
  27. package/dist/engine/inbox.js.map +1 -1
  28. package/dist/engine/loop.d.ts +4 -3
  29. package/dist/engine/loop.d.ts.map +1 -1
  30. package/dist/engine/loop.js +542 -179
  31. package/dist/engine/loop.js.map +1 -1
  32. package/dist/engine/prompt.d.ts +16 -0
  33. package/dist/engine/prompt.d.ts.map +1 -0
  34. package/dist/engine/prompt.js +8 -0
  35. package/dist/engine/prompt.js.map +1 -0
  36. package/dist/engine/strategy.d.ts +0 -1
  37. package/dist/engine/strategy.d.ts.map +1 -1
  38. package/dist/engine/thread.d.ts +4 -1
  39. package/dist/engine/thread.d.ts.map +1 -1
  40. package/dist/engine/thread.js.map +1 -1
  41. package/dist/engine/types.d.ts +25 -14
  42. package/dist/engine/types.d.ts.map +1 -1
  43. package/dist/engine/types.js +13 -1
  44. package/dist/engine/types.js.map +1 -1
  45. package/dist/index.d.ts +4 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +3 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/ledger/alignment.d.ts +1 -1
  50. package/dist/ledger/alignment.d.ts.map +1 -1
  51. package/dist/ledger/alignment.js +1 -1
  52. package/dist/ledger/alignment.js.map +1 -1
  53. package/dist/ledger/compiler.d.ts +1 -1
  54. package/dist/ledger/compiler.d.ts.map +1 -1
  55. package/dist/ledger/compiler.js +11 -5
  56. package/dist/ledger/compiler.js.map +1 -1
  57. package/dist/ledger/config.d.ts +10 -3
  58. package/dist/ledger/config.d.ts.map +1 -1
  59. package/dist/ledger/index.d.ts +1 -0
  60. package/dist/ledger/index.d.ts.map +1 -1
  61. package/dist/ledger/index.js +1 -0
  62. package/dist/ledger/index.js.map +1 -1
  63. package/dist/ledger/ledger.d.ts +2 -1
  64. package/dist/ledger/ledger.d.ts.map +1 -1
  65. package/dist/ledger/ledger.js +10 -4
  66. package/dist/ledger/ledger.js.map +1 -1
  67. package/dist/ledger/types.d.ts +5 -0
  68. package/dist/ledger/types.d.ts.map +1 -1
  69. package/dist/llm/from-catalog.d.ts +2 -0
  70. package/dist/llm/from-catalog.d.ts.map +1 -1
  71. package/dist/llm/from-catalog.js +3 -0
  72. package/dist/llm/from-catalog.js.map +1 -1
  73. package/dist/llm/index.d.ts +2 -0
  74. package/dist/llm/index.d.ts.map +1 -1
  75. package/dist/llm/index.js +1 -0
  76. package/dist/llm/index.js.map +1 -1
  77. package/dist/llm/openai-compatible.d.ts +7 -1
  78. package/dist/llm/openai-compatible.d.ts.map +1 -1
  79. package/dist/llm/openai-compatible.js +47 -11
  80. package/dist/llm/openai-compatible.js.map +1 -1
  81. package/dist/llm/types.d.ts +29 -0
  82. package/dist/llm/types.d.ts.map +1 -1
  83. package/dist/lsp/index.d.ts +10 -5
  84. package/dist/lsp/index.d.ts.map +1 -1
  85. package/dist/lsp/index.js +6 -4
  86. package/dist/lsp/index.js.map +1 -1
  87. package/dist/lsp/python.d.ts +22 -0
  88. package/dist/lsp/python.d.ts.map +1 -0
  89. package/dist/lsp/python.js +33 -0
  90. package/dist/lsp/python.js.map +1 -0
  91. package/dist/lsp/server.d.ts +184 -0
  92. package/dist/lsp/server.d.ts.map +1 -0
  93. package/dist/lsp/server.js +687 -0
  94. package/dist/lsp/server.js.map +1 -0
  95. package/dist/lsp/tools.d.ts +16 -2
  96. package/dist/lsp/tools.d.ts.map +1 -1
  97. package/dist/lsp/tools.js +565 -27
  98. package/dist/lsp/tools.js.map +1 -1
  99. package/dist/lsp/typescript.d.ts +13 -54
  100. package/dist/lsp/typescript.d.ts.map +1 -1
  101. package/dist/lsp/typescript.js +37 -262
  102. package/dist/lsp/typescript.js.map +1 -1
  103. package/dist/sandbox/daytona/index.d.ts +3 -1
  104. package/dist/sandbox/daytona/index.d.ts.map +1 -1
  105. package/dist/sandbox/daytona/index.js +9 -0
  106. package/dist/sandbox/daytona/index.js.map +1 -1
  107. package/dist/sandbox/docker/index.d.ts.map +1 -1
  108. package/dist/sandbox/docker/index.js +1 -0
  109. package/dist/sandbox/docker/index.js.map +1 -1
  110. package/dist/sandbox/e2b/index.d.ts +2 -1
  111. package/dist/sandbox/e2b/index.d.ts.map +1 -1
  112. package/dist/sandbox/e2b/index.js +9 -0
  113. package/dist/sandbox/e2b/index.js.map +1 -1
  114. package/dist/sandbox/heyo/index.d.ts.map +1 -1
  115. package/dist/sandbox/heyo/index.js +1 -0
  116. package/dist/sandbox/heyo/index.js.map +1 -1
  117. package/dist/sandbox/local/index.d.ts.map +1 -1
  118. package/dist/sandbox/local/index.js +1 -0
  119. package/dist/sandbox/local/index.js.map +1 -1
  120. package/dist/sandbox/provider.d.ts +2 -0
  121. package/dist/sandbox/provider.d.ts.map +1 -1
  122. package/dist/sandbox/types.d.ts +11 -0
  123. package/dist/sandbox/types.d.ts.map +1 -1
  124. package/dist/sandbox/types.js.map +1 -1
  125. package/dist/testing.d.ts.map +1 -1
  126. package/dist/testing.js +16 -0
  127. package/dist/testing.js.map +1 -1
  128. package/dist/tools/index.d.ts +2 -0
  129. package/dist/tools/index.d.ts.map +1 -1
  130. package/dist/tools/index.js +1 -0
  131. package/dist/tools/index.js.map +1 -1
  132. package/dist/tools/view-image.d.ts +20 -0
  133. package/dist/tools/view-image.d.ts.map +1 -0
  134. package/dist/tools/view-image.js +111 -0
  135. package/dist/tools/view-image.js.map +1 -0
  136. package/dist/verify/index.d.ts +3 -0
  137. package/dist/verify/index.d.ts.map +1 -0
  138. package/dist/verify/index.js +2 -0
  139. package/dist/verify/index.js.map +1 -0
  140. package/dist/verify/judge.d.ts +42 -0
  141. package/dist/verify/judge.d.ts.map +1 -0
  142. package/dist/verify/judge.js +119 -0
  143. package/dist/verify/judge.js.map +1 -0
  144. package/package.json +1 -1
@@ -1,23 +1,23 @@
1
1
  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
- import { compactConversation, DEFAULT_COMPACTION, estimateTokens, } from "./compaction.js";
5
- import { assistantMessage, conversationHistory, userMessage, } from "./context.js";
6
- import { renderInboxTask, } from "./inbox.js";
4
+ import { compactConversation, compactConversationStructured, DEFAULT_COMPACTION, estimateTokens, renderCompactionHandoff, } from "./compaction.js";
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
- import { resolveTurnIntent } from "./intent.js";
13
- import { addRequirement, compileLedger, ledgerGate, linkEvidence, recordEvidence, renderLedger, setRequirementStatus, verifyAlignment, } from "../ledger/index.js";
14
- import { chatAgent } from "../agents/chat/index.js";
15
- import { codingAgent } from "../agents/coding/index.js";
12
+ import { renderPromptBlocks } from "./prompt.js";
13
+ import { addRequirement, compileLedger, createLedger, ledgerGate, linkEvidence, recordEvidence, renderLedger, setRequirementStatus, } from "../ledger/index.js";
14
+ import { llmJudge } from "../verify/index.js";
16
15
  import { dispatchToolCalls, } from "./dispatch.js";
17
16
  import { mintOperationId, reconcilePendingOperations, settleFromResult, } from "./operations.js";
18
17
  import { RepeatTracker, cappedReplyNudge, createCourseCorrect, emptyTurnNudge, proseCheckpoint as proseCheckpointMessage, repeatedCallNudge, silentNudge, verboseNudge, visibleOutputTokens, } from "./guardrails.js";
19
18
  import { pollBackgroundJobs, renderBackgroundResult, renderLostJob, waitForBackgroundJobs, } from "./background.js";
20
- async function runOne(task, opts, chain = {}) {
19
+ export { userTurn } from "./types.js";
20
+ async function runOne(input, opts, chain = {}) {
21
21
  const strategy = opts.strategy ?? opts.agent?.strategy;
22
22
  if (!strategy)
23
23
  throw new Error("runAgent needs an agent or a strategy — pass opts.agent (chatAgent, codingAgent, …) or opts.strategy");
@@ -114,58 +114,66 @@ async function runOne(task, opts, chain = {}) {
114
114
  !strategy.phases.some((p) => p.id === opts.startPhase)) {
115
115
  throw new Error(`startPhase "${opts.startPhase}" is not a phase of strategy "${strategy.name}"`);
116
116
  }
117
- const context = task.context ?? (await strategy.prepareContext?.(opts.sandbox, task.task));
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));
118
122
  const prior = await opts.thread?.store.load(opts.thread.id);
119
123
  if (!opts.startPhase &&
120
124
  prior?.phase &&
121
125
  strategy.phases.some((item) => item.id === prior.phase))
122
126
  phase = prior.phase;
123
127
  let summary = prior?.summary;
128
+ let compaction = prior?.compaction;
124
129
  const budget = chain.budget ?? new BudgetTracker(opts.budgets);
125
- // ---- Task verification ------------------------------------------------
130
+ // ---- Task contracts and the judge --------------------------------------
126
131
  const verifierOn = opts.verifier !== undefined && opts.verifier !== false;
127
132
  const verifierConfig = verifierOn && typeof opts.verifier === "object" ? opts.verifier : {};
128
- const verifierLlm = verifierConfig.llm ?? opts.llm;
129
133
  const verifierGates = new Set(verifierConfig.gates ?? ["phase", "finish"]);
130
- const maxVerifierCalls = verifierConfig.maxCallsPerTask ?? 6;
131
- let verifierCalls = 0;
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()}`;
132
143
  let ledger = opts.ledger ?? (verifierOn ? prior?.ledger : undefined);
133
- if (verifierOn && !ledger) {
134
- const compiled = await compileLedger({
135
- llm: verifierLlm,
136
- taskId: opts.thread?.id ?? `task-${Date.now()}`,
137
- task: task.task,
138
- phases: strategy.phases,
139
- reasoningEffort: "low",
140
- });
141
- ledger = compiled.ledger;
142
- if (compiled.usage)
143
- budget.record(compiled.usage);
144
- }
145
- const recentEvidence = () => {
146
- if (!ledger)
147
- return "(none)";
148
- return Object.values(ledger.evidence)
149
- .slice(-6)
150
- .map((item) => `${item.id} ${item.tool}${item.summary ? `: ${item.summary}` : ""}${item.ok ? "" : " (failed)"}`)
151
- .join("\n");
152
- };
153
- const callVerifier = async (phaseGoal) => {
154
- if (!verifierOn || !ledger || verifierCalls >= maxVerifierCalls) {
155
- return { allow: true };
156
- }
157
- 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;
158
151
  try {
159
- const { verdict, usage } = await verifyAlignment({
160
- llm: verifierLlm,
161
- task: task.task,
152
+ const verdict = await judge.judge({
153
+ request: requestText,
162
154
  phase: phaseGoal,
163
155
  ledger,
164
- recentEvidence: recentEvidence(),
165
- reasoningEffort: "low",
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
+ })),
166
176
  });
167
- if (usage)
168
- budget.record(usage);
169
177
  return verdict;
170
178
  }
171
179
  catch (error) {
@@ -174,72 +182,23 @@ async function runOne(task, opts, chain = {}) {
174
182
  kind: "verifier_failed",
175
183
  detail: error instanceof Error ? error.message : String(error),
176
184
  });
177
- return {
178
- allow: verifierConfig.onError !== "block",
179
- aligned: false,
180
- justified: true,
181
- reason: "verifier unavailable",
182
- requiredAction: "none",
183
- confidence: 0,
184
- };
185
- }
186
- };
187
- if (verifierOn && ledger) {
188
- completionGate = async (summaryText) => {
189
- const structural = ledgerGate(ledger, {
190
- allowWaived: verifierConfig.allowWaived,
191
- });
192
- if (!structural.allow) {
185
+ if (verifierConfig.onError === "block") {
193
186
  return {
194
- ok: false,
195
- content: [
196
- "Verification gate: required work is not complete.",
197
- `open: ${structural.open.join(", ") || "none"}`,
198
- `missingEvidence: ${structural.missingEvidence.join(", ") || "none"}`,
199
- `failures: ${structural.failures.join(" | ") || "none"}`,
200
- "Attach evidence to each requirement with update_ledger, resolve failures, and try again.",
201
- ].join("\n"),
187
+ cleared: false,
188
+ requirements: requirements.map((item) => ({
189
+ id: item.id,
190
+ status: "unproven",
191
+ })),
192
+ confidence: 0,
202
193
  };
203
194
  }
204
- if (!verifierGates.has("finish")) {
205
- verified = true;
206
- return { ok: true, content: "" };
207
- }
208
- const verdict = await callVerifier({
209
- id: "finish",
210
- goal: "Deliver the task outcome with evidence.",
211
- });
212
- if (verdict.allow) {
213
- verified = true;
214
- return { ok: true, content: "" };
215
- }
216
- return {
217
- ok: false,
218
- content: `Verification gate rejected completion: ${verdict.reason ?? "unsupported claim"}. ` +
219
- `Required action: ${verdict.requiredAction ?? "gather_evidence"}. ` +
220
- `Claim: ${summaryText.slice(0, 400)}`,
221
- };
222
- };
223
- }
224
- const currentRequest = userMessage(task.task);
225
- const revivalOn = !!opts.revival;
226
- const revivalWarnAt = revivalWarnPercent(opts.revival ? opts.revival.warnAtPercent : undefined);
227
- let revivalWarned = false;
228
- let revivalRequest;
229
- const phaseOn = strategy.phases.length > 1 && opts.intrinsics?.phase !== false;
230
- const system = () => ({
231
- role: "system",
232
- content: strategy.systemPrompt({ task: task.task, context, summary }) +
233
- (phaseOn ? `\n\n${phaseDisciplineBlock(strategy.phases)}` : "") +
234
- (revivalOn ? `\n\n${revivalBlock(revivalWarnAt)}` : "") +
235
- (ledger
236
- ? `\n\nPROGRESS LEDGER (authoritative task state):\n${renderLedger(ledger)}\n\nUse 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.`
237
- : ""),
238
- });
239
- if (ledger) {
240
- const updateLedger = defineTool({
195
+ return undefined;
196
+ }
197
+ };
198
+ const updateLedger = () => {
199
+ const tool = defineTool({
241
200
  name: "update_ledger",
242
- description: "Update the task contract. Use it after gathering evidence: link a tool result to a requirement, mark it verified, record an open failure, or set the next step. Never invent evidence ids.",
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.",
243
202
  parameters: {
244
203
  type: "object",
245
204
  properties: {
@@ -248,6 +207,7 @@ async function runOne(task, opts, chain = {}) {
248
207
  enum: [
249
208
  "add_requirement",
250
209
  "link_evidence",
210
+ "verify",
251
211
  "set_status",
252
212
  "set_next_step",
253
213
  "add_failure",
@@ -256,8 +216,10 @@ async function runOne(task, opts, chain = {}) {
256
216
  },
257
217
  text: { type: "string" },
258
218
  required: { type: "boolean" },
219
+ phase: { type: "string" },
259
220
  requirementId: { type: "string" },
260
221
  evidenceId: { type: "string" },
222
+ claim: { type: "string" },
261
223
  status: {
262
224
  type: "string",
263
225
  enum: ["open", "in_progress", "verified", "blocked", "waived"],
@@ -273,16 +235,42 @@ async function runOne(task, opts, chain = {}) {
273
235
  const text = String(input.text ?? "").trim();
274
236
  if (!text)
275
237
  return { ok: false, content: "text is required" };
276
- const requirement = addRequirement(ledger, { text, required: input.required !== false }, "agent");
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");
277
245
  return {
278
246
  ok: true,
279
247
  content: `added ${requirement.id}: ${requirement.text}`,
280
248
  };
281
249
  }
282
250
  if (op === "link_evidence") {
283
- linkEvidence(ledger, String(input.requirementId ?? ""), String(input.evidenceId ?? ""));
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);
284
259
  return { ok: true, content: "evidence linked" };
285
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
+ }
286
274
  if (op === "set_status") {
287
275
  const status = String(input.status ?? "");
288
276
  if (status === "superseded") {
@@ -319,12 +307,167 @@ async function runOne(task, opts, chain = {}) {
319
307
  }
320
308
  return { ok: false, content: `unknown ledger op "${op}"` };
321
309
  }, { verb: "Updating the progress ledger" });
322
- available.set("update_ledger", updateLedger);
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);
323
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");
324
467
  const messages = [
325
468
  system(),
326
469
  ...conversationHistory(prior?.messages ?? []),
327
- currentRequest,
470
+ ...currentRequests,
328
471
  ];
329
472
  const compact = opts.compaction === false
330
473
  ? null
@@ -332,7 +475,46 @@ async function runOne(task, opts, chain = {}) {
332
475
  // The model's view of the transcript, bounded by assistant-text elision
333
476
  // (#77). `messages` stays whole — this only changes what is SENT.
334
477
  const elidedByMessage = new WeakMap();
335
- const forModel = () => messages.map((m) => elidedByMessage.get(m) ?? m);
478
+ const forModel = (includeTurn = true) => {
479
+ const out = messages.map((m) => elidedByMessage.get(m) ?? m);
480
+ if (!includeTurn)
481
+ return out;
482
+ const turn = turnState();
483
+ if (turn) {
484
+ // Keep the turn's final message last (tests and tool protocol both rely
485
+ // on it); put the changing runtime state immediately before it so the
486
+ // stable prefix is still reused.
487
+ const insertAt = Math.max(1, out.length - 1);
488
+ out.splice(insertAt, 0, {
489
+ role: "user",
490
+ content: turn,
491
+ });
492
+ }
493
+ return out;
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
+ };
336
518
  const evidence = [];
337
519
  // Commands the provider detached. The engine owns their results: it polls at
338
520
  // every turn boundary and, before a normal finish, waits for what is left.
@@ -513,31 +695,76 @@ async function runOne(task, opts, chain = {}) {
513
695
  content: `Unknown phase. Available: ${strategy.phases.map((item) => item.id).join(", ")}.`,
514
696
  };
515
697
  const previous = phase;
516
- if (verifierOn &&
517
- ledger &&
518
- verifierGates.has("phase") &&
519
- target.id !== previous) {
520
- const verdict = await callVerifier(target);
521
- if (!verdict.allow) {
522
- emit({
523
- type: "guardrail",
524
- kind: "transition_rejected",
525
- detail: `${previous} -> ${target.id}: ${verdict.reason}`,
526
- });
527
- return {
528
- ok: false,
529
- content: [
530
- `Phase transition rejected: ${verdict.reason}`,
531
- `Required action: ${verdict.requiredAction}`,
532
- "Gather the missing evidence or update the ledger, then transition again.",
533
- ].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,
534
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
+ }
535
756
  }
536
757
  }
537
758
  phase = target.id;
759
+ phaseRejections = 0;
760
+ phaseEvidence = [];
538
761
  pauseRequested =
539
762
  opts.pauseAfterPhase === previous && previous !== phase;
540
- emit({ type: "phase_started", phase });
763
+ emit({
764
+ type: "phase_started",
765
+ phase,
766
+ ...(forced ? { via: "advance_forced" } : {}),
767
+ });
541
768
  return {
542
769
  ok: true,
543
770
  content: `Current approach: ${target.id} — ${target.goal}`,
@@ -592,6 +819,122 @@ async function runOne(task, opts, chain = {}) {
592
819
  };
593
820
  }, { verb: "Requesting budget for the remaining work" }));
594
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
+ }
595
938
  /**
596
939
  * Settle operations only AFTER the conversation that contains their results
597
940
  * is durable. The opposite order opens the window this whole layer exists to
@@ -613,6 +956,7 @@ async function runOne(task, opts, chain = {}) {
613
956
  summary,
614
957
  messages: conversationHistory(messages),
615
958
  phase: done ? resumePhase : phase,
959
+ ...(compaction ? { compaction } : {}),
616
960
  ...(ledger ?? prior?.ledger
617
961
  ? { ledger: ledger ?? prior?.ledger }
618
962
  : {}),
@@ -696,6 +1040,7 @@ async function runOne(task, opts, chain = {}) {
696
1040
  phase,
697
1041
  evidence,
698
1042
  ...(ledger ? { ledger } : {}),
1043
+ ...(compaction ? { compaction } : {}),
699
1044
  ...(verifierOn ? { verified } : {}),
700
1045
  ...(revivalRequest &&
701
1046
  (reason === "budget_exceeded" || reason === "max_steps")
@@ -788,7 +1133,8 @@ async function runOne(task, opts, chain = {}) {
788
1133
  }
789
1134
  emit({
790
1135
  type: "run_started",
791
- task: task.task,
1136
+ text: requestText,
1137
+ ...(input.messages.length ? { messages: input.messages } : {}),
792
1138
  phases: strategy.phases.map((item) => item.id),
793
1139
  });
794
1140
  if (chain.delivered?.length)
@@ -931,34 +1277,64 @@ async function runOne(task, opts, chain = {}) {
931
1277
  }
932
1278
  }
933
1279
  if (compact && estimateTokens(messages) > compact.maxEstimatedTokens) {
934
- const folded = await compactConversation({
935
- llm: opts.llm,
936
- messages: forModel(),
937
- runningSummary: summary,
938
- keepRecentTokens: compact.keepRecentTokens,
939
- signal,
940
- });
1280
+ let folded = null;
1281
+ if (ledger) {
1282
+ try {
1283
+ folded = await compactConversationStructured({
1284
+ llm: opts.llm,
1285
+ messages: forModel(false),
1286
+ ...(summary ? { previousSummary: summary } : {}),
1287
+ ...(compaction ? { previousHandoff: compaction } : {}),
1288
+ ledger,
1289
+ ...(checkpointRef ? { git: { checkpoint: checkpointRef } } : {}),
1290
+ keepRecentTokens: compact.keepRecentTokens,
1291
+ signal,
1292
+ });
1293
+ }
1294
+ catch {
1295
+ folded = null;
1296
+ }
1297
+ }
1298
+ if (!folded) {
1299
+ folded = await compactConversation({
1300
+ llm: opts.llm,
1301
+ messages: forModel(false),
1302
+ runningSummary: summary,
1303
+ keepRecentTokens: compact.keepRecentTokens,
1304
+ signal,
1305
+ });
1306
+ }
941
1307
  if (folded) {
942
1308
  budget.record(folded.usage);
943
- summary = folded.summary;
1309
+ if (folded.handoff) {
1310
+ compaction = folded.handoff;
1311
+ summary = undefined;
1312
+ }
1313
+ else {
1314
+ summary = folded.summary;
1315
+ compaction = undefined;
1316
+ }
944
1317
  const tail = folded.keptMessages;
945
- // After a fold the model's earlier detail is gone from context;
946
- // point it at the durable record (see the Working notes section).
1318
+ // After a task reset the handoff is the durable record. A legacy
1319
+ // fold still points at the working-notes file.
947
1320
  const foldNote = {
948
1321
  role: "user",
949
- content: "[runtime] Context was folded to stay within the window. Your working notes " +
950
- "file (.anchor/notes.md, git-excluded) is the durable record of findings and " +
951
- "next steps — re-read it when you need detail from before the fold.",
1322
+ content: folded.handoff
1323
+ ? "[runtime] Context was reset to a durable task handoff. The authoritative task, requirements, evidence, failures, next steps, and recent observations are in the turn-scoped handoff; recent messages are retained verbatim."
1324
+ : "[runtime] Context was folded to stay within the window. Your working notes " +
1325
+ "file (.anchor/notes.md, git-excluded) is the durable record of findings and " +
1326
+ "next steps — re-read it when you need detail from before the fold.",
952
1327
  };
953
1328
  // Preserve the actual current request even if its earlier position
954
1329
  // was folded. Do not replace it with a generic "Begin" instruction.
955
- messages.splice(0, messages.length, system(), ...(tail.includes(currentRequest) ? [] : [currentRequest]), foldNote, ...tail);
1330
+ messages.splice(0, messages.length, system(), ...currentRequests.filter((message) => !tail.includes(message)), foldNote, ...tail);
956
1331
  emit({
957
1332
  type: "compacted",
958
1333
  removedMessages: folded.removedCount,
959
1334
  keptMessages: tail.length,
960
- summaryChars: summary.length,
961
- summary,
1335
+ summaryChars: folded.summary.length,
1336
+ summary: folded.summary,
1337
+ ...(folded.handoff ? { compaction: folded.handoff } : {}),
962
1338
  folding: folded.foldingPreview,
963
1339
  usage: folded.usage,
964
1340
  });
@@ -975,14 +1351,12 @@ async function runOne(task, opts, chain = {}) {
975
1351
  // fold. A job that finished since the previous turn (including one
976
1352
  // carried in from a previous run) rides this call, not the one after.
977
1353
  await pollJobs();
978
- // Refresh the authoritative contract every turn so evidence ids and
979
- // status changes are visible after each tool result.
980
- if (ledger)
981
- messages[0] = system();
982
1354
  steps++;
983
1355
  emit({ type: "llm_call_started", phase, step: steps });
1356
+ const modelMessages = forModel();
984
1357
  const response = await opts.llm.stream({
985
- messages: forModel(),
1358
+ messages: modelMessages,
1359
+ cacheBreakpoints: cacheBreakpointsFor(modelMessages),
986
1360
  tools: [...available.values()].map((tool) => ({
987
1361
  type: "function",
988
1362
  function: tool.definition,
@@ -1171,13 +1545,14 @@ async function runOne(task, opts, chain = {}) {
1171
1545
  call.name === "update_ledger") {
1172
1546
  return;
1173
1547
  }
1174
- recordEvidence(ledger, {
1548
+ const evidenceRef = recordEvidence(ledger, {
1175
1549
  tool: call.name,
1176
1550
  ref: `call:${call.id}`,
1177
1551
  ok: result.ok,
1178
1552
  summary: result.content.replace(/\s+/g, " ").slice(0, 500),
1179
1553
  ...(result.meta ? { meta: result.meta } : {}),
1180
1554
  });
1555
+ phaseEvidence.push(evidenceRef);
1181
1556
  },
1182
1557
  });
1183
1558
  runaway = outcome.runaway;
@@ -1308,6 +1683,12 @@ function usageDelta(now, before) {
1308
1683
  : {}),
1309
1684
  };
1310
1685
  }
1686
+ /** A required obligation still needs work, or an open failure blocks finish. */
1687
+ function hasActiveLedger(ledger) {
1688
+ if (!ledger)
1689
+ return false;
1690
+ return !ledgerGate(ledger).allow;
1691
+ }
1311
1692
  /**
1312
1693
  * A run chain (decision #82): one run at a time, with the consumer's inbox as
1313
1694
  * the source of what comes next. The chain shares one budget and carries
@@ -1318,7 +1699,7 @@ function usageDelta(now, before) {
1318
1699
  * cancellation, a caller-requested pause, or an exhausted budget — and returns
1319
1700
  * the last run's result with every run summarized on `.runs`.
1320
1701
  */
1321
- async function runChain(task, opts) {
1702
+ async function runChain(input, opts) {
1322
1703
  const inbox = opts.inbox;
1323
1704
  const { store, channel } = inbox;
1324
1705
  if (!channel)
@@ -1337,7 +1718,7 @@ async function runChain(task, opts) {
1337
1718
  };
1338
1719
  let stepsBefore = 0;
1339
1720
  let jobs;
1340
- let current = task;
1721
+ let current = input;
1341
1722
  let delivered;
1342
1723
  let claimed = [];
1343
1724
  let result;
@@ -1404,33 +1785,15 @@ async function runChain(task, opts) {
1404
1785
  return { ...result, runs };
1405
1786
  claimed = batch.map((message) => message.id);
1406
1787
  delivered = batch;
1407
- current = inbox.render ? inbox.render(batch) : renderInboxTask(batch);
1788
+ current = inbox.render ? inbox.render(batch) : renderInboxTurn(batch);
1408
1789
  }
1409
1790
  // The zero-progress guard fired. Report the last run as it stands; `.runs`
1410
1791
  // shows how many interruptions it took to get here.
1411
1792
  return { ...result, runs };
1412
1793
  }
1413
- export async function runAgent(task, opts) {
1414
- const verifierOn = opts.verifier !== undefined && opts.verifier !== false;
1415
- const intent = resolveTurnIntent(task.task, opts.intent ?? "auto");
1416
- // Conversation turns never create task state: no ledger, no verifier, no
1417
- // coding phases. A clear task uses the caller's agent, or the coding agent.
1418
- if (verifierOn && intent !== "task") {
1419
- const conversation = {
1420
- ...opts,
1421
- agent: chatAgent,
1422
- verifier: false,
1423
- intent: "chat",
1424
- ledger: undefined,
1425
- };
1426
- return opts.inbox
1427
- ? runChain(task, conversation)
1428
- : runOne(task, conversation);
1429
- }
1430
- const effective = {
1431
- ...opts,
1432
- agent: opts.agent ?? (verifierOn ? codingAgent : chatAgent),
1433
- };
1434
- return opts.inbox ? runChain(task, effective) : runOne(task, effective);
1794
+ export async function runAgent(input, opts) {
1795
+ if (opts.inbox)
1796
+ return runChain(input, opts);
1797
+ return runOne(input, opts);
1435
1798
  }
1436
1799
  //# sourceMappingURL=loop.js.map