@tangle-network/agent-runtime 0.339.0 → 0.342.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 (83) hide show
  1. package/dist/{activation-CVSFxis9.js → activation-CWPmt0Do.js} +4 -4
  2. package/dist/{activation-CVSFxis9.js.map → activation-CWPmt0Do.js.map} +1 -1
  3. package/dist/{activation-CJIIl_Ey.d.ts → activation-CXqtijue.d.ts} +2 -2
  4. package/dist/agent.d.ts +4 -18
  5. package/dist/agent.js +3 -21
  6. package/dist/agent.js.map +1 -1
  7. package/dist/analyst-loop.d.ts +2 -2
  8. package/dist/{bundle-DVOhBTic.js → bundle-Cwpit6Tg.js} +2 -2
  9. package/dist/{bundle-DVOhBTic.js.map → bundle-Cwpit6Tg.js.map} +1 -1
  10. package/dist/candidate-execution/index.d.ts +2 -40
  11. package/dist/candidate-execution/index.js +9 -255
  12. package/dist/candidate-execution/index.js.map +1 -1
  13. package/dist/{driver-control-acknowledgers-C53ggdJ6.js → coordination-manager-B5JsolDR.js} +4852 -4539
  14. package/dist/coordination-manager-B5JsolDR.js.map +1 -0
  15. package/dist/{delegate-CCWE_AkU.js → delegate-Rl-5paLJ.js} +2 -2
  16. package/dist/{delegate-CCWE_AkU.js.map → delegate-Rl-5paLJ.js.map} +1 -1
  17. package/dist/durable.d.ts +1 -1
  18. package/dist/durable.js +451 -358
  19. package/dist/durable.js.map +1 -1
  20. package/dist/{graph-CI9JXKqO.js → graph-Ce-59ZXn.js} +5 -5
  21. package/dist/{graph-CI9JXKqO.js.map → graph-Ce-59ZXn.js.map} +1 -1
  22. package/dist/{improve-BOXTLWJ3.js → improve-BZXt2qM0.js} +837 -704
  23. package/dist/improve-BZXt2qM0.js.map +1 -0
  24. package/dist/{improvement-adapter-HUt_-cWA.d.ts → improvement-adapter-sIthWkcN.d.ts} +2 -2
  25. package/dist/index.d.ts +6 -14
  26. package/dist/index.js +10 -10
  27. package/dist/intelligence.d.ts +4 -4
  28. package/dist/intelligence.js +205 -191
  29. package/dist/intelligence.js.map +1 -1
  30. package/dist/kernel.d.ts +3 -3
  31. package/dist/kernel.js +10 -10
  32. package/dist/{loop-runner-pmPIHFVY.js → loop-runner-B0MV1-CD.js} +2 -2
  33. package/dist/{loop-runner-pmPIHFVY.js.map → loop-runner-B0MV1-CD.js.map} +1 -1
  34. package/dist/{loop-runner-CWzG1bH4.d.ts → loop-runner-CuhbgVGo.d.ts} +3 -3
  35. package/dist/loop-runner-bin.d.ts +1 -1
  36. package/dist/loop-runner-bin.js +1 -1
  37. package/dist/mcp/bin.js +3 -3
  38. package/dist/mcp/index.d.ts +1 -1
  39. package/dist/mcp/index.js +5 -5
  40. package/dist/placement.d.ts +2 -26
  41. package/dist/placement.js +1 -35
  42. package/dist/placement.js.map +1 -1
  43. package/dist/{prepare-bXkvPMJA.js → prepare-C-cWCyt3.js} +988 -735
  44. package/dist/prepare-C-cWCyt3.js.map +1 -0
  45. package/dist/{profile-Dpei_Sq0.js → profile-CbMw5hAn.js} +2 -2
  46. package/dist/{profile-Dpei_Sq0.js.map → profile-CbMw5hAn.js.map} +1 -1
  47. package/dist/profile-materialization-bPm0gGLO.d.ts +49 -0
  48. package/dist/profiles.d.ts +1 -1
  49. package/dist/{protected-model-port-BGgpy2r9.d.ts → protected-model-port-DVVh-hEk.d.ts} +2 -6
  50. package/dist/{provision-supervisor-CJ6Zz6dz.js → provision-supervisor-DL6gEMQp.js} +444 -419
  51. package/dist/provision-supervisor-DL6gEMQp.js.map +1 -0
  52. package/dist/{runtime-B2fjL9Oi.js → runtime-eghmj6W4.js} +1245 -1028
  53. package/dist/runtime-eghmj6W4.js.map +1 -0
  54. package/dist/{sandbox-profile-compat-BzVBFqrz.js → sandbox-profile-compat-FN2kab2-.js} +72 -72
  55. package/dist/{sandbox-profile-compat-BzVBFqrz.js.map → sandbox-profile-compat-FN2kab2-.js.map} +1 -1
  56. package/dist/{search-executor-zMumiak_.js → search-executor-rbjdZ3OD.js} +3 -3
  57. package/dist/{search-executor-zMumiak_.js.map → search-executor-rbjdZ3OD.js.map} +1 -1
  58. package/dist/{server-_4voDNrl.js → server-BiK1Kcno.js} +4 -4
  59. package/dist/{server-_4voDNrl.js.map → server-BiK1Kcno.js.map} +1 -1
  60. package/dist/{structural-rollout-DpLCXzaL.js → structural-rollout-BD9IN7K_.js} +3 -3
  61. package/dist/{structural-rollout-DpLCXzaL.js.map → structural-rollout-BD9IN7K_.js.map} +1 -1
  62. package/dist/{supervise-qAwXmPtD.js → supervise-DmOi7gll.js} +6525 -5562
  63. package/dist/supervise-DmOi7gll.js.map +1 -0
  64. package/dist/{supervisor-DHrUJhEZ.js → supervisor-DUWcecAd.js} +13717 -12778
  65. package/dist/supervisor-DUWcecAd.js.map +1 -0
  66. package/dist/testing.d.ts +1 -1
  67. package/dist/testing.js +13 -13
  68. package/dist/tui/index.d.ts +1 -1
  69. package/dist/tui/index.js +1 -1
  70. package/dist/{types-CaOXqRTq.d.ts → types-Cgs89PQd.d.ts} +315 -409
  71. package/dist/{types-zWfqDjeL.d.ts → types-Cwrea-bU.d.ts} +2 -17
  72. package/dist/workspace-store/index.d.ts +1 -82
  73. package/dist/workspace-store/index.js +274 -680
  74. package/dist/workspace-store/index.js.map +1 -1
  75. package/package.json +8 -8
  76. package/dist/driver-control-acknowledgers-C53ggdJ6.js.map +0 -1
  77. package/dist/improve-BOXTLWJ3.js.map +0 -1
  78. package/dist/prepare-bXkvPMJA.js.map +0 -1
  79. package/dist/profile-materialization-B1wm2SPT.d.ts +0 -94
  80. package/dist/provision-supervisor-CJ6Zz6dz.js.map +0 -1
  81. package/dist/runtime-B2fjL9Oi.js.map +0 -1
  82. package/dist/supervise-qAwXmPtD.js.map +0 -1
  83. package/dist/supervisor-DHrUJhEZ.js.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { a as syncDurableDirectory, i as publishExclusiveDurableFile } from "./durable-file-gNI9ILAn.js";
2
- import { Cr as runSettledCommand, Dr as terminateProcessTreeAndConfirm, Ei as detachedSnapshot, pt as createExecutor, vr as linkAbort, yr as runAbortable } from "./supervisor-DHrUJhEZ.js";
2
+ import { Tr as terminateProcessTreeAndConfirm, _r as runAbortable, gr as linkAbort, mt as createExecutor, xi as detachedSnapshot, xr as runSettledCommand } from "./supervisor-DUWcecAd.js";
3
3
  import { i as ConfigError } from "./errors-DodWX-cb.js";
4
- import { F as collectAgentTurn, I as streamAgentTurn } from "./structural-rollout-DpLCXzaL.js";
5
- import { F as canonicalCandidateDigest$1, V as sha256Bytes$1, wt as armDeadlineTimer, z as immutableCandidateValue } from "./sandbox-profile-compat-BzVBFqrz.js";
4
+ import { F as collectAgentTurn, I as streamAgentTurn } from "./structural-rollout-BD9IN7K_.js";
5
+ import { B as sha256Bytes$1, Ct as armDeadlineTimer, P as canonicalCandidateDigest$1, R as immutableCandidateValue } from "./sandbox-profile-compat-FN2kab2-.js";
6
6
  import { a as concreteProfileModel, t as assertExecutableAgentProfile } from "./model-policy-CyCUwHed.js";
7
7
  import { t as sha256 } from "./content-digest-sBTLujlK.js";
8
- import { C as serializeRolloutPolicy, S as normalizeRolloutPolicy, w as structuralRolloutPolicyFromProfile, x as applyRolloutPolicyToProfile } from "./activation-CVSFxis9.js";
9
- import { a as reconcileInterruptedSearchCalls, b as improveSearchCost, c as searchExecutor, d as taggedLedger, i as isSearchEnvironmentFault, n as assertSearchLanes, s as searchCallTags, v as decideClosedSearch } from "./search-executor-zMumiak_.js";
8
+ import { C as serializeRolloutPolicy, S as normalizeRolloutPolicy, w as structuralRolloutPolicyFromProfile, x as applyRolloutPolicyToProfile } from "./activation-CWPmt0Do.js";
9
+ import { a as reconcileInterruptedSearchCalls, b as improveSearchCost, c as searchExecutor, d as taggedLedger, i as isSearchEnvironmentFault, n as assertSearchLanes, s as searchCallTags, v as decideClosedSearch } from "./search-executor-rbjdZ3OD.js";
10
10
  import { agentProfileEnvironmentSchema, agentProfileSchema, agentProfileTrainingSchema, agentTrainingDatasetIdentitySchema, agentTrainingParametersSchema, agentTrainingReceiptSchema, agentTrainingTaskKey, agentTrainingTaskSchema, applyAgentProfileDiff, canonicalAgentProfileDigest, canonicalCandidateBytes, diffAgentProfiles, sha256DigestSchema, snapshotAgentProfile, trainedModelIdForArtifact } from "@tangle-network/agent-interface";
11
11
  import { canonicalJson, makeProposalFinding } from "@tangle-network/agent-eval";
12
12
  import { redact } from "@tangle-network/agent-eval/traces";
@@ -50,6 +50,108 @@ function addRuntimeUsage(totals, event) {
50
50
  if (isUsageAmount(event.estimatedCostUsd)) totals.estimatedCostUsd = (totals.estimatedCostUsd ?? 0) + event.estimatedCostUsd;
51
51
  }
52
52
  //#endregion
53
+ //#region src/improvement/agentic-shot-evidence.ts
54
+ async function emitShotReceipt(callback, receipt, execution, primaryError) {
55
+ try {
56
+ await callback?.(receipt, execution);
57
+ } catch (callbackError) {
58
+ if (primaryError !== null) throw new AggregateError([primaryError, callbackError], "agenticGenerator: author shot failed and its receipt could not be persisted");
59
+ throw callbackError;
60
+ }
61
+ if (primaryError !== null) throw primaryError;
62
+ }
63
+ async function emitShotDisposition(callback, receipt, disposition) {
64
+ await callback?.(receipt, disposition);
65
+ }
66
+ async function rethrowShotSetupError(callback, receipt, worktreePath, stage, cause) {
67
+ const error = cause instanceof Error ? cause : new Error(String(cause));
68
+ try {
69
+ await emitShotDisposition(callback, receipt, {
70
+ kind: "setup-error",
71
+ worktreePath,
72
+ stage,
73
+ error: {
74
+ name: error.name,
75
+ message: error.message
76
+ }
77
+ });
78
+ } catch (callbackError) {
79
+ throw new AggregateError([cause, callbackError], "agenticGenerator: shot processing failed and its worktree disposition could not be persisted");
80
+ }
81
+ throw cause;
82
+ }
83
+ function shotReceipt(input) {
84
+ const error = input.error ? {
85
+ name: input.error instanceof Error ? input.error.name : "Error",
86
+ message: input.error instanceof Error ? input.error.message : String(input.error)
87
+ } : null;
88
+ const result = input.result;
89
+ const costBasis = costBasisFor(input.costReceipt, result);
90
+ const costUsd = input.costReceipt?.costUsd ?? result?.usage.costUsd ?? result?.usage.estimatedCostUsd ?? null;
91
+ return {
92
+ generation: input.generation ?? null,
93
+ candidateIndex: input.candidateIndex ?? null,
94
+ shot: input.shot + 1,
95
+ maxShots: input.maxShots,
96
+ profileDigest: input.profileDigest,
97
+ harness: input.harness,
98
+ provider: input.provider,
99
+ model: input.model,
100
+ reasoningEffort: input.profile.model?.reasoningEffort ?? null,
101
+ promptSha256: `sha256:${sha256(input.prompt)}`,
102
+ startedAt: input.startedAt.toISOString(),
103
+ completedAt: input.completedAt.toISOString(),
104
+ durationMs: input.completedAt.getTime() - input.startedAt.getTime(),
105
+ status: result?.status ?? null,
106
+ usage: result ? { ...result.usage } : null,
107
+ transportAttempts: result?.transportAttempts ?? null,
108
+ costCallId: input.costCallId,
109
+ costBasis,
110
+ costUsd: costBasis === "unknown" ? null : costUsd,
111
+ costUsdKnown: costBasis === "provider-reported",
112
+ error
113
+ };
114
+ }
115
+ function costBasisFor(receipt, turn) {
116
+ if (receipt) {
117
+ if (receipt.costUnknown) return "unknown";
118
+ return receipt.actualCostUsd === void 0 ? "estimated-pricing" : "provider-reported";
119
+ }
120
+ if (turn?.usage.usdKnown !== false && turn?.usage.costUsd !== void 0) return "provider-reported";
121
+ return turn?.usage.estimatedCostUsd !== void 0 ? "estimated-pricing" : "unknown";
122
+ }
123
+ function shotFailure(result) {
124
+ if (result.status === "completed") return null;
125
+ if (result.status === "aborted") return /* @__PURE__ */ new Error("agenticGenerator: author shot was cancelled by the caller");
126
+ return /* @__PURE__ */ new Error(`agenticGenerator: author shot failed${result.error?.message ? `: ${result.error.message}` : ""}`);
127
+ }
128
+ function costReceiptFromTurn(result, model) {
129
+ const tokensKnown = result.usage.tokensKnown !== false;
130
+ const actualCostUsd = result.usage.usdKnown === false ? void 0 : result.usage.costUsd;
131
+ const cachedTokens = promptCacheReadTokens(result.usage);
132
+ return {
133
+ model,
134
+ inputTokens: tokensKnown ? Math.max(0, result.usage.input - (cachedTokens ?? 0)) : 0,
135
+ outputTokens: tokensKnown ? result.usage.output : 0,
136
+ ...tokensKnown ? {} : { usageUnknown: true },
137
+ ...cachedTokens !== void 0 ? { cachedTokens } : {},
138
+ ...result.usage.reasoningTokens !== void 0 ? { reasoningTokens: result.usage.reasoningTokens } : {},
139
+ ...actualCostUsd !== void 0 ? { actualCostUsd } : result.usage.estimatedCostUsd !== void 0 ? { estimatedCostUsd: result.usage.estimatedCostUsd } : { costUnknown: true }
140
+ };
141
+ }
142
+ function promptCacheReadTokens(usage) {
143
+ const cache = usage.promptCache;
144
+ if (!cache) return void 0;
145
+ for (const field of [
146
+ "readTokens",
147
+ "cachedInputTokens",
148
+ "cacheReadInputTokens"
149
+ ]) {
150
+ const value = cache[field];
151
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
152
+ }
153
+ }
154
+ //#endregion
53
155
  //#region src/improvement/worktree-tree.ts
54
156
  /**
55
157
  * The exact content of a candidate worktree, as a Git tree object.
@@ -192,229 +294,275 @@ function agenticGenerator(opts) {
192
294
  const profile = detachedSnapshot(agentProfileSchema.parse(opts.profile), "agenticGenerator profile");
193
295
  assertExecutableAgentProfile(profile, "agenticGenerator");
194
296
  const harness = profile.harness;
195
- const provider = profile.model.provider.trim();
196
- const model = concreteProfileModel(profile);
197
- const profileDigest = canonicalAgentProfileDigest(profile);
198
- const dirty = opts.isDirty ?? worktreeDirty;
199
- const verify = opts.verify;
297
+ const author = {
298
+ opts,
299
+ profile,
300
+ harness,
301
+ provider: profile.model.provider.trim(),
302
+ model: concreteProfileModel(profile),
303
+ profileDigest: canonicalAgentProfileDigest(profile),
304
+ dirty: opts.isDirty ?? worktreeDirty
305
+ };
200
306
  return {
201
307
  kind: `agentic:${harness}`,
202
308
  proposesWithoutFindings: true,
203
- async generate({ worktreePath, findings, maxShots, signal, generation, candidateIndex, costLedger, costPhase }) {
309
+ generate: (request) => generateCandidate(author, request)
310
+ };
311
+ }
312
+ async function generateCandidate(author, request) {
313
+ const { opts, harness, provider, model, profile, profileDigest } = author;
314
+ const { findings, maxShots, signal, generation, candidateIndex } = request;
315
+ signal.throwIfAborted();
316
+ const { basePrompt, factory } = prepareAuthoring(author, request);
317
+ const needsRawTraceEvidence = requiresRawTraceEvidence(findings);
318
+ const shots = Math.max(1, maxShots);
319
+ const state = {
320
+ attemptNote: "",
321
+ best: null,
322
+ scored: null,
323
+ lastReceipt: null
324
+ };
325
+ for (let shot = 0; shot < shots; shot++) {
326
+ signal.throwIfAborted();
327
+ if (state.best) state.best.onDisk = false;
328
+ const taskPrompt = state.attemptNote ? `${basePrompt}\n\n${state.attemptNote}` : basePrompt;
329
+ const startedAt = /* @__PURE__ */ new Date();
330
+ const ran = await runAuthorShot(author, request, factory, taskPrompt, shot);
331
+ const execution = ran.turn ? detachedSnapshot(ran.turn, "agenticGenerator shot execution") : null;
332
+ const receipt = shotReceipt({
333
+ generation,
334
+ candidateIndex,
335
+ shot,
336
+ maxShots: shots,
337
+ harness,
338
+ provider,
339
+ model,
340
+ profile,
341
+ profileDigest,
342
+ prompt: taskPrompt,
343
+ startedAt,
344
+ completedAt: /* @__PURE__ */ new Date(),
345
+ result: execution,
346
+ costCallId: ran.costCallId,
347
+ costReceipt: ran.costReceipt,
348
+ error: ran.shotError
349
+ });
350
+ state.lastReceipt = receipt;
351
+ await emitShotReceipt(opts.onShotCompleted, receipt, execution, ran.shotError);
352
+ signal.throwIfAborted();
353
+ if (!execution) throw new Error("agenticGenerator: author shot completed without a Runtime turn");
354
+ const step = await settleShot(author, request, state, receipt, {
355
+ shot,
356
+ shots,
357
+ needsRawTraceEvidence
358
+ });
359
+ if ("candidate" in step) return step.candidate;
360
+ state.attemptNote = step.note;
361
+ }
362
+ if (state.best !== null) {
363
+ if (!state.lastReceipt) throw new Error("agenticGenerator: a tree was banked without a shot receipt");
364
+ return await shipBankedTree(author, request, state.lastReceipt, state.best);
365
+ }
366
+ return {
367
+ applied: false,
368
+ summary: ""
369
+ };
370
+ }
371
+ /** Check the request and build the author's prompt and executor, before any shot spends. */
372
+ function prepareAuthoring(author, request) {
373
+ const { opts } = author;
374
+ const { worktreePath, findings, costLedger } = request;
375
+ if (opts.maximumCharge && !costLedger) throw new Error("agenticGenerator: maximumCharge requires the run-wide CostLedger supplied by agent-eval");
376
+ const basePrompt = opts.buildPrompt({ findings });
377
+ if (typeof basePrompt !== "string" || basePrompt.trim().length === 0) throw new Error("agenticGenerator: buildPrompt must return a non-empty string");
378
+ const executorConfig = opts.executorForWorktree(worktreePath);
379
+ if (executorConfig.backend === "bridge" && !samePath(executorConfig.cwd, worktreePath)) throw new Error("agenticGenerator: bridge executor cwd must equal the candidate worktree path");
380
+ if (executorConfig.backend === "cli-in-place" && !samePath(executorConfig.workspacePath, worktreePath)) throw new Error("agenticGenerator: cli-in-place executor workspacePath must equal the candidate worktree path");
381
+ return {
382
+ basePrompt,
383
+ factory: createExecutor(executorConfig)
384
+ };
385
+ }
386
+ /** Run one author shot through the run-wide ledger when there is one. A failure is returned, not thrown. */
387
+ async function runAuthorShot(author, request, factory, taskPrompt, shot) {
388
+ const { opts, profile, harness, model } = author;
389
+ const { signal, generation, candidateIndex, costLedger, costPhase } = request;
390
+ let turn = null;
391
+ let costReceipt = null;
392
+ let costCallId = null;
393
+ let shotError = null;
394
+ try {
395
+ const execute = async (executionSignal, callId) => {
396
+ turn = await collectAgentTurn(streamAgentTurn({
397
+ kind: "executor",
398
+ profile,
399
+ factory
400
+ }, { prompt: taskPrompt }, {
401
+ signal: executionSignal,
402
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
403
+ ...callId ? { callId } : {}
404
+ }));
405
+ const failure = shotFailure(turn);
406
+ if (failure) throw failure;
407
+ return turn;
408
+ };
409
+ if (costLedger) {
410
+ const paid = await costLedger.runPaidCall({
411
+ channel: "driver",
412
+ phase: costPhase ?? "search.proposal",
413
+ actor: `agentic-generator:${harness}`,
414
+ model,
415
+ tags: {
416
+ generation: String(generation ?? -1),
417
+ candidateIndex: String(candidateIndex ?? -1),
418
+ shot: String(shot + 1)
419
+ },
420
+ signal,
421
+ ...opts.maximumCharge ? { maximumCharge: opts.maximumCharge } : {},
422
+ execute: (executionSignal, callId) => execute(executionSignal, callId),
423
+ receipt: (result) => costReceiptFromTurn(result, model),
424
+ receiptFromError: () => turn ? costReceiptFromTurn(turn, model) : void 0
425
+ });
426
+ costCallId = paid.callId ?? null;
427
+ costReceipt = paid.receipt ?? null;
428
+ if (!paid.succeeded) throw paid.error;
429
+ turn = paid.value;
430
+ } else turn = await execute(signal);
431
+ } catch (cause) {
432
+ shotError = cause instanceof Error ? cause : new Error(String(cause));
433
+ }
434
+ return {
435
+ turn,
436
+ costReceipt,
437
+ costCallId,
438
+ shotError
439
+ };
440
+ }
441
+ /** Decide what a completed shot's worktree means: ship it, or the note the next shot starts from. */
442
+ async function settleShot(author, request, state, receipt, position) {
443
+ const { opts, dirty } = author;
444
+ const { worktreePath, findings, signal } = request;
445
+ let worktreeChanged;
446
+ try {
447
+ worktreeChanged = dirty(worktreePath);
448
+ } catch (cause) {
449
+ signal.throwIfAborted();
450
+ return rethrowShotSetupError(opts.onShotDisposition, receipt, worktreePath, "worktree-inspection", cause);
451
+ }
452
+ if (!worktreeChanged) {
453
+ signal.throwIfAborted();
454
+ await emitShotDisposition(opts.onShotDisposition, receipt, {
455
+ kind: "clean",
456
+ worktreePath
457
+ });
458
+ signal.throwIfAborted();
459
+ return { note: EMPTY_TREE_NOTE };
460
+ }
461
+ if (position.needsRawTraceEvidence) {
462
+ let problem;
463
+ try {
464
+ problem = rawTraceEvidenceProblem(worktreePath, findings);
465
+ } catch (cause) {
204
466
  signal.throwIfAborted();
205
- if (opts.maximumCharge && !costLedger) throw new Error("agenticGenerator: maximumCharge requires the run-wide CostLedger supplied by agent-eval");
206
- const basePrompt = opts.buildPrompt({ findings });
207
- if (typeof basePrompt !== "string" || basePrompt.trim().length === 0) throw new Error("agenticGenerator: buildPrompt must return a non-empty string");
208
- const executorConfig = opts.executorForWorktree(worktreePath);
209
- if (executorConfig.backend === "bridge" && !samePath(executorConfig.cwd, worktreePath)) throw new Error("agenticGenerator: bridge executor cwd must equal the candidate worktree path");
210
- if (executorConfig.backend === "cli-in-place" && !samePath(executorConfig.workspacePath, worktreePath)) throw new Error("agenticGenerator: cli-in-place executor workspacePath must equal the candidate worktree path");
211
- const factory = createExecutor(executorConfig);
212
- const needsRawTraceEvidence = requiresRawTraceEvidence(findings);
213
- const shots = Math.max(1, maxShots);
214
- let attemptNote = "";
215
- let best = null;
216
- let scored = null;
217
- let lastReceipt = null;
218
- /** Ship the best tree this candidate produced, restoring it when a later
219
- * shot wrote over it. */
220
- const shipBankedTree = async (receipt, banked) => {
221
- const restoredFromShot = banked.onDisk ? null : banked.shot;
222
- if (!banked.onDisk) {
223
- if (banked.tree === null) throw new Error(`agenticGenerator: shot ${banked.shot} produced the best tree but it was never snapshotted`);
224
- restoreWorktreeTree(worktreePath, banked.tree);
225
- banked.onDisk = true;
226
- }
227
- signal.throwIfAborted();
228
- await emitShotDisposition(opts.onShotDisposition, receipt, {
229
- kind: "accepted",
230
- worktreePath,
231
- verified: true,
232
- restoredFromShot
233
- });
234
- signal.throwIfAborted();
235
- return acceptedCandidate(findings);
236
- };
237
- for (let shot = 0; shot < shots; shot++) {
238
- signal.throwIfAborted();
239
- if (best) best.onDisk = false;
240
- const taskPrompt = attemptNote ? `${basePrompt}\n\n${attemptNote}` : basePrompt;
241
- const startedAt = /* @__PURE__ */ new Date();
242
- let turn = null;
243
- let costReceipt = null;
244
- let costCallId = null;
245
- let shotError = null;
246
- try {
247
- const execute = async (executionSignal, callId) => {
248
- turn = await collectAgentTurn(streamAgentTurn({
249
- kind: "executor",
250
- profile,
251
- factory
252
- }, { prompt: taskPrompt }, {
253
- signal: executionSignal,
254
- ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
255
- ...callId ? { callId } : {}
256
- }));
257
- const failure = shotFailure(turn);
258
- if (failure) throw failure;
259
- return turn;
260
- };
261
- if (costLedger) {
262
- const paid = await costLedger.runPaidCall({
263
- channel: "driver",
264
- phase: costPhase ?? "search.proposal",
265
- actor: `agentic-generator:${harness}`,
266
- model,
267
- tags: {
268
- generation: String(generation ?? -1),
269
- candidateIndex: String(candidateIndex ?? -1),
270
- shot: String(shot + 1)
271
- },
272
- signal,
273
- ...opts.maximumCharge ? { maximumCharge: opts.maximumCharge } : {},
274
- execute: (executionSignal, callId) => execute(executionSignal, callId),
275
- receipt: (result) => costReceiptFromTurn(result, model),
276
- receiptFromError: () => turn ? costReceiptFromTurn(turn, model) : void 0
277
- });
278
- costCallId = paid.callId ?? null;
279
- costReceipt = paid.receipt ?? null;
280
- if (!paid.succeeded) throw paid.error;
281
- turn = paid.value;
282
- } else turn = await execute(signal);
283
- } catch (cause) {
284
- shotError = cause instanceof Error ? cause : new Error(String(cause));
285
- }
286
- const execution = turn ? detachedSnapshot(turn, "agenticGenerator shot execution") : null;
287
- const receipt = shotReceipt({
288
- generation,
289
- candidateIndex,
290
- shot,
291
- maxShots: shots,
292
- harness,
293
- provider,
294
- model,
295
- profile,
296
- profileDigest,
297
- prompt: taskPrompt,
298
- startedAt,
299
- completedAt: /* @__PURE__ */ new Date(),
300
- result: execution,
301
- costCallId,
302
- costReceipt,
303
- error: shotError
304
- });
305
- lastReceipt = receipt;
306
- await emitShotReceipt(opts.onShotCompleted, receipt, execution, shotError);
307
- signal.throwIfAborted();
308
- if (!execution) throw new Error("agenticGenerator: author shot completed without a Runtime turn");
309
- let worktreeChanged;
310
- try {
311
- worktreeChanged = dirty(worktreePath);
312
- } catch (cause) {
313
- signal.throwIfAborted();
314
- return rethrowShotSetupError(opts.onShotDisposition, receipt, worktreePath, "worktree-inspection", cause);
315
- }
316
- if (!worktreeChanged) {
317
- signal.throwIfAborted();
318
- await emitShotDisposition(opts.onShotDisposition, receipt, {
319
- kind: "clean",
320
- worktreePath
321
- });
322
- signal.throwIfAborted();
323
- attemptNote = EMPTY_TREE_NOTE;
324
- continue;
325
- }
326
- if (needsRawTraceEvidence) {
327
- let problem;
328
- try {
329
- problem = rawTraceEvidenceProblem(worktreePath, findings);
330
- } catch (cause) {
331
- signal.throwIfAborted();
332
- return rethrowShotSetupError(opts.onShotDisposition, receipt, worktreePath, "raw-trace-evidence", cause);
333
- }
334
- if (problem) {
335
- signal.throwIfAborted();
336
- await emitShotDisposition(opts.onShotDisposition, receipt, {
337
- kind: "rejected",
338
- worktreePath,
339
- stage: "raw-trace-evidence",
340
- feedback: problem
341
- });
342
- signal.throwIfAborted();
343
- attemptNote = problem;
344
- continue;
345
- }
346
- }
347
- if (!verify) {
348
- signal.throwIfAborted();
349
- await emitShotDisposition(opts.onShotDisposition, receipt, {
350
- kind: "accepted",
351
- worktreePath,
352
- verified: false,
353
- restoredFromShot: null
354
- });
355
- signal.throwIfAborted();
356
- return acceptedCandidate(findings);
357
- }
358
- let result;
359
- try {
360
- signal.throwIfAborted();
361
- result = await verify(worktreePath, signal);
362
- signal.throwIfAborted();
363
- } catch (cause) {
364
- signal.throwIfAborted();
365
- return rethrowShotSetupError(opts.onShotDisposition, receipt, worktreePath, "verification", cause);
366
- }
367
- if (result.ok) {
368
- const score = admittedScore(result, scored, shot);
369
- scored = score !== null;
370
- const previousBest = best;
371
- let banked;
372
- if (previousBest === null || (score ?? 0) >= (previousBest.score ?? 0)) banked = {
373
- shot: shot + 1,
374
- score,
375
- tree: null,
376
- onDisk: true
377
- };
378
- else banked = previousBest;
379
- const becomesBest = banked !== previousBest;
380
- best = banked;
381
- if (result.keepGoing !== true) {
382
- signal.throwIfAborted();
383
- return await shipBankedTree(receipt, banked);
384
- }
385
- if (becomesBest && shot < shots - 1) banked.tree = snapshotWorktreeTree(worktreePath);
386
- signal.throwIfAborted();
387
- await emitShotDisposition(opts.onShotDisposition, receipt, {
388
- kind: "kept",
389
- worktreePath,
390
- score,
391
- best: becomesBest,
392
- feedback: result.feedback ?? null
393
- });
394
- signal.throwIfAborted();
395
- attemptNote = keptNote(result.feedback);
396
- continue;
397
- }
398
- signal.throwIfAborted();
399
- await emitShotDisposition(opts.onShotDisposition, receipt, {
400
- kind: "rejected",
401
- worktreePath,
402
- stage: "verification",
403
- feedback: result.feedback ?? null
404
- });
405
- signal.throwIfAborted();
406
- attemptNote = failureNote(result.feedback);
407
- }
408
- if (best !== null) {
409
- if (!lastReceipt) throw new Error("agenticGenerator: a tree was banked without a shot receipt");
410
- return await shipBankedTree(lastReceipt, best);
411
- }
412
- return {
413
- applied: false,
414
- summary: ""
415
- };
467
+ return rethrowShotSetupError(opts.onShotDisposition, receipt, worktreePath, "raw-trace-evidence", cause);
416
468
  }
417
- };
469
+ if (problem) {
470
+ signal.throwIfAborted();
471
+ await emitShotDisposition(opts.onShotDisposition, receipt, {
472
+ kind: "rejected",
473
+ worktreePath,
474
+ stage: "raw-trace-evidence",
475
+ feedback: problem
476
+ });
477
+ signal.throwIfAborted();
478
+ return { note: problem };
479
+ }
480
+ }
481
+ if (!opts.verify) {
482
+ signal.throwIfAborted();
483
+ await emitShotDisposition(opts.onShotDisposition, receipt, {
484
+ kind: "accepted",
485
+ worktreePath,
486
+ verified: false,
487
+ restoredFromShot: null
488
+ });
489
+ signal.throwIfAborted();
490
+ return { candidate: acceptedCandidate(findings) };
491
+ }
492
+ return settleVerifiedShot(author, request, state, receipt, position, opts.verify);
493
+ }
494
+ /** Run the verifier on a dirty tree, then ship, bank or reject it. */
495
+ async function settleVerifiedShot(author, request, state, receipt, { shot, shots }, verify) {
496
+ const { opts } = author;
497
+ const { worktreePath, signal } = request;
498
+ let result;
499
+ try {
500
+ signal.throwIfAborted();
501
+ result = await verify(worktreePath, signal);
502
+ signal.throwIfAborted();
503
+ } catch (cause) {
504
+ signal.throwIfAborted();
505
+ return rethrowShotSetupError(opts.onShotDisposition, receipt, worktreePath, "verification", cause);
506
+ }
507
+ if (result.ok) {
508
+ const score = admittedScore(result, state.scored, shot);
509
+ state.scored = score !== null;
510
+ const previousBest = state.best;
511
+ let banked;
512
+ if (previousBest === null || (score ?? 0) >= (previousBest.score ?? 0)) banked = {
513
+ shot: shot + 1,
514
+ score,
515
+ tree: null,
516
+ onDisk: true
517
+ };
518
+ else banked = previousBest;
519
+ const becomesBest = banked !== previousBest;
520
+ state.best = banked;
521
+ if (result.keepGoing !== true) {
522
+ signal.throwIfAborted();
523
+ return { candidate: await shipBankedTree(author, request, receipt, banked) };
524
+ }
525
+ if (becomesBest && shot < shots - 1) banked.tree = snapshotWorktreeTree(worktreePath);
526
+ signal.throwIfAborted();
527
+ await emitShotDisposition(opts.onShotDisposition, receipt, {
528
+ kind: "kept",
529
+ worktreePath,
530
+ score,
531
+ best: becomesBest,
532
+ feedback: result.feedback ?? null
533
+ });
534
+ signal.throwIfAborted();
535
+ return { note: keptNote(result.feedback) };
536
+ }
537
+ signal.throwIfAborted();
538
+ await emitShotDisposition(opts.onShotDisposition, receipt, {
539
+ kind: "rejected",
540
+ worktreePath,
541
+ stage: "verification",
542
+ feedback: result.feedback ?? null
543
+ });
544
+ signal.throwIfAborted();
545
+ return { note: failureNote(result.feedback) };
546
+ }
547
+ /** Ship the best tree this candidate produced, restoring it when a later
548
+ * shot wrote over it. */
549
+ async function shipBankedTree(author, request, receipt, banked) {
550
+ const { worktreePath, findings, signal } = request;
551
+ const restoredFromShot = banked.onDisk ? null : banked.shot;
552
+ if (!banked.onDisk) {
553
+ if (banked.tree === null) throw new Error(`agenticGenerator: shot ${banked.shot} produced the best tree but it was never snapshotted`);
554
+ restoreWorktreeTree(worktreePath, banked.tree);
555
+ banked.onDisk = true;
556
+ }
557
+ signal.throwIfAborted();
558
+ await emitShotDisposition(author.opts.onShotDisposition, receipt, {
559
+ kind: "accepted",
560
+ worktreePath,
561
+ verified: true,
562
+ restoredFromShot
563
+ });
564
+ signal.throwIfAborted();
565
+ return acceptedCandidate(findings);
418
566
  }
419
567
  /** The rank a passing tree carries, refusing a set of trees that cannot be ordered. */
420
568
  function admittedScore(result, scored, shot) {
@@ -424,129 +572,29 @@ function admittedScore(result, scored, shot) {
424
572
  if (typeof result.score !== "number" || !Number.isFinite(result.score)) throw new Error(`agenticGenerator: verify returned a non-finite score (${String(result.score)}) for shot ${shot + 1}`);
425
573
  return result.score;
426
574
  }
427
- async function emitShotReceipt(callback, receipt, execution, primaryError) {
428
- try {
429
- await callback?.(receipt, execution);
430
- } catch (callbackError) {
431
- if (primaryError !== null) throw new AggregateError([primaryError, callbackError], "agenticGenerator: author shot failed and its receipt could not be persisted");
432
- throw callbackError;
433
- }
434
- if (primaryError !== null) throw primaryError;
575
+ const EMPTY_TREE_NOTE = "NOTE: your previous attempt left the working tree unchanged. Make the concrete file edits now.";
576
+ /** Next-shot feedback when the worktree is dirty but failed verification. The
577
+ * edits persist on disk, so the harness resumes atop them — tell it to fix in
578
+ * place, not start over. Verifier detail is truncated to keep the prompt bounded. */
579
+ function failureNote(feedback) {
580
+ const detail = feedback?.trim();
581
+ return [
582
+ "NOTE: your edits are in the working tree but verification FAILED.",
583
+ "Fix the problem in place — build on your existing edits, do not revert them.",
584
+ detail ? `Verifier output:\n${truncate$1(detail, 4e3)}` : "No verifier detail was captured."
585
+ ].join("\n");
435
586
  }
436
- async function emitShotDisposition(callback, receipt, disposition) {
437
- await callback?.(receipt, disposition);
438
- }
439
- async function rethrowShotSetupError(callback, receipt, worktreePath, stage, cause) {
440
- const error = cause instanceof Error ? cause : new Error(String(cause));
441
- try {
442
- await emitShotDisposition(callback, receipt, {
443
- kind: "setup-error",
444
- worktreePath,
445
- stage,
446
- error: {
447
- name: error.name,
448
- message: error.message
449
- }
450
- });
451
- } catch (callbackError) {
452
- throw new AggregateError([cause, callbackError], "agenticGenerator: shot processing failed and its worktree disposition could not be persisted");
453
- }
454
- throw cause;
455
- }
456
- function shotReceipt(input) {
457
- const error = input.error ? {
458
- name: input.error instanceof Error ? input.error.name : "Error",
459
- message: input.error instanceof Error ? input.error.message : String(input.error)
460
- } : null;
461
- const result = input.result;
462
- const costBasis = costBasisFor(input.costReceipt, result);
463
- const costUsd = input.costReceipt?.costUsd ?? result?.usage.costUsd ?? result?.usage.estimatedCostUsd ?? null;
464
- return {
465
- generation: input.generation ?? null,
466
- candidateIndex: input.candidateIndex ?? null,
467
- shot: input.shot + 1,
468
- maxShots: input.maxShots,
469
- profileDigest: input.profileDigest,
470
- harness: input.harness,
471
- provider: input.provider,
472
- model: input.model,
473
- reasoningEffort: input.profile.model?.reasoningEffort ?? null,
474
- promptSha256: `sha256:${sha256(input.prompt)}`,
475
- startedAt: input.startedAt.toISOString(),
476
- completedAt: input.completedAt.toISOString(),
477
- durationMs: input.completedAt.getTime() - input.startedAt.getTime(),
478
- status: result?.status ?? null,
479
- usage: result ? { ...result.usage } : null,
480
- transportAttempts: result?.transportAttempts ?? null,
481
- costCallId: input.costCallId,
482
- costBasis,
483
- costUsd: costBasis === "unknown" ? null : costUsd,
484
- costUsdKnown: costBasis === "provider-reported",
485
- error
486
- };
487
- }
488
- function costBasisFor(receipt, turn) {
489
- if (receipt) {
490
- if (receipt.costUnknown) return "unknown";
491
- return receipt.actualCostUsd === void 0 ? "estimated-pricing" : "provider-reported";
492
- }
493
- if (turn?.usage.usdKnown !== false && turn?.usage.costUsd !== void 0) return "provider-reported";
494
- return turn?.usage.estimatedCostUsd !== void 0 ? "estimated-pricing" : "unknown";
495
- }
496
- function shotFailure(result) {
497
- if (result.status === "completed") return null;
498
- if (result.status === "aborted") return /* @__PURE__ */ new Error("agenticGenerator: author shot was cancelled by the caller");
499
- return /* @__PURE__ */ new Error(`agenticGenerator: author shot failed${result.error?.message ? `: ${result.error.message}` : ""}`);
500
- }
501
- function costReceiptFromTurn(result, model) {
502
- const tokensKnown = result.usage.tokensKnown !== false;
503
- const actualCostUsd = result.usage.usdKnown === false ? void 0 : result.usage.costUsd;
504
- const cachedTokens = promptCacheReadTokens(result.usage);
505
- return {
506
- model,
507
- inputTokens: tokensKnown ? Math.max(0, result.usage.input - (cachedTokens ?? 0)) : 0,
508
- outputTokens: tokensKnown ? result.usage.output : 0,
509
- ...tokensKnown ? {} : { usageUnknown: true },
510
- ...cachedTokens !== void 0 ? { cachedTokens } : {},
511
- ...result.usage.reasoningTokens !== void 0 ? { reasoningTokens: result.usage.reasoningTokens } : {},
512
- ...actualCostUsd !== void 0 ? { actualCostUsd } : result.usage.estimatedCostUsd !== void 0 ? { estimatedCostUsd: result.usage.estimatedCostUsd } : { costUnknown: true }
513
- };
514
- }
515
- function promptCacheReadTokens(usage) {
516
- const cache = usage.promptCache;
517
- if (!cache) return void 0;
518
- for (const field of [
519
- "readTokens",
520
- "cachedInputTokens",
521
- "cacheReadInputTokens"
522
- ]) {
523
- const value = cache[field];
524
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
525
- }
526
- }
527
- const EMPTY_TREE_NOTE = "NOTE: your previous attempt left the working tree unchanged. Make the concrete file edits now.";
528
- /** Next-shot feedback when the worktree is dirty but failed verification. The
529
- * edits persist on disk, so the harness resumes atop them — tell it to fix in
530
- * place, not start over. Verifier detail is truncated to keep the prompt bounded. */
531
- function failureNote(feedback) {
532
- const detail = feedback?.trim();
533
- return [
534
- "NOTE: your edits are in the working tree but verification FAILED.",
535
- "Fix the problem in place — build on your existing edits, do not revert them.",
536
- detail ? `Verifier output:\n${truncate$1(detail, 4e3)}` : "No verifier detail was captured."
537
- ].join("\n");
538
- }
539
- /** Next-shot feedback when the worktree PASSED and the verifier asked for
540
- * another shot. The passing tree is banked, so the author is told to improve
541
- * it rather than protect it — a worse tree cannot cost it the candidate. */
542
- function keptNote(feedback) {
543
- const detail = feedback?.trim();
544
- return [
545
- "NOTE: your edits are in the working tree and verification PASSED.",
546
- "Shots remain in this budget — keep improving the change in place, do not revert it.",
547
- "The best version you produce is the one that ships.",
548
- detail ? `Verifier output:\n${truncate$1(detail, 4e3)}` : "No verifier detail was captured."
549
- ].join("\n");
587
+ /** Next-shot feedback when the worktree PASSED and the verifier asked for
588
+ * another shot. The passing tree is banked, so the author is told to improve
589
+ * it rather than protect it — a worse tree cannot cost it the candidate. */
590
+ function keptNote(feedback) {
591
+ const detail = feedback?.trim();
592
+ return [
593
+ "NOTE: your edits are in the working tree and verification PASSED.",
594
+ "Shots remain in this budget — keep improving the change in place, do not revert it.",
595
+ "The best version you produce is the one that ships.",
596
+ detail ? `Verifier output:\n${truncate$1(detail, 4e3)}` : "No verifier detail was captured."
597
+ ].join("\n");
550
598
  }
551
599
  function rawTraceEvidenceProblem(worktreePath, findings) {
552
600
  if (worktreeChangedPaths(worktreePath).filter((path) => path !== RAW_TRACE_DIAGNOSIS_PATH).length === 0) return [`NOTE: raw-trace mode requires a real code/config edit in addition to ${RAW_TRACE_DIAGNOSIS_PATH}.`, "Your previous attempt only changed the diagnosis artifact. Inspect the cited traces and make the causal code change."].join("\n");
@@ -1974,6 +2022,121 @@ async function runMethodImprovement(profile, opts) {
1974
2022
  };
1975
2023
  }
1976
2024
  //#endregion
2025
+ //#region src/improvement/search-session.ts
2026
+ /** A refusal as the ledger stores it: redacted with the share profile. */
2027
+ function searchRefusalText(error) {
2028
+ return redact(error instanceof Error ? error.message : String(error), { profile: "share" }).value;
2029
+ }
2030
+ /** The node a candidate surface becomes: materialized, or refused with the reason. */
2031
+ function profileNodeOf(prepared, candidateSurface) {
2032
+ const surface = immutableCandidateValue(candidateSurface);
2033
+ try {
2034
+ return {
2035
+ surface,
2036
+ profile: prepared.materializeCandidate(surface),
2037
+ refusal: null
2038
+ };
2039
+ } catch (error) {
2040
+ return {
2041
+ surface,
2042
+ profile: null,
2043
+ refusal: searchRefusalText(error)
2044
+ };
2045
+ }
2046
+ }
2047
+ /**
2048
+ * The codec that also records each node's profile by node id. Placement reads a node's profile
2049
+ * to skip the lanes that refuse it, so every node the ledger holds, and every node this process
2050
+ * registers, is known before its cells are placed.
2051
+ */
2052
+ function registeringProfileCodec(prepared, searchId, profiles) {
2053
+ const profiled = profileCodec(prepared);
2054
+ return {
2055
+ node(recorder, node) {
2056
+ const registered = profiled.node(recorder, node);
2057
+ profiles.set(searchNodeId(searchId, registered.artifactDigest), node.profile);
2058
+ return registered;
2059
+ },
2060
+ diff: profiled.diff,
2061
+ load(recorder, node) {
2062
+ const loaded = profiled.load(recorder, node);
2063
+ profiles.set(node.nodeId, loaded.profile);
2064
+ return loaded;
2065
+ }
2066
+ };
2067
+ }
2068
+ /** Profiles as search artifacts: nodes by profile digest, edges by profile diffs. */
2069
+ function profileCodec(prepared) {
2070
+ const surfaceId = prepared.surface;
2071
+ return {
2072
+ node(recorder, node) {
2073
+ if (node.profile) {
2074
+ const artifact = recorder.blob("agent-profile", {
2075
+ kind: "agent-profile",
2076
+ profile: node.profile
2077
+ });
2078
+ return {
2079
+ artifactDigest: canonicalAgentProfileDigest(node.profile),
2080
+ artifact,
2081
+ surfaces: [{
2082
+ surfaceId,
2083
+ kind: "agent-profile",
2084
+ artifact
2085
+ }]
2086
+ };
2087
+ }
2088
+ const artifact = recorder.blob("surface", {
2089
+ kind: "unmaterialized-surface",
2090
+ surface: node.surface,
2091
+ refusal: node.refusal
2092
+ });
2093
+ return {
2094
+ artifactDigest: canonicalCandidateDigest$1({
2095
+ kind: "unmaterialized-surface",
2096
+ surface: node.surface
2097
+ }),
2098
+ artifact,
2099
+ surfaces: [{
2100
+ surfaceId,
2101
+ kind: "agent-profile",
2102
+ artifact
2103
+ }]
2104
+ };
2105
+ },
2106
+ diff(recorder, parent, child) {
2107
+ if (!parent.profile || !child.profile) return { unknown: "a surface that does not materialize has no profile to diff" };
2108
+ const diffs = diffAgentProfiles(parent.profile, child.profile);
2109
+ const to = canonicalAgentProfileDigest(child.profile);
2110
+ if (canonicalAgentProfileDigest(diffs.reduce(applyAgentProfileDiff, parent.profile)) !== to) return { unknown: "the Interface profile diffs do not reproduce the child profile" };
2111
+ return recorder.blob("diff", {
2112
+ kind: "agent-profile-diff",
2113
+ from: canonicalAgentProfileDigest(parent.profile),
2114
+ to,
2115
+ diffs
2116
+ });
2117
+ },
2118
+ load(recorder, node) {
2119
+ const stored = recorder.readBlob(node.artifact);
2120
+ if (stored.kind === "agent-profile") {
2121
+ const parsed = agentProfileSchema.safeParse(stored.profile);
2122
+ if (!parsed.success) throw new Error(`improve(): node ${node.nodeId} holds an invalid AgentProfile`);
2123
+ const loaded = immutableCandidateValue(parsed.data);
2124
+ return {
2125
+ surface: prepared.surfaceOf(loaded),
2126
+ profile: loaded,
2127
+ refusal: null
2128
+ };
2129
+ }
2130
+ if (stored.kind === "unmaterialized-surface" && stored.surface !== void 0) return {
2131
+ surface: stored.surface,
2132
+ profile: null,
2133
+ refusal: stored.refusal ?? null
2134
+ };
2135
+ throw new Error(`improve(): node ${node.nodeId} holds no profile search artifact`);
2136
+ }
2137
+ };
2138
+ }
2139
+ //#endregion
1977
2140
  //#region src/improvement/search-method.ts
1978
2141
  /** Build Runtime's native search for `improve(profile, { method })`. */
1979
2142
  function searchMethod(options) {
@@ -2025,14 +2188,12 @@ const SEARCH_METHOD_SOURCE = {
2025
2188
  };
2026
2189
  async function runSearchImprovement(profile, opts) {
2027
2190
  const { method, claim: inputClaim, agent, executionRunId, recordCell, trainScenarios, selectionScenarios, testScenarios, judges: _judges, surface: _surface, executionRef: _executionRef, validateCandidate: _validateCandidate, findings: _findings, skills: _skills, profileComponents: _profileComponents, minimumLift: _minimumLift, subject: _subject, costCeiling, costLedger: inputCostLedger, seed = 42, signal, workerSlots, budgetPool, trace, ...campaignOptions } = opts;
2028
- if (!isImproveSearchMethod(method)) throw new ConfigError("improve(): a search needs method: searchMethod(...)");
2029
- const claim = defineEvaluationClaim(inputClaim);
2030
- if (claim.minimumEffect === void 0) throw new ConfigError("improve(): a search claim needs minimumEffect for its power check");
2031
- for (const [name, split] of [
2032
- ["trainScenarios", trainScenarios],
2033
- ["selectionScenarios", selectionScenarios],
2034
- ["testScenarios", testScenarios]
2035
- ]) if (!Array.isArray(split) || split.length === 0) throw new ConfigError(`improve(): a search needs at least one of ${name}`);
2191
+ const splitScenarios = {
2192
+ trainScenarios,
2193
+ selectionScenarios,
2194
+ testScenarios
2195
+ };
2196
+ const claim = assertSearchRequest(method, inputClaim, splitScenarios);
2036
2197
  const { allocation, policy, lanes } = method;
2037
2198
  const reps = allocation.reps;
2038
2199
  const concurrency = lanes.reduce((total, lane) => total + lane.capacity, 0);
@@ -2059,121 +2220,34 @@ async function runSearchImprovement(profile, opts) {
2059
2220
  runDir: searchDir,
2060
2221
  costCeilingUsd: costCeiling
2061
2222
  });
2062
- const unitOf = (scenario) => summarizeEvaluationUnits(claim, [scenario]).units[0].id;
2063
- const tasks = (scenarios) => scenarios.map((scenario) => ({
2064
- taskId: scenario.id,
2065
- unitId: unitOf(scenario),
2066
- source: {
2067
- uri: `scenario://${scenario.id}`,
2068
- revision: canonicalCandidateDigest$1(scenario)
2069
- }
2070
- }));
2071
- const splits = {
2072
- train: tasks(trainScenarios),
2073
- selection: tasks(selectionScenarios),
2074
- test: tasks(testScenarios)
2075
- };
2076
- const developmentUnits = new Set([...splits.train, ...splits.selection].map((task) => task.unitId));
2077
- const execution = {
2078
- model: prepared.searchIdentity.model,
2079
- agent: prepared.searchIdentity.agent,
2080
- benchmark: {
2081
- uri: "agent-runtime:improve-splits",
2082
- revision: canonicalCandidateDigest$1({
2083
- development: prepared.identity.developmentSplitDigest,
2084
- test: prepared.identity.finalTestSplitDigest
2085
- })
2086
- }
2087
- };
2088
- const reservedClaimUsd = Math.max(method.claimReserveUsd ?? 0, searchClaimReserveUsd({
2089
- testTasks: splits.test.length,
2090
- reps,
2091
- cellUsd: Math.max(...lanes.map((lane) => lane.cellUsd))
2092
- }));
2093
- const recorder = await SearchRecorder.open({
2094
- ledger: storage.kind === "filesystem" ? openSearchLedger({
2095
- path: `${searchDir}/ledger.jsonl`,
2096
- searchId
2097
- }) : openSearchLedger({
2098
- path: `${searchDir}/ledger.jsonl`,
2099
- searchId,
2100
- store: storage
2101
- }),
2102
- storage
2103
- }, {
2104
- subject: prepared.searchIdentity.subject,
2105
- process: {
2106
- name: method.name,
2107
- executionRef: SEARCH_METHOD_SOURCE
2108
- },
2109
- artifactKind: "agent-profile",
2110
- objective: {
2111
- metric: "composite",
2112
- direction: "maximize",
2113
- judge: prepared.searchIdentity.judge,
2114
- claim
2115
- },
2116
- splits: {
2117
- ...splits,
2118
- heldOutUnits: splits.test.every((task) => !developmentUnits.has(task.unitId))
2119
- },
2120
- policy: {
2121
- expansion: policy.name,
2122
- allocation: allocation.name,
2123
- seed
2124
- },
2125
- budget: {
2126
- maxUsd: costCeiling ?? null,
2127
- maxCells: null,
2128
- maxNodes: 1 + method.maxExpansions * method.childrenPerProposal,
2129
- deadline: method.deadline,
2130
- maxConcurrency: concurrency,
2131
- reservedClaimUsd: costCeiling === void 0 ? 0 : reservedClaimUsd
2132
- },
2133
- containment: null,
2134
- derivedFrom: null,
2135
- identity: execution
2136
- });
2137
- const nodeOf = (candidateSurface) => {
2138
- const surface = immutableCandidateValue(candidateSurface);
2139
- try {
2140
- return {
2141
- surface,
2142
- profile: prepared.materializeCandidate(surface),
2143
- refusal: null
2144
- };
2145
- } catch (error) {
2146
- return {
2147
- surface,
2148
- profile: null,
2149
- refusal: refusalText(error)
2150
- };
2151
- }
2223
+ const execution = searchExecutionIdentity(prepared);
2224
+ const recorder = await openSearchRecorder({
2225
+ method,
2226
+ prepared,
2227
+ claim,
2228
+ splits: searchSplits(claim, splitScenarios),
2229
+ storage,
2230
+ searchId,
2231
+ searchDir,
2232
+ seed,
2233
+ costCeiling,
2234
+ concurrency,
2235
+ execution
2236
+ });
2237
+ const session = {
2238
+ method,
2239
+ prepared,
2240
+ searchId,
2241
+ costLedger,
2242
+ recorder
2152
2243
  };
2153
2244
  const profiles = /* @__PURE__ */ new Map();
2154
- const profiled = profileCodec(prepared);
2155
- const codec = {
2156
- node(recorder, node) {
2157
- const registered = profiled.node(recorder, node);
2158
- profiles.set(searchNodeId(searchId, registered.artifactDigest), node.profile);
2159
- return registered;
2160
- },
2161
- diff: profiled.diff,
2162
- load(recorder, node) {
2163
- const loaded = profiled.load(recorder, node);
2164
- profiles.set(node.nodeId, loaded.profile);
2165
- return loaded;
2166
- }
2167
- };
2245
+ const codec = registeringProfileCodec(prepared, searchId, profiles);
2168
2246
  for (const node of (await recorder.state()).nodes()) codec.load(recorder, node);
2169
2247
  await reconcileInterruptedSearchCalls(costLedger, searchId, lanes);
2170
- const laneRefusal = (nodeProfile) => {
2171
- const refusals = lanes.map((lane) => `${lane.name}: ${lane.refusal(nodeProfile)}`);
2172
- return lanes.some((lane) => lane.refusal(nodeProfile) === void 0) ? null : `no lane can run the profile (${refusals.join("; ")})`;
2173
- };
2174
- const root = nodeOf(prepared.baselineSurface);
2248
+ const root = profileNodeOf(prepared, prepared.baselineSurface);
2175
2249
  if (root.profile) {
2176
- const refused = laneRefusal(root.profile);
2250
+ const refused = laneRefusal(lanes, root.profile);
2177
2251
  if (refused) throw new ConfigError(`improve(): the baseline profile cannot run: ${refused}`);
2178
2252
  }
2179
2253
  const scenarios = [
@@ -2181,11 +2255,7 @@ async function runSearchImprovement(profile, opts) {
2181
2255
  ...selectionScenarios,
2182
2256
  ...testScenarios
2183
2257
  ];
2184
- const scenarioIds = /* @__PURE__ */ new Set();
2185
- for (const scenario of scenarios) {
2186
- if (scenarioIds.has(scenario.id)) throw new ConfigError(`improve(): scenario '${scenario.id}' appears in more than one split`);
2187
- scenarioIds.add(scenario.id);
2188
- }
2258
+ assertDistinctScenarios(scenarios);
2189
2259
  const executor = searchExecutor({
2190
2260
  lanes,
2191
2261
  profileOf(nodeId) {
@@ -2200,167 +2270,164 @@ async function runSearchImprovement(profile, opts) {
2200
2270
  ...workerSlots ? { workerSlots } : {},
2201
2271
  ...budgetPool ? { budgetPool } : {},
2202
2272
  ...trace ? { trace } : {},
2203
- async runAttempt({ work, lane, context, costLedger: cellLedger, span }) {
2204
- const nodeProfile = work.artifact.profile;
2205
- if (!nodeProfile) throw new Error(`improve(): refused node ${work.nodeId} was given a cell`);
2206
- const profileDigest = canonicalAgentProfileDigest(nodeProfile);
2207
- let fault;
2208
- const cell = (await runCampaign({
2209
- ...campaignOptions,
2210
- ...lane.kind === "subscription" && campaignOptions.expectUsage === void 0 ? { expectUsage: "off" } : {},
2211
- scenarios,
2212
- judges: prepared.judges.map((judge) => ({
2213
- ...judge,
2214
- async score(input) {
2215
- try {
2216
- return await judge.score(input);
2217
- } catch (error) {
2218
- if (isSearchEnvironmentFault(error)) fault = error;
2219
- throw error;
2220
- }
2221
- }
2222
- })),
2223
- seed,
2224
- reps,
2225
- storage,
2226
- runDir: `${searchDir}/nodes/${profileDigest.slice(7)}`,
2227
- dispatchRef: `improve:${evaluationRef}:${profileDigest}`,
2228
- async dispatch(scenario, ctx) {
2229
- try {
2230
- return await agent(nodeProfile, scenario, {
2231
- ...ctx,
2232
- search: context
2233
- });
2234
- } catch (error) {
2235
- if (isSearchEnvironmentFault(error)) fault = error;
2236
- throw error;
2237
- }
2238
- },
2239
- cellFilter: ({ scenario, rep }) => scenario.id === work.taskId && rep === work.rep,
2240
- resumable: true,
2241
- maxConcurrency: 1,
2242
- costLedger: cellLedger,
2243
- costPhase: `search.${work.stage}`,
2244
- buildTraceWriter: () => ({
2245
- span,
2246
- flush: async () => {}
2247
- }),
2248
- signal: work.signal
2249
- })).cells[0];
2250
- if (!cell) throw new Error(`improve(): cell ${work.cellId} produced no campaign cell`);
2251
- const result = campaignCellSearchResult(cell, {
2252
- execution,
2253
- lane: lane.name
2254
- });
2255
- const execRunId = cell.error ? null : executionRunId?.(cell.artifact) ?? null;
2256
- const settled = faultResult(execRunId && context.trace ? {
2257
- ...result,
2258
- traceRef: {
2259
- traceId: context.trace.traceId,
2260
- execRunId,
2261
- spansWritten: null,
2262
- spansDropped: null
2263
- }
2264
- } : result, fault);
2265
- return recordCell && settled.outcome.status !== "errored" ? {
2266
- ...settled,
2267
- recordCell: (finalResult) => recordCell({
2268
- cell,
2269
- search: context,
2270
- result: finalResult
2271
- })
2272
- } : settled;
2273
- }
2274
- });
2275
- const { proposer } = method;
2276
- const proposerSource = {
2277
- uri: `proposer:${proposer.kind}`,
2278
- revision: canonicalCandidateDigest$1({
2279
- kind: proposer.kind,
2280
- propose: Function.prototype.toString.call(proposer.propose),
2281
- decide: proposer.decide ? Function.prototype.toString.call(proposer.decide) : null
2273
+ runAttempt: searchCellRunner({
2274
+ campaignOptions,
2275
+ prepared,
2276
+ agent,
2277
+ executionRunId,
2278
+ recordCell,
2279
+ scenarios,
2280
+ seed,
2281
+ reps,
2282
+ storage,
2283
+ searchDir,
2284
+ evaluationRef,
2285
+ execution
2282
2286
  })
2283
- };
2284
- const proposalPhase = "search.proposal";
2285
- const port = {
2286
- name: proposer.kind,
2287
- kind: "optimizer",
2288
- source: proposerSource,
2289
- execution: {
2290
- kind: "deterministic",
2291
- source: proposerSource
2287
+ });
2288
+ const startedAt = Date.now();
2289
+ return decideSearchResult({
2290
+ session,
2291
+ codec,
2292
+ result: await runSearch({
2293
+ recorder,
2294
+ root,
2295
+ codec,
2296
+ policy,
2297
+ allocation,
2298
+ proposer: searchProposerPort(session),
2299
+ executor,
2300
+ admit: (node) => admissionRefusal(session, node),
2301
+ maxExpansions: method.maxExpansions,
2302
+ maxAttempts: method.maxAttempts,
2303
+ ...signal ? { signal } : {}
2304
+ }),
2305
+ costCeiling,
2306
+ startedAt
2307
+ });
2308
+ }
2309
+ /** Open the search's ledger: a fresh search records its opening, an interrupted one continues. */
2310
+ function openSearchRecorder(input) {
2311
+ const { method, prepared, claim, splits, storage, searchId, searchDir, seed, costCeiling } = input;
2312
+ const { policy, allocation, lanes } = method;
2313
+ const reservedClaimUsd = Math.max(method.claimReserveUsd ?? 0, searchClaimReserveUsd({
2314
+ testTasks: splits.test.length,
2315
+ reps: allocation.reps,
2316
+ cellUsd: Math.max(...lanes.map((lane) => lane.cellUsd))
2317
+ }));
2318
+ return SearchRecorder.open({
2319
+ ledger: storage.kind === "filesystem" ? openSearchLedger({
2320
+ path: `${searchDir}/ledger.jsonl`,
2321
+ searchId
2322
+ }) : openSearchLedger({
2323
+ path: `${searchDir}/ledger.jsonl`,
2324
+ searchId,
2325
+ store: storage
2326
+ }),
2327
+ storage
2328
+ }, {
2329
+ subject: prepared.searchIdentity.subject,
2330
+ process: {
2331
+ name: method.name,
2332
+ executionRef: SEARCH_METHOD_SOURCE
2292
2333
  },
2293
- childrenPerProposal: method.childrenPerProposal,
2294
- async propose(request) {
2295
- const tags = searchCallTags(searchId, { operation: request.operationId });
2296
- const state = await recorder.state();
2297
- const parents = request.parents.map(({ nodeId, artifact }) => ({
2298
- nodeId,
2299
- artifact: artifact.surface
2300
- }));
2301
- const context = Object.freeze({
2302
- currentSurface: parents[0]?.artifact ?? prepared.baselineSurface,
2303
- operator: request.operator,
2304
- parents,
2305
- train: searchProposerView(state),
2306
- summary: renderSearchSummary(state, { split: "train" }),
2307
- history: [],
2308
- findings: prepared.findings,
2309
- populationSize: method.childrenPerProposal,
2310
- generation: request.expansion,
2311
- signal: request.signal,
2312
- costLedger: taggedLedger(costLedger, tags),
2313
- costPhase: proposalPhase
2314
- });
2315
- const decision = proposer.decide?.({ history: [] });
2316
- const proposed = decision?.stop ? [] : await proposer.propose(context);
2317
- if (!Array.isArray(proposed)) throw new ConfigError("improve(): the search proposer must return an array");
2318
- const fresh = costLedger.list({
2319
- phase: proposalPhase,
2320
- tags
2321
- });
2322
- return {
2323
- children: proposed.slice(0, method.childrenPerProposal).map((child) => isProposedCandidate(child) ? {
2324
- artifact: nodeOf(child.surface),
2325
- label: child.label,
2326
- rationale: child.rationale,
2327
- ...child.attribution ? { attribution: child.attribution } : {}
2328
- } : {
2329
- artifact: nodeOf(child),
2330
- label: "",
2331
- rationale: ""
2332
- }),
2333
- ...decision?.stop ? { stop: decision.reason ?? "the proposer decided to stop" } : {},
2334
- accounting: searchReceiptAccounting(fresh),
2335
- execution: searchProposalExecution(fresh, prepared.searchIdentity.model.provider, proposerSource)
2336
- };
2334
+ artifactKind: "agent-profile",
2335
+ objective: {
2336
+ metric: "composite",
2337
+ direction: "maximize",
2338
+ judge: prepared.searchIdentity.judge,
2339
+ claim
2340
+ },
2341
+ splits,
2342
+ policy: {
2343
+ expansion: policy.name,
2344
+ allocation: allocation.name,
2345
+ seed
2346
+ },
2347
+ budget: {
2348
+ maxUsd: costCeiling ?? null,
2349
+ maxCells: null,
2350
+ maxNodes: 1 + method.maxExpansions * method.childrenPerProposal,
2351
+ deadline: method.deadline,
2352
+ maxConcurrency: input.concurrency,
2353
+ reservedClaimUsd: costCeiling === void 0 ? 0 : reservedClaimUsd
2354
+ },
2355
+ containment: null,
2356
+ derivedFrom: null,
2357
+ identity: input.execution
2358
+ });
2359
+ }
2360
+ /** The request is a search with a claim that can be powered, and every split has scenarios. */
2361
+ function assertSearchRequest(method, inputClaim, splits) {
2362
+ if (!isImproveSearchMethod(method)) throw new ConfigError("improve(): a search needs method: searchMethod(...)");
2363
+ const claim = defineEvaluationClaim(inputClaim);
2364
+ if (claim.minimumEffect === void 0) throw new ConfigError("improve(): a search claim needs minimumEffect for its power check");
2365
+ for (const [name, split] of Object.entries(splits)) if (!Array.isArray(split) || split.length === 0) throw new ConfigError(`improve(): a search needs at least one of ${name}`);
2366
+ return claim;
2367
+ }
2368
+ function assertDistinctScenarios(scenarios) {
2369
+ const scenarioIds = /* @__PURE__ */ new Set();
2370
+ for (const scenario of scenarios) {
2371
+ if (scenarioIds.has(scenario.id)) throw new ConfigError(`improve(): scenario '${scenario.id}' appears in more than one split`);
2372
+ scenarioIds.add(scenario.id);
2373
+ }
2374
+ }
2375
+ /** The search tasks of each split, and whether the test units are held out of development. */
2376
+ function searchSplits(claim, scenarios) {
2377
+ const unitOf = (scenario) => summarizeEvaluationUnits(claim, [scenario]).units[0].id;
2378
+ const tasks = (list) => list.map((scenario) => ({
2379
+ taskId: scenario.id,
2380
+ unitId: unitOf(scenario),
2381
+ source: {
2382
+ uri: `scenario://${scenario.id}`,
2383
+ revision: canonicalCandidateDigest$1(scenario)
2337
2384
  }
2385
+ }));
2386
+ const splits = {
2387
+ train: tasks(scenarios.trainScenarios),
2388
+ selection: tasks(scenarios.selectionScenarios),
2389
+ test: tasks(scenarios.testScenarios)
2338
2390
  };
2339
- const admit = (node) => {
2340
- if (node.refusal !== null || node.profile === null) return node.refusal;
2341
- try {
2342
- prepared.admitCandidate(node.profile, node.surface);
2343
- } catch (error) {
2344
- return refusalText(error);
2391
+ const developmentUnits = new Set([...splits.train, ...splits.selection].map((task) => task.unitId));
2392
+ return {
2393
+ ...splits,
2394
+ heldOutUnits: splits.test.every((task) => !developmentUnits.has(task.unitId))
2395
+ };
2396
+ }
2397
+ function searchExecutionIdentity(prepared) {
2398
+ return {
2399
+ model: prepared.searchIdentity.model,
2400
+ agent: prepared.searchIdentity.agent,
2401
+ benchmark: {
2402
+ uri: "agent-runtime:improve-splits",
2403
+ revision: canonicalCandidateDigest$1({
2404
+ development: prepared.identity.developmentSplitDigest,
2405
+ test: prepared.identity.finalTestSplitDigest
2406
+ })
2345
2407
  }
2346
- const paths = privateValuePaths([node.surface]);
2347
- if (paths.length > 0) return `the surface carries a credential or private value at ${paths.slice(0, 8).join(", ")}`;
2348
- return laneRefusal(node.profile);
2349
2408
  };
2350
- const startedAt = Date.now();
2351
- const result = await runSearch({
2352
- recorder,
2353
- root,
2354
- codec,
2355
- policy,
2356
- allocation,
2357
- proposer: port,
2358
- executor,
2359
- admit,
2360
- maxExpansions: method.maxExpansions,
2361
- maxAttempts: method.maxAttempts,
2362
- ...signal ? { signal } : {}
2363
- });
2409
+ }
2410
+ /** Why no lane can run the profile, or null when one can. */
2411
+ function laneRefusal(lanes, nodeProfile) {
2412
+ const refusals = lanes.map((lane) => `${lane.name}: ${lane.refusal(nodeProfile)}`);
2413
+ return lanes.some((lane) => lane.refusal(nodeProfile) === void 0) ? null : `no lane can run the profile (${refusals.join("; ")})`;
2414
+ }
2415
+ /** Why a node is refused admission to the search, or null when it is admitted. */
2416
+ function admissionRefusal({ prepared, method }, node) {
2417
+ if (node.refusal !== null || node.profile === null) return node.refusal;
2418
+ try {
2419
+ prepared.admitCandidate(node.profile, node.surface);
2420
+ } catch (error) {
2421
+ return searchRefusalText(error);
2422
+ }
2423
+ const paths = privateValuePaths([node.surface]);
2424
+ if (paths.length > 0) return `the surface carries a credential or private value at ${paths.slice(0, 8).join(", ")}`;
2425
+ return laneRefusal(method.lanes, node.profile);
2426
+ }
2427
+ /** Read the closed search back from its ledger and decide whether its leader ships. */
2428
+ async function decideSearchResult(input) {
2429
+ const { session, codec, result, costCeiling, startedAt } = input;
2430
+ const { method, prepared, searchId, costLedger, recorder } = session;
2364
2431
  const { state } = result;
2365
2432
  const claimResult = result.claim;
2366
2433
  if (!claimResult || !result.claimVerification) throw new Error(`improve(): search ${searchId} closed without a claim`);
@@ -2411,77 +2478,6 @@ async function runSearchImprovement(profile, opts) {
2411
2478
  async dispose() {}
2412
2479
  };
2413
2480
  }
2414
- /** Profiles as search artifacts: nodes by profile digest, edges by profile diffs. */
2415
- function profileCodec(prepared) {
2416
- const surfaceId = prepared.surface;
2417
- return {
2418
- node(recorder, node) {
2419
- if (node.profile) {
2420
- const artifact = recorder.blob("agent-profile", {
2421
- kind: "agent-profile",
2422
- profile: node.profile
2423
- });
2424
- return {
2425
- artifactDigest: canonicalAgentProfileDigest(node.profile),
2426
- artifact,
2427
- surfaces: [{
2428
- surfaceId,
2429
- kind: "agent-profile",
2430
- artifact
2431
- }]
2432
- };
2433
- }
2434
- const artifact = recorder.blob("surface", {
2435
- kind: "unmaterialized-surface",
2436
- surface: node.surface,
2437
- refusal: node.refusal
2438
- });
2439
- return {
2440
- artifactDigest: canonicalCandidateDigest$1({
2441
- kind: "unmaterialized-surface",
2442
- surface: node.surface
2443
- }),
2444
- artifact,
2445
- surfaces: [{
2446
- surfaceId,
2447
- kind: "agent-profile",
2448
- artifact
2449
- }]
2450
- };
2451
- },
2452
- diff(recorder, parent, child) {
2453
- if (!parent.profile || !child.profile) return { unknown: "a surface that does not materialize has no profile to diff" };
2454
- const diffs = diffAgentProfiles(parent.profile, child.profile);
2455
- const to = canonicalAgentProfileDigest(child.profile);
2456
- if (canonicalAgentProfileDigest(diffs.reduce(applyAgentProfileDiff, parent.profile)) !== to) return { unknown: "the Interface profile diffs do not reproduce the child profile" };
2457
- return recorder.blob("diff", {
2458
- kind: "agent-profile-diff",
2459
- from: canonicalAgentProfileDigest(parent.profile),
2460
- to,
2461
- diffs
2462
- });
2463
- },
2464
- load(recorder, node) {
2465
- const stored = recorder.readBlob(node.artifact);
2466
- if (stored.kind === "agent-profile") {
2467
- const parsed = agentProfileSchema.safeParse(stored.profile);
2468
- if (!parsed.success) throw new Error(`improve(): node ${node.nodeId} holds an invalid AgentProfile`);
2469
- const loaded = immutableCandidateValue(parsed.data);
2470
- return {
2471
- surface: prepared.surfaceOf(loaded),
2472
- profile: loaded,
2473
- refusal: null
2474
- };
2475
- }
2476
- if (stored.kind === "unmaterialized-surface" && stored.surface !== void 0) return {
2477
- surface: stored.surface,
2478
- profile: null,
2479
- refusal: stored.refusal ?? null
2480
- };
2481
- throw new Error(`improve(): node ${node.nodeId} holds no profile search artifact`);
2482
- }
2483
- };
2484
- }
2485
2481
  /** Everything that decides which search a call continues. */
2486
2482
  function searchMethodDescriptor(method) {
2487
2483
  return {
@@ -2504,6 +2500,148 @@ function searchMethodDescriptor(method) {
2504
2500
  maxAttempts: method.maxAttempts
2505
2501
  };
2506
2502
  }
2503
+ const PROPOSAL_PHASE = "search.proposal";
2504
+ function searchProposerPort(session) {
2505
+ const { method, prepared, recorder, costLedger, searchId } = session;
2506
+ const { proposer } = method;
2507
+ const proposerSource = {
2508
+ uri: `proposer:${proposer.kind}`,
2509
+ revision: canonicalCandidateDigest$1({
2510
+ kind: proposer.kind,
2511
+ propose: Function.prototype.toString.call(proposer.propose),
2512
+ decide: proposer.decide ? Function.prototype.toString.call(proposer.decide) : null
2513
+ })
2514
+ };
2515
+ return {
2516
+ name: proposer.kind,
2517
+ kind: "optimizer",
2518
+ source: proposerSource,
2519
+ execution: {
2520
+ kind: "deterministic",
2521
+ source: proposerSource
2522
+ },
2523
+ childrenPerProposal: method.childrenPerProposal,
2524
+ async propose(request) {
2525
+ const tags = searchCallTags(searchId, { operation: request.operationId });
2526
+ const state = await recorder.state();
2527
+ const parents = request.parents.map(({ nodeId, artifact }) => ({
2528
+ nodeId,
2529
+ artifact: artifact.surface
2530
+ }));
2531
+ const context = Object.freeze({
2532
+ currentSurface: parents[0]?.artifact ?? prepared.baselineSurface,
2533
+ operator: request.operator,
2534
+ parents,
2535
+ train: searchProposerView(state),
2536
+ summary: renderSearchSummary(state, { split: "train" }),
2537
+ history: [],
2538
+ findings: prepared.findings,
2539
+ populationSize: method.childrenPerProposal,
2540
+ generation: request.expansion,
2541
+ signal: request.signal,
2542
+ costLedger: taggedLedger(costLedger, tags),
2543
+ costPhase: PROPOSAL_PHASE
2544
+ });
2545
+ const decision = proposer.decide?.({ history: [] });
2546
+ const proposed = decision?.stop ? [] : await proposer.propose(context);
2547
+ if (!Array.isArray(proposed)) throw new ConfigError("improve(): the search proposer must return an array");
2548
+ const fresh = costLedger.list({
2549
+ phase: PROPOSAL_PHASE,
2550
+ tags
2551
+ });
2552
+ return {
2553
+ children: proposed.slice(0, method.childrenPerProposal).map((child) => isProposedCandidate(child) ? {
2554
+ artifact: profileNodeOf(prepared, child.surface),
2555
+ label: child.label,
2556
+ rationale: child.rationale,
2557
+ ...child.attribution ? { attribution: child.attribution } : {}
2558
+ } : {
2559
+ artifact: profileNodeOf(prepared, child),
2560
+ label: "",
2561
+ rationale: ""
2562
+ }),
2563
+ ...decision?.stop ? { stop: decision.reason ?? "the proposer decided to stop" } : {},
2564
+ accounting: searchReceiptAccounting(fresh),
2565
+ execution: searchProposalExecution(fresh, prepared.searchIdentity.model.provider, proposerSource)
2566
+ };
2567
+ }
2568
+ };
2569
+ }
2570
+ /** The executor's cell runner for one search. */
2571
+ function searchCellRunner(env) {
2572
+ const { campaignOptions, prepared, executionRunId, recordCell, execution } = env;
2573
+ return async ({ work, lane, context, costLedger: cellLedger, span }) => {
2574
+ const nodeProfile = work.artifact.profile;
2575
+ if (!nodeProfile) throw new Error(`improve(): refused node ${work.nodeId} was given a cell`);
2576
+ const profileDigest = canonicalAgentProfileDigest(nodeProfile);
2577
+ let fault;
2578
+ const cell = (await runCampaign({
2579
+ ...campaignOptions,
2580
+ ...lane.kind === "subscription" && campaignOptions.expectUsage === void 0 ? { expectUsage: "off" } : {},
2581
+ scenarios: env.scenarios,
2582
+ judges: prepared.judges.map((judge) => ({
2583
+ ...judge,
2584
+ async score(input) {
2585
+ try {
2586
+ return await judge.score(input);
2587
+ } catch (error) {
2588
+ if (isSearchEnvironmentFault(error)) fault = error;
2589
+ throw error;
2590
+ }
2591
+ }
2592
+ })),
2593
+ seed: env.seed,
2594
+ reps: env.reps,
2595
+ storage: env.storage,
2596
+ runDir: `${env.searchDir}/nodes/${profileDigest.slice(7)}`,
2597
+ dispatchRef: `improve:${env.evaluationRef}:${profileDigest}`,
2598
+ async dispatch(scenario, ctx) {
2599
+ try {
2600
+ return await env.agent(nodeProfile, scenario, {
2601
+ ...ctx,
2602
+ search: context
2603
+ });
2604
+ } catch (error) {
2605
+ if (isSearchEnvironmentFault(error)) fault = error;
2606
+ throw error;
2607
+ }
2608
+ },
2609
+ cellFilter: ({ scenario, rep }) => scenario.id === work.taskId && rep === work.rep,
2610
+ resumable: true,
2611
+ maxConcurrency: 1,
2612
+ costLedger: cellLedger,
2613
+ costPhase: `search.${work.stage}`,
2614
+ buildTraceWriter: () => ({
2615
+ span,
2616
+ flush: async () => {}
2617
+ }),
2618
+ signal: work.signal
2619
+ })).cells[0];
2620
+ if (!cell) throw new Error(`improve(): cell ${work.cellId} produced no campaign cell`);
2621
+ const result = campaignCellSearchResult(cell, {
2622
+ execution,
2623
+ lane: lane.name
2624
+ });
2625
+ const execRunId = cell.error ? null : executionRunId?.(cell.artifact) ?? null;
2626
+ const settled = faultResult(execRunId && context.trace ? {
2627
+ ...result,
2628
+ traceRef: {
2629
+ traceId: context.trace.traceId,
2630
+ execRunId,
2631
+ spansWritten: null,
2632
+ spansDropped: null
2633
+ }
2634
+ } : result, fault);
2635
+ return recordCell && settled.outcome.status !== "errored" ? {
2636
+ ...settled,
2637
+ recordCell: (finalResult) => recordCell({
2638
+ cell,
2639
+ search: context,
2640
+ result: finalResult
2641
+ })
2642
+ } : settled;
2643
+ };
2644
+ }
2507
2645
  /**
2508
2646
  * An environment fault the agent raised settles retryable. The campaign reports every error as
2509
2647
  * final because it applies its own retry policy; here the kernel owns retries.
@@ -2516,16 +2654,12 @@ function faultResult(result, fault) {
2516
2654
  ...result.outcome,
2517
2655
  error: {
2518
2656
  code: "environment-fault",
2519
- message: refusalText(fault),
2657
+ message: searchRefusalText(fault),
2520
2658
  retryable: true
2521
2659
  }
2522
2660
  }
2523
2661
  };
2524
2662
  }
2525
- /** A refusal as the ledger stores it: redacted with the share profile. */
2526
- function refusalText(error) {
2527
- return redact(error instanceof Error ? error.message : String(error), { profile: "share" }).value;
2528
- }
2529
2663
  //#endregion
2530
2664
  //#region src/improvement/training.ts
2531
2665
  function positiveLimit(value, label) {
@@ -2540,10 +2674,9 @@ async function hashFile(path, maximum, signal, capture = false) {
2540
2674
  const hash = createHash("sha256");
2541
2675
  const chunks = [];
2542
2676
  let bytes = 0;
2543
- for await (const chunk of file.createReadStream({
2544
- autoClose: false,
2545
- signal
2546
- })) {
2677
+ signal.throwIfAborted();
2678
+ for await (const chunk of file.createReadStream({ autoClose: false })) {
2679
+ signal.throwIfAborted();
2547
2680
  bytes += chunk.length;
2548
2681
  if (bytes > maximum) throw new Error("artifact exceeded its byte limit");
2549
2682
  if (bytes > before.size) throw new Error("artifact changed while being hashed");
@@ -2918,4 +3051,4 @@ async function improve(profileOrCode, opts) {
2918
3051
  //#endregion
2919
3052
  export { assertProfileTrainingIsHeldOut as a, withMethodRuntimeControls as c, commandVerifier as d, addRuntimeUsage as f, assertCandidateValidator as i, rethrowAfterCleanup as l, isUsageAmount as m, createCommandProfileTrainer as n, parseExecutionRef as o, createRuntimeUsageTotals as p, searchMethod as r, privateValuePaths as s, improve as t, agenticGenerator as u };
2920
3053
 
2921
- //# sourceMappingURL=improve-BOXTLWJ3.js.map
3054
+ //# sourceMappingURL=improve-BZXt2qM0.js.map