@sema-agent/core 1.452.0 → 2.0.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 (162) hide show
  1. package/dist/agents/agent-transcript-tool.d.ts +15 -0
  2. package/dist/agents/agent-transcript-tool.js +122 -0
  3. package/dist/agents/cascade.js +5 -8
  4. package/dist/agents/repair-loop.d.ts +1 -1
  5. package/dist/agents/repair-loop.js +1 -1
  6. package/dist/agents/retain-ledger.d.ts +60 -0
  7. package/dist/agents/retain-ledger.js +225 -0
  8. package/dist/agents/roster-store.d.ts +1 -0
  9. package/dist/agents/roster-store.js +9 -0
  10. package/dist/agents/send-message-tool.d.ts +41 -0
  11. package/dist/agents/send-message-tool.js +469 -0
  12. package/dist/agents/subagent.d.ts +33 -108
  13. package/dist/agents/subagent.js +22 -834
  14. package/dist/agents/teacher.d.ts +0 -1
  15. package/dist/agents/teacher.js +0 -9
  16. package/dist/bin/sema-tb.js +3 -3
  17. package/dist/brain/anthropic.js +15 -40
  18. package/dist/brain/openai.js +15 -40
  19. package/dist/brain/reasoning.d.ts +1 -1
  20. package/dist/brain/stream-engine.js +2 -2
  21. package/dist/brain/stream-shared.d.ts +17 -0
  22. package/dist/brain/stream-shared.js +50 -0
  23. package/dist/brain/timeout.d.ts +1 -1
  24. package/dist/brain/timeout.js +1 -1
  25. package/dist/config/catalog.js +1 -3
  26. package/dist/config/defaults.d.ts +6 -0
  27. package/dist/config/defaults.js +6 -0
  28. package/dist/core/ask-question.d.ts +1 -1
  29. package/dist/core/background-shell.d.ts +1 -1
  30. package/dist/core/checkpoint-store.d.ts +0 -1
  31. package/dist/core/exec-gate.d.ts +1 -1
  32. package/dist/core/exec-output-tail.d.ts +1 -0
  33. package/dist/core/exec-output-tail.js +6 -0
  34. package/dist/core/file-snapshot-store.d.ts +1 -1
  35. package/dist/core/fs-write-gate-policy.d.ts +1 -1
  36. package/dist/core/git-worktree-env.d.ts +1 -1
  37. package/dist/core/hooks.d.ts +1 -1
  38. package/dist/core/image-downsample.d.ts +44 -0
  39. package/dist/core/image-downsample.js +97 -0
  40. package/dist/core/lsp.d.ts +1 -1
  41. package/dist/core/mcp.d.ts +3 -45
  42. package/dist/core/mcp.js +2 -97
  43. package/dist/core/media-byte-cap.d.ts +1 -1
  44. package/dist/core/memory-recall.d.ts +0 -12
  45. package/dist/core/memory-recall.js +0 -222
  46. package/dist/core/memory.d.ts +0 -6
  47. package/dist/core/memory.js +0 -88
  48. package/dist/core/message-utils.d.ts +1 -1
  49. package/dist/core/oracle-isolation.d.ts +1 -1
  50. package/dist/core/remote-env.d.ts +1 -1
  51. package/dist/core/roles.d.ts +1 -1
  52. package/dist/core/runner/active-skill-scope.d.ts +1 -1
  53. package/dist/core/runner/assemble-result.d.ts +0 -2
  54. package/dist/core/runner/memory-consolidation.d.ts +1 -1
  55. package/dist/core/runner/prepare-memory.d.ts +16 -0
  56. package/dist/core/runner/prepare-memory.js +191 -0
  57. package/dist/core/runner/prepare-task.d.ts +4 -28
  58. package/dist/core/runner/prepare-task.js +19 -207
  59. package/dist/core/runner/prompt-suggestions.d.ts +1 -1
  60. package/dist/core/runner/runtask.d.ts +2 -3
  61. package/dist/core/runner/runtask.js +337 -529
  62. package/dist/core/runner/session-rule-policy.d.ts +1 -1
  63. package/dist/core/runner/synthetic-tools.d.ts +5 -5
  64. package/dist/core/runner/synthetic-tools.js +4 -4
  65. package/dist/core/runner/tool-disclosure.d.ts +3 -3
  66. package/dist/core/runner/tool-disclosure.js +2 -2
  67. package/dist/core/runner/tool-output-projection.d.ts +6 -0
  68. package/dist/core/runner/tool-output-projection.js +81 -0
  69. package/dist/core/runtime.d.ts +1 -1
  70. package/dist/core/scheduler.d.ts +1 -1
  71. package/dist/core/sensitive-path-policy.d.ts +1 -1
  72. package/dist/core/session-reconcile.d.ts +1 -1
  73. package/dist/core/session-store.d.ts +2 -1
  74. package/dist/core/session-store.js +2 -1
  75. package/dist/core/session.d.ts +0 -1
  76. package/dist/core/task-registry-agent.d.ts +115 -0
  77. package/dist/core/task-registry-agent.js +1163 -0
  78. package/dist/core/task-registry-monitor.d.ts +13 -0
  79. package/dist/core/task-registry-monitor.js +347 -0
  80. package/dist/core/task-registry-shared.d.ts +294 -0
  81. package/dist/core/task-registry-shared.js +271 -0
  82. package/dist/core/task-registry-workflow.d.ts +5 -0
  83. package/dist/core/task-registry-workflow.js +143 -0
  84. package/dist/core/task-registry.d.ts +51 -216
  85. package/dist/core/task-registry.js +127 -1940
  86. package/dist/core/task-tool-shape.d.ts +32 -0
  87. package/dist/core/task-tool-shape.js +83 -0
  88. package/dist/core/tool-policy.d.ts +1 -2
  89. package/dist/core/tool-result-budget.d.ts +1 -1
  90. package/dist/core/tool-result-store.d.ts +2 -2
  91. package/dist/core/tool-result-store.js +1 -1
  92. package/dist/core/tools.d.ts +2 -3
  93. package/dist/core/tools.js +0 -8
  94. package/dist/core/trace.d.ts +2 -2
  95. package/dist/core/types.d.ts +0 -29
  96. package/dist/core/workflow-run-store-contract.d.ts +1 -1
  97. package/dist/core/workflow-run-store-contract.js +19 -19
  98. package/dist/engine/execution-env/node-execution-env.d.ts +13 -0
  99. package/dist/engine/execution-env/node-execution-env.js +55 -25
  100. package/dist/engine/lsp/frame-decoder.d.ts +1 -1
  101. package/dist/engine/lsp/frame-decoder.js +1 -1
  102. package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
  103. package/dist/engine/lsp/stdio-lsp-transport.js +2 -2
  104. package/dist/index.d.ts +16 -14
  105. package/dist/index.js +13 -11
  106. package/dist/internal/harness-types.d.ts +7 -0
  107. package/dist/internal/harness-types.js +1 -0
  108. package/dist/internal/harness.d.ts +1 -7
  109. package/dist/internal/harness.js +1 -0
  110. package/dist/orchestration/goal.js +2 -2
  111. package/dist/orchestration/run-workflow-tool.d.ts +1 -1
  112. package/dist/orchestration/workflow-types.d.ts +193 -0
  113. package/dist/orchestration/workflow-types.js +54 -0
  114. package/dist/orchestration/workflow.d.ts +3 -193
  115. package/dist/orchestration/workflow.js +7 -59
  116. package/dist/prompt-assembly/assemble.d.ts +3 -3
  117. package/dist/prompt-assembly/assemble.js +0 -16
  118. package/dist/prompt-assembly/explain.d.ts +1 -1
  119. package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
  120. package/dist/prompts/default.d.ts +1 -6
  121. package/dist/prompts/default.js +3 -11
  122. package/dist/scenarios/env.d.ts +1 -1
  123. package/dist/scenarios/full-body.d.ts +0 -3
  124. package/dist/scenarios/full-body.js +0 -2
  125. package/dist/stores/file/background-agent-store.d.ts +2 -15
  126. package/dist/stores/file/background-agent-store.js +30 -100
  127. package/dist/stores/file/checkpoint-store.d.ts +1 -14
  128. package/dist/stores/file/checkpoint-store.js +47 -119
  129. package/dist/stores/file/file-snapshot-store.d.ts +1 -1
  130. package/dist/stores/file/shared-ledger.d.ts +35 -0
  131. package/dist/stores/file/shared-ledger.js +94 -0
  132. package/dist/stores/file/workflow-run-store.d.ts +1 -14
  133. package/dist/stores/file/workflow-run-store.js +24 -93
  134. package/dist/tools/fs/bash-readonly-classifier.d.ts +8 -0
  135. package/dist/tools/fs/bash-readonly-classifier.js +166 -0
  136. package/dist/tools/fs/fs-bash.d.ts +31 -0
  137. package/dist/tools/fs/fs-bash.js +672 -0
  138. package/dist/tools/fs/fs-pdf.d.ts +22 -0
  139. package/dist/tools/fs/fs-pdf.js +236 -0
  140. package/dist/tools/fs/fs-read.d.ts +8 -0
  141. package/dist/tools/fs/fs-read.js +286 -0
  142. package/dist/tools/fs/fs-search-tools.d.ts +5 -0
  143. package/dist/tools/fs/fs-search-tools.js +173 -0
  144. package/dist/tools/fs/fs-shared.d.ts +55 -0
  145. package/dist/tools/fs/fs-shared.js +144 -0
  146. package/dist/tools/fs/fs-write.d.ts +8 -0
  147. package/dist/tools/fs/fs-write.js +423 -0
  148. package/dist/tools/fs/index.d.ts +12 -65
  149. package/dist/tools/fs/index.js +26 -2090
  150. package/dist/tools/fs/pdf.d.ts +1 -1
  151. package/dist/tools/fs/repo-map.d.ts +3 -3
  152. package/dist/tools/fs/repo-map.js +1 -1
  153. package/dist/tools/fs/safety.d.ts +1 -1
  154. package/dist/tools/fs/search.d.ts +1 -1
  155. package/dist/tools/monitor.d.ts +1 -1
  156. package/dist/tools/scheduler-tools.d.ts +1 -1
  157. package/dist/tools/web.d.ts +2 -2
  158. package/dist/tools/web.js +1 -1
  159. package/dist/tools/worktree.d.ts +1 -1
  160. package/package.json +1 -1
  161. package/dist/core/dynamic-recall.d.ts +0 -32
  162. package/dist/core/dynamic-recall.js +0 -76
@@ -18,8 +18,6 @@ import { runWithBrainTelemetry, runWithStatusSink } from "../../brain/status-sin
18
18
  import { expandTiers, resolveModel, resolveTaskModel } from "../roles.js";
19
19
  import { runSideQuery } from "../side-query.js";
20
20
  import { generatePromptSuggestions } from "./prompt-suggestions.js";
21
- import { runMemoryConsolidation } from "./memory-consolidation.js";
22
- import { advanceCursorAfterInline } from "../consolidate-scope.js";
23
21
  import { PushQueue } from "../push-queue.js";
24
22
  import { TtlSessionStore } from "../session-store.js";
25
23
  import { toImageContent } from "./image.js";
@@ -41,16 +39,24 @@ import { toolPolicyNameSets } from "../tool-policy.js";
41
39
  import { defaultTaskRegistry } from "../task-registry.js";
42
40
  import { discloseDroppedPending, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
43
41
  import { ToolDetachHub } from "../tool-detach.js";
44
- import { createBrainMemorySelector } from "../memory-recall.js";
45
- import { buildRecallQuery, runDynamicRecall } from "../dynamic-recall.js";
46
- import { guardedMemoryStore } from "../memory.js";
47
- import { releaseSession } from "../../agents/session-util.js";
48
- export const DEFAULT_MAX_TURNS = 1000;
42
+ import { DEFAULT_MAX_TURNS } from "../../config/defaults.js";
43
+ import { structuredFrom, toolOutputFrom } from "./tool-output-projection.js";
44
+ export { DEFAULT_MAX_TURNS };
45
+ function createRunState() {
46
+ return {
47
+ telemetry: { cacheFamily: "input-excludes-cached", pricing: { inputPer1M: 0, outputPer1M: 0 }, tracer: undefined, taskId: "", taskStart: 0, taskStartMonotonic: 0, cacheBreakReported: false },
48
+ degrade: { degradeToModel: undefined, degraded: undefined, outputErrorStreak: 0, outputInvalid: false, recordDegraded: () => { } },
49
+ limits: { turnsExceeded: false, budgetHit: undefined, outputRetryCap: 0, effectiveMaxTurns: undefined },
50
+ budget: { remainingMicroUsd: undefined, maxCostMicroUsd: undefined, overBudget: () => false, streamCancel: false, callOutputChars: 0, lastStreamBudgetCheck: 0, projectedOverBudget: () => false },
51
+ turn: { callStartAt: undefined, firstTokenAt: undefined, turnUsage: undefined, turnUsageMissing: false, turnStopReason: undefined, lastTurnHadToolCalls: false },
52
+ counters: { nudgesSent: 0, callCutoffs: 0, repetitionCuts: 0, repetitionSpared: 0, repetitionEvents: [], REPETITION_EVENTS_CAP: 0, preemptIgnoredReported: false, wroteThisRun: false, finalVerifyInjections: 0, groundingSignalPreR9: false, groundingSignalPostR9: false, cadenceTurns: 0 },
53
+ attach: { attachmentsCfg: undefined, agentListingOn: false, skillsListingOn: false, attachState: undefined, dateState: undefined, instrProbe: undefined, instrState: undefined },
54
+ };
55
+ }
49
56
  const MAX_CONSECUTIVE_COMPACTION_FAILURES = 3;
50
57
  const STOP_HOOK_BLOCK_CAP = 8;
51
58
  const COMPACTION_REGROWTH_FACTOR = 1.5;
52
59
  const COMPACTION_FREED_EPSILON = 256;
53
- const MAX_TOOL_END_OUTPUT_CHARS = 16384;
54
60
  const BATCH_RESPONSE_MAX_CHARS = 500;
55
61
  function batchResponseDigest(result) {
56
62
  const content = result !== null && typeof result === "object" ? result.content : result;
@@ -220,9 +226,7 @@ export class Runner {
220
226
  parentConstraintRegistry = new Map();
221
227
  static PARENT_CONSTRAINT_REGISTRY_CAP = 1024;
222
228
  constructor(deps) {
223
- this.deps = deps.memoryStore
224
- ? { ...deps, memoryStore: guardedMemoryStore(deps.memoryStore, deps.utilityGate) }
225
- : deps;
229
+ this.deps = deps;
226
230
  if (this.deps.tiers) {
227
231
  this.deps = { ...this.deps, models: expandTiers(this.deps.models, this.deps.tiers) };
228
232
  }
@@ -333,7 +337,6 @@ export class Runner {
333
337
  const queue = new PushQueue();
334
338
  const detachHub = new ToolDetachHub();
335
339
  let resultValue;
336
- let consolidationDone = Promise.resolve();
337
340
  let suggestionsDone = Promise.resolve([]);
338
341
  let handle;
339
342
  let publishReady;
@@ -378,8 +381,6 @@ export class Runner {
378
381
  }
379
382
  this.parentConstraintRegistry.set(r.checkpointToken, pcs);
380
383
  }
381
- }, (p) => {
382
- consolidationDone = p;
383
384
  }, (p) => {
384
385
  suggestionsDone = p;
385
386
  }, (h) => {
@@ -440,7 +441,7 @@ export class Runner {
440
441
  result: "",
441
442
  errorMessage: err instanceof Error ? err.message : String(err),
442
443
  errorCode: code,
443
- stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costUsd: 0 },
444
+ stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costMicroUsd: 0 },
444
445
  };
445
446
  emitTrace(spec.tracer ?? this.deps.tracer, () => ({
446
447
  kind: "task.end",
@@ -499,10 +500,6 @@ export class Runner {
499
500
  await settled;
500
501
  return resultValue;
501
502
  },
502
- consolidation: async () => {
503
- await run.catch(() => { });
504
- await consolidationDone.catch(() => { });
505
- },
506
503
  suggestions: async () => {
507
504
  await run.catch(() => { });
508
505
  return suggestionsDone.catch(() => []);
@@ -626,7 +623,7 @@ export class Runner {
626
623
  })()),
627
624
  };
628
625
  }
629
- async runLocked(spec, queue, setResult, onConsolidation, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef) {
626
+ async runLocked(spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef) {
630
627
  const prepareResume = resume
631
628
  ? {
632
629
  leafId: resume.cp.leafId,
@@ -641,9 +638,6 @@ export class Runner {
641
638
  resume.outcome.decision === "allow",
642
639
  }
643
640
  : undefined;
644
- const memorySelector = this.deps.memoryRecall?.enabled || this.deps.dynamicRecall?.enabled
645
- ? (this.deps.memoryRecall?.select ?? createBrainMemorySelector(this, { model: this.deps.memoryRecall?.selectorModel, release: (id) => releaseSession(this, id) }))
646
- : undefined;
647
641
  const taskNotificationQueue = new SystemInjectionQueue();
648
642
  const detachHub = internals?.detachHub ?? new ToolDetachHub();
649
643
  let notificationHarness;
@@ -691,7 +685,7 @@ export class Runner {
691
685
  resolve("queued");
692
686
  });
693
687
  };
694
- const prepared = await prepareTask(spec, this.deps, this.sessions, prepareResume, memorySelector, {
688
+ const prepared = await prepareTask(spec, this.deps, this.sessions, prepareResume, {
695
689
  ...(internals ?? {}),
696
690
  detachHub,
697
691
  onTaskNotification: (notification, opts) => {
@@ -731,8 +725,8 @@ export class Runner {
731
725
  }
732
726
  }
733
727
  onReady({ harness: prepared.harness, abortController: prepared.abortController });
734
- const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costUsd: 0, costMicroUsd: 0 };
735
- prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - taskStartMonotonic) });
728
+ const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
729
+ prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
736
730
  if (resume &&
737
731
  resume.cp.suspendedAt !== undefined &&
738
732
  (resume.cp.gate.kind === "human" ||
@@ -746,12 +740,12 @@ export class Runner {
746
740
  const gateToolName = "toolName" in resume.cp.gate ? resume.cp.gate.toolName : undefined;
747
741
  prepared.humanReviewRef.gates.push({ kind: resume.cp.gate.kind, waitMs, ...(decision !== undefined ? { decision } : {}), ...(gateToolName !== undefined ? { toolName: gateToolName } : {}) });
748
742
  }
749
- let cacheFamily = cacheFamilyOf(prepared.model);
750
- let pricing = this.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
751
- let degradeToModel;
743
+ const rs = createRunState();
744
+ rs.telemetry.cacheFamily = cacheFamilyOf(prepared.model);
745
+ rs.telemetry.pricing = this.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
752
746
  if (spec.degrade) {
753
747
  try {
754
- degradeToModel = resolveModel(spec.degrade.to, this.deps.models);
748
+ rs.degrade.degradeToModel = resolveModel(spec.degrade.to, this.deps.models);
755
749
  }
756
750
  catch (e) {
757
751
  this.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId });
@@ -761,25 +755,23 @@ export class Runner {
761
755
  if (famOverride && !["input-includes-cached", "input-excludes-cached", "openai", "anthropic"].includes(famOverride)) {
762
756
  this.deps.onError?.(new Error(`prompt-cache: unrecognized model.params.promptCacheFamily "${famOverride}" — ignored (fell back to model.api inference). Use "input-includes-cached" | "input-excludes-cached" (aliases "openai" | "anthropic").`), { phase: "prompt-cache", sessionId: prepared.sessionId });
763
757
  }
764
- let turnsExceeded = false;
765
- let budgetHit;
766
- let degraded;
767
- let outputErrorStreak = 0;
768
- let outputInvalid = false;
769
- let cacheBreakReported = false;
770
- const outputRetryCap = resolveOutputRetries(spec.outputRetries);
771
- const effectiveMaxTurns = resolveMaxTurns(spec.limits);
772
- const tracer = spec.tracer ?? this.deps.tracer;
773
- const taskId = spec.taskId ?? prepared.sessionId;
758
+ rs.limits.turnsExceeded = false;
759
+ rs.degrade.outputErrorStreak = 0;
760
+ rs.degrade.outputInvalid = false;
761
+ rs.telemetry.cacheBreakReported = false;
762
+ rs.limits.outputRetryCap = resolveOutputRetries(spec.outputRetries);
763
+ rs.limits.effectiveMaxTurns = resolveMaxTurns(spec.limits);
764
+ rs.telemetry.tracer = spec.tracer ?? this.deps.tracer;
765
+ rs.telemetry.taskId = spec.taskId ?? prepared.sessionId;
774
766
  if (taskIdRef) {
775
- taskIdRef.current = taskId;
767
+ taskIdRef.current = rs.telemetry.taskId;
776
768
  taskIdRef.sessionId = prepared.sessionId;
777
769
  }
778
- const taskStart = Date.now();
779
- const taskStartMonotonic = performance.now();
780
- void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, taskId)).catch(() => { });
781
- const recordDegraded = (info, toModel) => {
782
- if (degraded !== undefined)
770
+ rs.telemetry.taskStart = Date.now();
771
+ rs.telemetry.taskStartMonotonic = performance.now();
772
+ void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId)).catch(() => { });
773
+ rs.degrade.recordDegraded = (info, toModel) => {
774
+ if (rs.degrade.degraded !== undefined)
783
775
  return;
784
776
  let m = toModel;
785
777
  if (!m) {
@@ -791,16 +783,16 @@ export class Runner {
791
783
  }
792
784
  }
793
785
  if (m) {
794
- pricing = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
795
- cacheFamily = cacheFamilyOf(m);
786
+ rs.telemetry.pricing = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
787
+ rs.telemetry.cacheFamily = cacheFamilyOf(m);
796
788
  }
797
789
  else {
798
790
  if (this.deps.pricing?.[info.to])
799
- pricing = this.deps.pricing[info.to];
800
- cacheFamily = "input-excludes-cached";
791
+ rs.telemetry.pricing = this.deps.pricing[info.to];
792
+ rs.telemetry.cacheFamily = "input-excludes-cached";
801
793
  }
802
- degraded = info;
803
- emitTrace(tracer, () => ({ kind: "task.degraded", version: 1, taskId, from: info.from, to: info.to, reason: info.reason, atTurn: info.atTurn, ts: Date.now() }));
794
+ rs.degrade.degraded = info;
795
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "task.degraded", version: 1, taskId: rs.telemetry.taskId, from: info.from, to: info.to, reason: info.reason, atTurn: info.atTurn, ts: Date.now() }));
804
796
  try {
805
797
  this.deps.onError?.(new Error(`degraded to a cheaper model: ${info.from} → ${info.to} (${info.reason}) at turn ${info.atTurn}`), { phase: "degraded", sessionId: prepared.sessionId });
806
798
  }
@@ -808,63 +800,62 @@ export class Runner {
808
800
  }
809
801
  };
810
802
  const sliceWindowMicroUsd = spec.maxCostUsd !== undefined ? Math.round(spec.maxCostUsd * 1e6) : undefined;
811
- const remainingMicroUsd = prepared.resourceLedger
803
+ rs.budget.remainingMicroUsd = prepared.resourceLedger
812
804
  ? remainingBudgetMicroUsd(prepared.resourceLedger)
813
805
  : spec.resourceSuspend?.totalBudgetUsd !== undefined
814
806
  ? Math.round(spec.resourceSuspend.totalBudgetUsd * 1e6)
815
807
  : undefined;
816
- const maxCostMicroUsd = sliceWindowMicroUsd !== undefined && remainingMicroUsd !== undefined
817
- ? Math.min(sliceWindowMicroUsd, remainingMicroUsd)
818
- : (sliceWindowMicroUsd ?? remainingMicroUsd);
819
- const overBudget = () => (maxCostMicroUsd !== undefined && stats.costMicroUsd > maxCostMicroUsd) ||
808
+ rs.budget.maxCostMicroUsd =
809
+ sliceWindowMicroUsd !== undefined && rs.budget.remainingMicroUsd !== undefined
810
+ ? Math.min(sliceWindowMicroUsd, rs.budget.remainingMicroUsd)
811
+ : (sliceWindowMicroUsd ?? rs.budget.remainingMicroUsd);
812
+ rs.budget.overBudget = () => (rs.budget.maxCostMicroUsd !== undefined && stats.costMicroUsd > rs.budget.maxCostMicroUsd) ||
820
813
  (spec.maxTokens !== undefined && stats.tokens > spec.maxTokens);
821
- const streamCancel = (spec.budgetStreamCancel ?? maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
822
- let callOutputChars = 0;
823
- let lastStreamBudgetCheck = 0;
824
- const projectedOverBudget = () => {
825
- const estOut = Math.ceil(callOutputChars / 4);
826
- if (maxCostMicroUsd !== undefined) {
827
- const estOutMicro = computeCostMicroUsd({ totalInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: estOut }, pricing);
828
- if (stats.costMicroUsd + estOutMicro > maxCostMicroUsd)
814
+ rs.budget.streamCancel =
815
+ (spec.budgetStreamCancel ?? rs.budget.maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
816
+ rs.budget.callOutputChars = 0;
817
+ rs.budget.lastStreamBudgetCheck = 0;
818
+ rs.budget.projectedOverBudget = () => {
819
+ const estOut = Math.ceil(rs.budget.callOutputChars / 4);
820
+ if (rs.budget.maxCostMicroUsd !== undefined) {
821
+ const estOutMicro = computeCostMicroUsd({ totalInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: estOut }, rs.telemetry.pricing);
822
+ if (stats.costMicroUsd + estOutMicro > rs.budget.maxCostMicroUsd)
829
823
  return true;
830
824
  }
831
825
  return spec.maxTokens !== undefined && stats.tokens + estOut > spec.maxTokens;
832
826
  };
833
- let callStartAt;
834
- let firstTokenAt;
835
- let turnUsage;
836
- let turnUsageMissing = false;
837
- let turnStopReason;
838
- let nudgesSent = 0;
839
- let callCutoffs = 0;
840
- let repetitionCuts = 0;
841
- let repetitionSpared = 0;
842
- const repetitionEvents = [];
843
- const REPETITION_EVENTS_CAP = 20;
844
- let preemptIgnoredReported = false;
845
- let wroteThisRun = false;
846
- let finalVerifyInjections = 0;
847
- let groundingSignalPreR9 = false;
848
- let groundingSignalPostR9 = false;
849
- const attachmentsCfg = spec.attachments;
850
- const agentListingOn = attachmentsCfg?.agentListing !== false && eventDefaultOn("agent_listing");
851
- const skillsListingOn = attachmentsCfg?.skillsListing !== false && eventDefaultOn("skills_listing");
852
- const listingsLive = (agentListingOn && prepared.agentListing !== undefined) || (skillsListingOn && prepared.skillsListing !== undefined);
853
- const attachState = attachmentsCfg !== undefined || listingsLive ? createAttachmentState() : undefined;
854
- const dateState = prepared.dateChange !== undefined ? { announcedDate: prepared.dateChange.legDate } : undefined;
855
- const instrProbe = this.deps.probeInstructionSources;
856
- const instrState = instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
857
- ? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
858
- : undefined;
859
- let cadenceTurns = 0;
860
- let lastTurnHadToolCalls = false;
861
- if (attachState !== undefined && attachmentsCfg?.backgroundTasks === true) {
827
+ rs.turn.turnUsageMissing = false;
828
+ rs.counters.nudgesSent = 0;
829
+ rs.counters.callCutoffs = 0;
830
+ rs.counters.repetitionCuts = 0;
831
+ rs.counters.repetitionSpared = 0;
832
+ rs.counters.repetitionEvents = [];
833
+ rs.counters.REPETITION_EVENTS_CAP = 20;
834
+ rs.counters.preemptIgnoredReported = false;
835
+ rs.counters.wroteThisRun = false;
836
+ rs.counters.finalVerifyInjections = 0;
837
+ rs.counters.groundingSignalPreR9 = false;
838
+ rs.counters.groundingSignalPostR9 = false;
839
+ rs.attach.attachmentsCfg = spec.attachments;
840
+ rs.attach.agentListingOn = rs.attach.attachmentsCfg?.agentListing !== false && eventDefaultOn("agent_listing");
841
+ rs.attach.skillsListingOn = rs.attach.attachmentsCfg?.skillsListing !== false && eventDefaultOn("skills_listing");
842
+ const listingsLive = (rs.attach.agentListingOn && prepared.agentListing !== undefined) || (rs.attach.skillsListingOn && prepared.skillsListing !== undefined);
843
+ rs.attach.attachState = rs.attach.attachmentsCfg !== undefined || listingsLive ? createAttachmentState() : undefined;
844
+ rs.attach.dateState = prepared.dateChange !== undefined ? { announcedDate: prepared.dateChange.legDate } : undefined;
845
+ rs.attach.instrProbe = this.deps.probeInstructionSources;
846
+ rs.attach.instrState =
847
+ rs.attach.instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
848
+ ? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
849
+ : undefined;
850
+ rs.counters.cadenceTurns = 0;
851
+ rs.turn.lastTurnHadToolCalls = false;
852
+ if (rs.attach.attachState !== undefined && rs.attach.attachmentsCfg?.backgroundTasks === true) {
862
853
  try {
863
854
  const branch = await prepared.session.getBranch();
864
855
  for (let i = branch.length - 1; i >= 0; i--) {
865
856
  const e = branch[i];
866
857
  if (e.type === "compaction") {
867
- attachState.postCompactPending = true;
858
+ rs.attach.attachState.postCompactPending = true;
868
859
  break;
869
860
  }
870
861
  if (e.type === "message" && e.message.role === "assistant")
@@ -874,9 +865,9 @@ export class Runner {
874
865
  catch {
875
866
  }
876
867
  }
877
- if (attachState !== undefined &&
878
- ((agentListingOn && prepared.agentListing?.seedAnnounced === true) ||
879
- (skillsListingOn && prepared.skillsListing?.seedAnnounced === true))) {
868
+ if (rs.attach.attachState !== undefined &&
869
+ ((rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) ||
870
+ (rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true))) {
880
871
  const cpListings = resume?.cp.state.announcedListings;
881
872
  let entryListings;
882
873
  try {
@@ -927,23 +918,23 @@ export class Runner {
927
918
  return undefined;
928
919
  }
929
920
  };
930
- if (agentListingOn && prepared.agentListing?.seedAnnounced === true) {
921
+ if (rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) {
931
922
  const listing = prepared.agentListing;
932
923
  if (cpListings?.agents === undefined && entryListings?.agents !== undefined) {
933
924
  const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
934
- attachState.announcedAgentTypes = new Map(entryListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
925
+ rs.attach.attachState.announcedAgentTypes = new Map(entryListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
935
926
  prepared.announcedListingsRef.agents = [...entryListings.agents];
936
927
  if (entryListings.models !== undefined) {
937
- attachState.announcedModels = [...entryListings.models];
928
+ rs.attach.attachState.announcedModels = [...entryListings.models];
938
929
  prepared.announcedListingsRef.models = [...entryListings.models];
939
930
  }
940
931
  }
941
932
  else if (cpListings?.agents !== undefined) {
942
933
  const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
943
- attachState.announcedAgentTypes = new Map(cpListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
934
+ rs.attach.attachState.announcedAgentTypes = new Map(cpListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
944
935
  prepared.announcedListingsRef.agents = [...cpListings.agents];
945
936
  if (cpListings.models !== undefined) {
946
- attachState.announcedModels = [...cpListings.models];
937
+ rs.attach.attachState.announcedModels = [...cpListings.models];
947
938
  prepared.announcedListingsRef.models = [...cpListings.models];
948
939
  }
949
940
  }
@@ -955,26 +946,26 @@ export class Runner {
955
946
  });
956
947
  if (replayed !== undefined) {
957
948
  const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
958
- attachState.announcedAgentTypes = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
949
+ rs.attach.attachState.announcedAgentTypes = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
959
950
  prepared.announcedListingsRef.agents = [...replayed];
960
951
  const replayedModels = replayAnnouncedModels(await branchLoad());
961
952
  if (replayedModels !== undefined) {
962
- attachState.announcedModels = [...replayedModels];
953
+ rs.attach.attachState.announcedModels = [...replayedModels];
963
954
  prepared.announcedListingsRef.models = [...replayedModels];
964
955
  }
965
956
  }
966
957
  }
967
958
  }
968
- if (skillsListingOn && prepared.skillsListing?.seedAnnounced === true) {
959
+ if (rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true) {
969
960
  const listing = prepared.skillsListing;
970
961
  if (cpListings?.skills === undefined && entryListings?.skills !== undefined) {
971
962
  const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
972
- attachState.announcedSkills = new Map(entryListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
963
+ rs.attach.attachState.announcedSkills = new Map(entryListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
973
964
  prepared.announcedListingsRef.skills = [...entryListings.skills];
974
965
  }
975
966
  else if (cpListings?.skills !== undefined) {
976
967
  const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
977
- attachState.announcedSkills = new Map(cpListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
968
+ rs.attach.attachState.announcedSkills = new Map(cpListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
978
969
  prepared.announcedListingsRef.skills = [...cpListings.skills];
979
970
  }
980
971
  else {
@@ -985,7 +976,7 @@ export class Runner {
985
976
  });
986
977
  if (replayed !== undefined) {
987
978
  const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
988
- attachState.announcedSkills = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
979
+ rs.attach.attachState.announcedSkills = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
989
980
  prepared.announcedListingsRef.skills = [...replayed];
990
981
  }
991
982
  }
@@ -1002,11 +993,11 @@ export class Runner {
1002
993
  const text = typeof m.content === "string"
1003
994
  ? m.content
1004
995
  : m.content.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : "")).join("\n");
1005
- if (text.includes("<system-reminder>[final verification]") && finalVerifyInjections < 2)
1006
- finalVerifyInjections += 1;
996
+ if (text.includes("<system-reminder>[final verification]") && rs.counters.finalVerifyInjections < 2)
997
+ rs.counters.finalVerifyInjections += 1;
1007
998
  }
1008
- else if (m.role === "toolResult" && !wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
1009
- wroteThisRun = true;
999
+ else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
1000
+ rs.counters.wroteThisRun = true;
1010
1001
  }
1011
1002
  }
1012
1003
  }
@@ -1014,10 +1005,10 @@ export class Runner {
1014
1005
  }
1015
1006
  }
1016
1007
  let attachmentsInjected = 0;
1017
- const todoToolMounted = attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TodoWrite");
1018
- const taskToolsMounted = attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TaskCreate");
1008
+ const todoToolMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TodoWrite");
1009
+ const taskToolsMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TaskCreate");
1019
1010
  const writeFamilyByName = new Map();
1020
- if (attachState !== undefined) {
1011
+ if (rs.attach.attachState !== undefined) {
1021
1012
  for (const t of prepared.tools) {
1022
1013
  const family = writeFamilyOfCanonical(canonicalToolName(t.name));
1023
1014
  if (family !== undefined)
@@ -1029,11 +1020,11 @@ export class Runner {
1029
1020
  const toolStartAt = new Map();
1030
1021
  const turnToolSpan = {};
1031
1022
  const startedToolCallIds = new Set();
1032
- emitTrace(tracer, () => ({ kind: "task.start", version: 1, taskId, model: prepared.model.id, engineVersion: engineVersion(), ts: taskStart }));
1033
- emitTrace(tracer, () => ({
1023
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "task.start", version: 1, taskId: rs.telemetry.taskId, model: prepared.model.id, engineVersion: engineVersion(), ts: rs.telemetry.taskStart }));
1024
+ emitTrace(rs.telemetry.tracer, () => ({
1034
1025
  kind: "prompt.assembled",
1035
1026
  version: 1,
1036
- taskId,
1027
+ taskId: rs.telemetry.taskId,
1037
1028
  constitution: prepared.promptManifest.constitution,
1038
1029
  blocks: prepared.promptManifest.blocks,
1039
1030
  ...(prepared.promptManifest.sections ? { sections: prepared.promptManifest.sections } : {}),
@@ -1041,36 +1032,36 @@ export class Runner {
1041
1032
  ...(prepared.promptManifest.snapshot ? { snapshot: prepared.promptManifest.snapshot } : {}),
1042
1033
  ...(prepared.promptManifest.lowering ? { lowering: prepared.promptManifest.lowering } : {}),
1043
1034
  totalChars: prepared.promptManifest.blocks.reduce((n, b) => n + b.chars, 0),
1044
- ts: taskStart,
1035
+ ts: rs.telemetry.taskStart,
1045
1036
  }));
1046
- emitTrace(tracer, () => {
1037
+ emitTrace(rs.telemetry.tracer, () => {
1047
1038
  const reasons = declarationReasons(spec.configOverrides);
1048
1039
  return {
1049
1040
  kind: "config.assembled",
1050
1041
  version: 1,
1051
- taskId,
1042
+ taskId: rs.telemetry.taskId,
1052
1043
  catalogVersion: CONFIG_CATALOG_VERSION,
1053
1044
  fields: resolveEffectiveConfig(spec, { modelMaxTokens: prepared.model.maxTokens }),
1054
1045
  ...(Object.keys(reasons).length > 0 ? { overrideReasons: reasons } : {}),
1055
- ts: taskStart,
1046
+ ts: rs.telemetry.taskStart,
1056
1047
  };
1057
1048
  });
1058
1049
  if (prepared.turnSnapshot !== undefined) {
1059
- prepared.turnSnapshot.onChange = (snap) => emitTrace(tracer, () => ({
1050
+ prepared.turnSnapshot.onChange = (snap) => emitTrace(rs.telemetry.tracer, () => ({
1060
1051
  kind: "prompt.snapshot_changed",
1061
1052
  version: 1,
1062
- taskId,
1053
+ taskId: rs.telemetry.taskId,
1063
1054
  snapshot: { cacheIdentity: snap.cacheIdentity, elements: { ...snap.elements } },
1064
1055
  ts: Date.now(),
1065
1056
  }));
1066
1057
  }
1067
1058
  if (prepared.thinking && prepared.thinking !== "off" && prepared.model.reasoning === true) {
1068
- emitTrace(tracer, () => {
1059
+ emitTrace(rs.telemetry.tracer, () => {
1069
1060
  const r = resolveReasoning(prepared.thinking, prepared.model);
1070
1061
  return {
1071
1062
  kind: "reasoning.resolved",
1072
1063
  version: 1,
1073
- taskId,
1064
+ taskId: rs.telemetry.taskId,
1074
1065
  model: prepared.model.id,
1075
1066
  requested: r.requested,
1076
1067
  effective: r.effective,
@@ -1091,8 +1082,8 @@ export class Runner {
1091
1082
  const effectiveTimeoutMs = sliceWindowWalltimeMs !== undefined && remainingWalltimeBudgetMs !== undefined
1092
1083
  ? Math.min(sliceWindowWalltimeMs, remainingWalltimeBudgetMs)
1093
1084
  : (sliceWindowWalltimeMs ?? remainingWalltimeBudgetMs);
1094
- const walltimeDeadlineMs = effectiveTimeoutMs !== undefined ? taskStart + effectiveTimeoutMs : undefined;
1095
- const walltimeMonotonicDeadline = effectiveTimeoutMs !== undefined ? taskStartMonotonic + effectiveTimeoutMs : undefined;
1085
+ const walltimeDeadlineMs = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStart + effectiveTimeoutMs : undefined;
1086
+ const walltimeMonotonicDeadline = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStartMonotonic + effectiveTimeoutMs : undefined;
1096
1087
  if (prepared.callCapRef && prepared.suspendForResource === undefined && walltimeDeadlineMs !== undefined) {
1097
1088
  prepared.callCapRef.deadlineMs = walltimeDeadlineMs;
1098
1089
  if (spec.limits?.gracefulFinalize !== false) {
@@ -1109,7 +1100,7 @@ export class Runner {
1109
1100
  if (conf !== false && budgetSec && budgetSec > 0) {
1110
1101
  const [a, b] = conf?.at ?? [0.8, 0.95];
1111
1102
  if (a > 0 && a < 1) {
1112
- const t1 = taskStart + a * budgetSec * 1000;
1103
+ const t1 = rs.telemetry.taskStart + a * budgetSec * 1000;
1113
1104
  const remainMin = Math.max(1, Math.round((budgetSec * 1000 * (1 - a)) / 60_000));
1114
1105
  const budgetMin = Math.max(1, Math.round(budgetSec / 60));
1115
1106
  nudgeSchedule.push({
@@ -1117,7 +1108,7 @@ export class Runner {
1117
1108
  text: `<system-reminder>[deadline] ~${remainMin} min of the ~${budgetMin}-min wall-clock budget remain. ` +
1118
1109
  `Start converging: persist finished work (write files / commit), prefer completing in-flight items over opening new ones.</system-reminder>`,
1119
1110
  });
1120
- const t2 = taskStart + Math.max(Math.min(b * budgetSec * 1000, budgetSec * 1000 - 120_000), a * budgetSec * 1000 + 1);
1111
+ const t2 = rs.telemetry.taskStart + Math.max(Math.min(b * budgetSec * 1000, budgetSec * 1000 - 120_000), a * budgetSec * 1000 + 1);
1121
1112
  if (b > a && b < 1) {
1122
1113
  nudgeSchedule.push({
1123
1114
  atMs: t2,
@@ -1139,7 +1130,7 @@ export class Runner {
1139
1130
  `</system-reminder>`;
1140
1131
  const timeout = startTimeout(prepared.harness, prepared.abortController, walltimeMonotonicDeadline !== undefined ? walltimeMonotonicDeadline - performance.now() : undefined, prepared.suspendForResource !== undefined);
1141
1132
  const parentToolCallId = internals?.parentToolCallId;
1142
- const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: taskId } : { eventId: uuidv7() };
1133
+ const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: rs.telemetry.taskId } : { eventId: uuidv7() };
1143
1134
  notificationIdent = ident;
1144
1135
  const pushContent = (e) => {
1145
1136
  queue.push(e);
@@ -1168,121 +1159,41 @@ export class Runner {
1168
1159
  });
1169
1160
  };
1170
1161
  const telemetryEmit = (t) => {
1171
- emitTrace(tracer, () => t.kind === "failover"
1162
+ emitTrace(rs.telemetry.tracer, () => t.kind === "failover"
1172
1163
  ? {
1173
1164
  kind: "brain.failover",
1174
1165
  version: 1,
1175
- taskId,
1166
+ taskId: rs.telemetry.taskId,
1176
1167
  servedIndex: t.servedIndex,
1177
1168
  total: t.total,
1178
1169
  ...(t.errorCode !== undefined ? { errorCode: t.errorCode } : {}),
1179
1170
  ts: Date.now(),
1180
1171
  }
1181
1172
  : t.kind === "breaker"
1182
- ? { kind: "breaker.transition", version: 1, taskId, key: t.key, phase: t.phase, failures: t.failures, ts: Date.now() }
1173
+ ? { kind: "breaker.transition", version: 1, taskId: rs.telemetry.taskId, key: t.key, phase: t.phase, failures: t.failures, ts: Date.now() }
1183
1174
  : t.kind === "retry"
1184
- ? { kind: "brain.retry", version: 1, taskId, attempt: t.attempt, phase: t.phase, ts: Date.now() }
1185
- : { kind: "vision.placeholder", version: 1, taskId, count: t.count, ts: Date.now() });
1175
+ ? { kind: "brain.retry", version: 1, taskId: rs.telemetry.taskId, attempt: t.attempt, phase: t.phase, ts: Date.now() }
1176
+ : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
1186
1177
  };
1187
1178
  const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, fn));
1188
- const safeJsonLength = (v) => {
1189
- try {
1190
- return JSON.stringify(v)?.length;
1191
- }
1192
- catch {
1193
- return undefined;
1194
- }
1195
- };
1196
- const trunc = (s) => ({
1197
- text: s.slice(0, MAX_TOOL_END_OUTPUT_CHARS) + `…[+${s.length - MAX_TOOL_END_OUTPUT_CHARS} chars truncated]`,
1198
- totalChars: s.length,
1199
- });
1200
- const toolOutputFrom = (result) => {
1201
- const content = result !== null && typeof result === "object" ? result.content : result;
1202
- if (content === undefined)
1203
- return undefined;
1204
- if (typeof content === "string") {
1205
- if (content.length <= MAX_TOOL_END_OUTPUT_CHARS)
1206
- return { output: content, truncated: false };
1207
- const t = trunc(content);
1208
- return { output: t.text, truncated: true, totalChars: t.totalChars };
1209
- }
1210
- let serialized;
1211
- try {
1212
- serialized = JSON.stringify(content) ?? "";
1213
- }
1214
- catch {
1215
- return { output: "[unserializable tool output]", truncated: false };
1216
- }
1217
- if (serialized.length <= MAX_TOOL_END_OUTPUT_CHARS)
1218
- return { output: content, truncated: false };
1219
- let anyNonText = false;
1220
- let trueChars = 0;
1221
- const raw = Array.isArray(content)
1222
- ? content
1223
- .map((b) => {
1224
- const block = b;
1225
- if (block?.type === "text" && typeof block.text === "string") {
1226
- trueChars += block.text.length;
1227
- return block.text;
1228
- }
1229
- anyNonText = true;
1230
- trueChars += typeof block?.data === "string" ? block.data.length : (safeJsonLength(block) ?? 0);
1231
- return `[${typeof block?.type === "string" ? block.type : "block"}${typeof block?.mimeType === "string" ? `: ${block.mimeType}` : ""}]`;
1232
- })
1233
- .join("\n")
1234
- : serialized;
1235
- const totalChars = Array.isArray(content) ? trueChars : serialized.length;
1236
- if (raw.length > MAX_TOOL_END_OUTPUT_CHARS) {
1237
- const t = trunc(raw);
1238
- return { output: t.text, truncated: true, totalChars };
1239
- }
1240
- if (!anyNonText) {
1241
- return { output: raw, truncated: false };
1242
- }
1243
- return { output: raw, truncated: true, totalChars };
1244
- };
1245
- const CC_DETAIL_TYPES = new Set([
1246
- "edit", "multiedit", "create", "update", "bash", "notebook-edit", "notebook", "text", "grep", "glob", "mcp",
1247
- "agent", "task", "task-list", "task-output", "memory-saved", "workflow-run",
1248
- "web-fetch", "web-search", "todo", "cron-create", "cron-delete", "cron-list", "image",
1249
- "task-stop", "tool-search", "memory-recall", "repo-map", "fork", "enter-plan-mode", "exit-plan-mode",
1250
- ]);
1251
- const structuredFrom = (result) => {
1252
- const details = result !== null && typeof result === "object" ? result.details : undefined;
1253
- if (details === null || typeof details !== "object")
1254
- return undefined;
1255
- const t = details.type;
1256
- if (typeof t !== "string" || !CC_DETAIL_TYPES.has(t))
1257
- return undefined;
1258
- try {
1259
- const serialized = JSON.stringify(details) ?? "";
1260
- if (serialized.length > MAX_TOOL_END_OUTPUT_CHARS * 4)
1261
- return undefined;
1262
- }
1263
- catch {
1264
- return undefined;
1265
- }
1266
- return details;
1267
- };
1268
1179
  const onMessageUpdate = (event) => {
1269
1180
  const ev = event.assistantMessageEvent;
1270
- if (callStartAt === undefined)
1271
- callStartAt = Date.now();
1181
+ if (rs.turn.callStartAt === undefined)
1182
+ rs.turn.callStartAt = Date.now();
1272
1183
  if (ev.type === "text_delta" && ev.delta) {
1273
- if (firstTokenAt === undefined)
1274
- firstTokenAt = Date.now();
1184
+ if (rs.turn.firstTokenAt === undefined)
1185
+ rs.turn.firstTokenAt = Date.now();
1275
1186
  pushContent({ type: "text_delta", delta: ev.delta, ...ident() });
1276
1187
  }
1277
1188
  else if (ev.type === "thinking_delta" && ev.delta) {
1278
1189
  pushContent({ type: "reasoning_delta", delta: ev.delta, ...ident() });
1279
1190
  }
1280
- if (streamCancel && !budgetHit && (ev.type === "text_delta" || ev.type === "thinking_delta") && ev.delta) {
1281
- callOutputChars += ev.delta.length;
1282
- if (callOutputChars - lastStreamBudgetCheck >= 256) {
1283
- lastStreamBudgetCheck = callOutputChars;
1284
- if (projectedOverBudget()) {
1285
- budgetHit = "exceeded";
1191
+ if (rs.budget.streamCancel && !rs.limits.budgetHit && (ev.type === "text_delta" || ev.type === "thinking_delta") && ev.delta) {
1192
+ rs.budget.callOutputChars += ev.delta.length;
1193
+ if (rs.budget.callOutputChars - rs.budget.lastStreamBudgetCheck >= 256) {
1194
+ rs.budget.lastStreamBudgetCheck = rs.budget.callOutputChars;
1195
+ if (rs.budget.projectedOverBudget()) {
1196
+ rs.limits.budgetHit = "exceeded";
1286
1197
  void prepared.harness.abort();
1287
1198
  prepared.abortController.abort();
1288
1199
  }
@@ -1294,55 +1205,54 @@ export class Runner {
1294
1205
  if (event.entryId !== undefined && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")) {
1295
1206
  emitCommitted(event.entryId, m.role, m.role === "toolResult" ? m.toolCallId : undefined);
1296
1207
  }
1297
- if (degraded === undefined && m.role === "assistant") {
1208
+ if (rs.degrade.degraded === undefined && m.role === "assistant") {
1298
1209
  const deg = readDegradation(m);
1299
1210
  if (deg)
1300
- recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
1211
+ rs.degrade.recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
1301
1212
  }
1302
1213
  if (m.role === "assistant" && "usage" in m && m.usage) {
1303
1214
  if (m.usageMissing)
1304
- turnUsageMissing = true;
1215
+ rs.turn.turnUsageMissing = true;
1305
1216
  const u = m.usage;
1306
1217
  stats.tokens += u.totalTokens || 0;
1307
- const { totalInputTokens: turnInput, costMicroUsd: turnCostMicroUsd } = usageCostMicroUsd(cacheFamily, u, pricing);
1218
+ const { totalInputTokens: turnInput, costMicroUsd: turnCostMicroUsd } = usageCostMicroUsd(rs.telemetry.cacheFamily, u, rs.telemetry.pricing);
1308
1219
  stats.promptTokens += turnInput;
1309
1220
  stats.cachedTokens += u.cacheRead || 0;
1310
1221
  stats.cacheWriteTokens += u.cacheWrite || 0;
1311
1222
  stats.outputTokens += u.output || 0;
1312
- stats.costUsd += u.cost?.total || 0;
1313
1223
  stats.costMicroUsd += turnCostMicroUsd;
1314
- turnUsage ??= { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
1315
- turnUsage.inputTokens += turnInput;
1316
- turnUsage.outputTokens += u.output || 0;
1317
- turnUsage.cacheReadTokens += u.cacheRead || 0;
1318
- turnUsage.cacheWriteTokens += u.cacheWrite || 0;
1319
- turnUsage.costMicroUsd += turnCostMicroUsd;
1224
+ rs.turn.turnUsage ??= { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
1225
+ rs.turn.turnUsage.inputTokens += turnInput;
1226
+ rs.turn.turnUsage.outputTokens += u.output || 0;
1227
+ rs.turn.turnUsage.cacheReadTokens += u.cacheRead || 0;
1228
+ rs.turn.turnUsage.cacheWriteTokens += u.cacheWrite || 0;
1229
+ rs.turn.turnUsage.costMicroUsd += turnCostMicroUsd;
1320
1230
  const callEndAt = Date.now();
1321
1231
  const callIssuedAt = prepared.callIssuedAtRef?.current;
1322
1232
  if (prepared.callIssuedAtRef)
1323
1233
  prepared.callIssuedAtRef.current = undefined;
1324
1234
  const capLast = prepared.callCapRef?.state.last;
1325
1235
  if (prepared.callCapRef) {
1326
- recordCallSample(prepared.callCapRef.state, u.output || 0, callStartAt !== undefined ? callEndAt - callStartAt : 0, {
1236
+ recordCallSample(prepared.callCapRef.state, u.output || 0, rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0, {
1327
1237
  contentValid: m.usageMissing === true && hasRealContent(m.content),
1328
1238
  });
1329
- if (callStartAt !== undefined && firstTokenAt !== undefined) {
1330
- recordTtftSample(prepared.callCapRef.state, firstTokenAt - callStartAt);
1239
+ if (rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined) {
1240
+ recordTtftSample(prepared.callCapRef.state, rs.turn.firstTokenAt - rs.turn.callStartAt);
1331
1241
  }
1332
1242
  prepared.callCapRef.state.last = undefined;
1333
1243
  }
1334
- emitTrace(tracer, () => ({
1244
+ emitTrace(rs.telemetry.tracer, () => ({
1335
1245
  kind: "brain.call",
1336
1246
  version: 1,
1337
- taskId,
1247
+ taskId: rs.telemetry.taskId,
1338
1248
  model: m.model,
1339
1249
  provider: m.provider,
1340
1250
  promptTokens: turnInput,
1341
1251
  completionTokens: u.output || 0,
1342
1252
  cacheRead: u.cacheRead || 0,
1343
1253
  cacheWrite: u.cacheWrite || 0,
1344
- latencyMs: callStartAt !== undefined ? callEndAt - callStartAt : 0,
1345
- firstTokenMs: callStartAt !== undefined && firstTokenAt !== undefined ? firstTokenAt - callStartAt : undefined,
1254
+ latencyMs: rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0,
1255
+ firstTokenMs: rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined ? rs.turn.firstTokenAt - rs.turn.callStartAt : undefined,
1346
1256
  ...(callIssuedAt !== undefined ? { callStartedAt: callIssuedAt } : {}),
1347
1257
  costMicroUsd: turnCostMicroUsd,
1348
1258
  ...(capLast !== undefined ? { callCap: capLast.cap, capThinkingSkipped: capLast.thinkingSkipped } : {}),
@@ -1373,16 +1283,16 @@ export class Runner {
1373
1283
  cacheRead: u.cacheRead || 0,
1374
1284
  });
1375
1285
  if (finding) {
1376
- cacheBreakReported = true;
1286
+ rs.telemetry.cacheBreakReported = true;
1377
1287
  this.deps.onError?.(new Error(`prompt-cache: break at turn ${finding.turn} — cache_read ${finding.cacheReadBefore}→${finding.cacheReadAfter}; ${finding.detail}. See design/31.`), { phase: "prompt-cache", sessionId: prepared.sessionId, classification: finding.cause });
1378
1288
  }
1379
1289
  }
1380
1290
  }
1381
1291
  if (m.role === "assistant" && typeof m.stopReason === "string") {
1382
- turnStopReason = m.stopReason;
1292
+ rs.turn.turnStopReason = m.stopReason;
1383
1293
  }
1384
1294
  if (m.role === "assistant" && m.errorMessage === WALLTIME_CUTOFF_MESSAGE) {
1385
- callCutoffs += 1;
1295
+ rs.counters.callCutoffs += 1;
1386
1296
  if (prepared.callCapRef)
1387
1297
  prepared.callCapRef.finalizeMode = true;
1388
1298
  if (!finalizeInjected) {
@@ -1404,16 +1314,16 @@ export class Runner {
1404
1314
  ];
1405
1315
  for (const { action, e } of observed) {
1406
1316
  if (action === "cut")
1407
- repetitionCuts += 1;
1317
+ rs.counters.repetitionCuts += 1;
1408
1318
  else
1409
- repetitionSpared += 1;
1410
- if (repetitionEvents.length < REPETITION_EVENTS_CAP) {
1411
- repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment: e.segment });
1319
+ rs.counters.repetitionSpared += 1;
1320
+ if (rs.counters.repetitionEvents.length < rs.counters.REPETITION_EVENTS_CAP) {
1321
+ rs.counters.repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment: e.segment });
1412
1322
  }
1413
- emitTrace(tracer, () => ({
1323
+ emitTrace(rs.telemetry.tracer, () => ({
1414
1324
  kind: "repetition.detected",
1415
1325
  version: 1,
1416
- taskId,
1326
+ taskId: rs.telemetry.taskId,
1417
1327
  turn: stats.turns + 1,
1418
1328
  action,
1419
1329
  rule: e.rule,
@@ -1434,14 +1344,14 @@ export class Runner {
1434
1344
  }
1435
1345
  if (m.role === "assistant") {
1436
1346
  const content = m.content;
1437
- if (attachState !== undefined)
1438
- cadenceTurns = advanceCadenceClock(attachState, cadenceTurns, content, writeFamilyOf);
1439
- lastTurnHadToolCalls = Array.isArray(content) && content.some((c) => c?.type === "toolCall");
1440
- }
1441
- callStartAt = undefined;
1442
- firstTokenAt = undefined;
1443
- callOutputChars = 0;
1444
- lastStreamBudgetCheck = 0;
1347
+ if (rs.attach.attachState !== undefined)
1348
+ rs.counters.cadenceTurns = advanceCadenceClock(rs.attach.attachState, rs.counters.cadenceTurns, content, writeFamilyOf);
1349
+ rs.turn.lastTurnHadToolCalls = Array.isArray(content) && content.some((c) => c?.type === "toolCall");
1350
+ }
1351
+ rs.turn.callStartAt = undefined;
1352
+ rs.turn.firstTokenAt = undefined;
1353
+ rs.budget.callOutputChars = 0;
1354
+ rs.budget.lastStreamBudgetCheck = 0;
1445
1355
  };
1446
1356
  const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
1447
1357
  const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
@@ -1453,11 +1363,11 @@ export class Runner {
1453
1363
  startedToolCallIds.add(event.toolCallId);
1454
1364
  batchArgs?.set(event.toolCallId, event.args);
1455
1365
  stats.toolCalls += 1;
1456
- if (spec.finalVerification === true && !(finalVerifyInjections === 0 ? groundingSignalPreR9 : groundingSignalPostR9) && hasVerifiableStructureSignal(event.args)) {
1457
- if (finalVerifyInjections === 0)
1458
- groundingSignalPreR9 = true;
1366
+ if (spec.finalVerification === true && !(rs.counters.finalVerifyInjections === 0 ? rs.counters.groundingSignalPreR9 : rs.counters.groundingSignalPostR9) && hasVerifiableStructureSignal(event.args)) {
1367
+ if (rs.counters.finalVerifyInjections === 0)
1368
+ rs.counters.groundingSignalPreR9 = true;
1459
1369
  else
1460
- groundingSignalPostR9 = true;
1370
+ rs.counters.groundingSignalPostR9 = true;
1461
1371
  }
1462
1372
  const activityArg = primaryActivityArg(event.args);
1463
1373
  internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) });
@@ -1479,13 +1389,13 @@ export class Runner {
1479
1389
  turnToolSpan.startMin = turnToolSpan.startMin === undefined ? toolStarted : Math.min(turnToolSpan.startMin, toolStarted);
1480
1390
  turnToolSpan.endMax = turnToolSpan.endMax === undefined ? toolNow : Math.max(turnToolSpan.endMax, toolNow);
1481
1391
  }
1482
- if (!wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
1483
- wroteThisRun = true;
1392
+ if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
1393
+ rs.counters.wroteThisRun = true;
1484
1394
  }
1485
- if (attachState !== undefined && !event.isError) {
1395
+ if (rs.attach.attachState !== undefined && !event.isError) {
1486
1396
  const det = event.result?.details;
1487
1397
  if (det !== undefined)
1488
- reduceToolEnd(attachState, det);
1398
+ reduceToolEnd(rs.attach.attachState, det);
1489
1399
  }
1490
1400
  if (postToolBatchHook !== undefined) {
1491
1401
  if (prepared.blockedToolCalls.delete(event.toolCallId)) {
@@ -1503,10 +1413,10 @@ export class Runner {
1503
1413
  batchArgs?.delete(event.toolCallId);
1504
1414
  }
1505
1415
  }
1506
- emitTrace(tracer, () => ({
1416
+ emitTrace(rs.telemetry.tracer, () => ({
1507
1417
  kind: "tool.call",
1508
1418
  version: 1,
1509
- taskId,
1419
+ taskId: rs.telemetry.taskId,
1510
1420
  name: event.toolName,
1511
1421
  durationMs: toolStarted !== undefined ? toolNow - toolStarted : 0,
1512
1422
  ok: !event.isError,
@@ -1547,33 +1457,33 @@ export class Runner {
1547
1457
  }
1548
1458
  if (spec.outputSchema && event.toolName === OUTPUT_TOOL_NAME) {
1549
1459
  if (event.isError) {
1550
- outputErrorStreak += 1;
1551
- if (outputErrorStreak > outputRetryCap) {
1552
- outputInvalid = true;
1460
+ rs.degrade.outputErrorStreak += 1;
1461
+ if (rs.degrade.outputErrorStreak > rs.limits.outputRetryCap) {
1462
+ rs.degrade.outputInvalid = true;
1553
1463
  void prepared.harness.abort();
1554
1464
  prepared.abortController.abort();
1555
1465
  }
1556
1466
  }
1557
1467
  else {
1558
- outputErrorStreak = 0;
1468
+ rs.degrade.outputErrorStreak = 0;
1559
1469
  }
1560
1470
  }
1561
1471
  };
1562
1472
  const onTurnEnd = () => {
1563
1473
  stats.turns += 1;
1564
- const stopExtra = turnStopReason !== undefined ? { stopReason: turnStopReason } : {};
1565
- queue.push(turnUsage
1566
- ? { type: "turn_end", usage: turnUsage, ...(turnUsageMissing ? { usageMissing: true } : {}), ...stopExtra, ...ident() }
1474
+ const stopExtra = rs.turn.turnStopReason !== undefined ? { stopReason: rs.turn.turnStopReason } : {};
1475
+ queue.push(rs.turn.turnUsage
1476
+ ? { type: "turn_end", usage: rs.turn.turnUsage, ...(rs.turn.turnUsageMissing ? { usageMissing: true } : {}), ...stopExtra, ...ident() }
1567
1477
  : { type: "turn_end", usageMissing: true, ...stopExtra, ...ident() });
1568
- turnUsageMissing = false;
1569
- turnStopReason = undefined;
1478
+ rs.turn.turnUsageMissing = false;
1479
+ rs.turn.turnStopReason = undefined;
1570
1480
  if (parentToolCallId !== undefined) {
1571
1481
  const progress = {
1572
1482
  type: "task_progress",
1573
- taskId,
1483
+ taskId: rs.telemetry.taskId,
1574
1484
  ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
1575
1485
  ...(subagentName ? { name: subagentName } : {}),
1576
- usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - taskStart },
1486
+ usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
1577
1487
  status: "running",
1578
1488
  ...ident(),
1579
1489
  };
@@ -1584,37 +1494,37 @@ export class Runner {
1584
1494
  catch {
1585
1495
  }
1586
1496
  }
1587
- emitTrace(tracer, () => ({ kind: "turn.end", version: 1, taskId, turn: stats.turns, ts: Date.now() }));
1588
- turnUsage = undefined;
1589
- callStartAt = undefined;
1590
- firstTokenAt = undefined;
1591
- callOutputChars = 0;
1592
- lastStreamBudgetCheck = 0;
1497
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "turn.end", version: 1, taskId: rs.telemetry.taskId, turn: stats.turns, ts: Date.now() }));
1498
+ rs.turn.turnUsage = undefined;
1499
+ rs.turn.callStartAt = undefined;
1500
+ rs.turn.firstTokenAt = undefined;
1501
+ rs.budget.callOutputChars = 0;
1502
+ rs.budget.lastStreamBudgetCheck = 0;
1593
1503
  toolStartAt.clear();
1594
- if (effectiveMaxTurns !== undefined && effectiveMaxTurns > 0 && stats.turns >= effectiveMaxTurns) {
1595
- turnsExceeded = true;
1504
+ if (rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns) {
1505
+ rs.limits.turnsExceeded = true;
1596
1506
  if (prepared.suspendForResource === undefined) {
1597
1507
  void prepared.harness.abort();
1598
1508
  prepared.abortController.abort();
1599
1509
  }
1600
1510
  }
1601
1511
  else {
1602
- if (degraded === undefined &&
1512
+ if (rs.degrade.degraded === undefined &&
1603
1513
  spec.degrade &&
1604
- degradeToModel &&
1605
- maxCostMicroUsd !== undefined &&
1606
- stats.costMicroUsd >= spec.degrade.atCostFraction * maxCostMicroUsd) {
1607
- void prepared.harness.setModel(degradeToModel);
1608
- recordDegraded({ from: prepared.model.id, to: degradeToModel.id, reason: "budget", atTurn: stats.turns }, degradeToModel);
1514
+ rs.degrade.degradeToModel &&
1515
+ rs.budget.maxCostMicroUsd !== undefined &&
1516
+ stats.costMicroUsd >= spec.degrade.atCostFraction * rs.budget.maxCostMicroUsd) {
1517
+ void prepared.harness.setModel(rs.degrade.degradeToModel);
1518
+ rs.degrade.recordDegraded({ from: prepared.model.id, to: rs.degrade.degradeToModel.id, reason: "budget", atTurn: stats.turns }, rs.degrade.degradeToModel);
1609
1519
  if (prepared.callCapRef &&
1610
1520
  spec.limits?.maxOutputTokens === undefined &&
1611
- degradeToModel.maxTokens !== undefined &&
1612
- (prepared.callCapRef.staticCapTokens === undefined || degradeToModel.maxTokens < prepared.callCapRef.staticCapTokens)) {
1613
- prepared.callCapRef.staticCapTokens = degradeToModel.maxTokens;
1521
+ rs.degrade.degradeToModel.maxTokens !== undefined &&
1522
+ (prepared.callCapRef.staticCapTokens === undefined || rs.degrade.degradeToModel.maxTokens < prepared.callCapRef.staticCapTokens)) {
1523
+ prepared.callCapRef.staticCapTokens = rs.degrade.degradeToModel.maxTokens;
1614
1524
  }
1615
1525
  }
1616
- if (!budgetHit && overBudget()) {
1617
- budgetHit = "exceeded";
1526
+ if (!rs.limits.budgetHit && rs.budget.overBudget()) {
1527
+ rs.limits.budgetHit = "exceeded";
1618
1528
  if (prepared.suspendForResource === undefined) {
1619
1529
  void prepared.harness.abort();
1620
1530
  prepared.abortController.abort();
@@ -1634,11 +1544,10 @@ export class Runner {
1634
1544
  stats.cachedTokens += u.cacheRead || 0;
1635
1545
  stats.cacheWriteTokens += u.cacheWrite || 0;
1636
1546
  stats.outputTokens += u.output || 0;
1637
- stats.costUsd += u.cost?.total || 0;
1638
1547
  stats.costMicroUsd += costMicroUsd;
1639
1548
  stats.compactionMicroUsd = (stats.compactionMicroUsd ?? 0) + costMicroUsd;
1640
- emitTrace(tracer, () => ({
1641
- kind: "brain.call", version: 1, taskId, model: m.id, provider: m.provider,
1549
+ emitTrace(rs.telemetry.tracer, () => ({
1550
+ kind: "brain.call", version: 1, taskId: rs.telemetry.taskId, model: m.id, provider: m.provider,
1642
1551
  promptTokens: totalInputTokens, completionTokens: u.output || 0,
1643
1552
  cacheRead: u.cacheRead || 0, cacheWrite: u.cacheWrite || 0,
1644
1553
  latencyMs: 0, costMicroUsd, ...(typeof msg?.stopReason === "string" ? { stopReason: msg.stopReason } : {}), ts: Date.now(),
@@ -1658,11 +1567,11 @@ export class Runner {
1658
1567
  const compactionBreaker = { failures: 0 };
1659
1568
  const compactionThroughputRef = {};
1660
1569
  const windowSafetyOptions = (mainModel) => ({
1661
- ...(maxCostMicroUsd !== undefined
1570
+ ...(rs.budget.maxCostMicroUsd !== undefined
1662
1571
  ? {
1663
1572
  fallbackBudget: {
1664
1573
  spentMicroUsd: () => stats.costMicroUsd,
1665
- capMicroUsd: maxCostMicroUsd,
1574
+ capMicroUsd: rs.budget.maxCostMicroUsd,
1666
1575
  mainInputPer1M: (this.deps.pricing?.[mainModel.id] ?? modelCostToPricing(mainModel.cost)).inputPer1M,
1667
1576
  },
1668
1577
  }
@@ -1678,10 +1587,10 @@ export class Runner {
1678
1587
  : {}),
1679
1588
  onWindowSafety: (info) => {
1680
1589
  if (info.kind === "fallback") {
1681
- emitTrace(tracer, () => ({
1590
+ emitTrace(rs.telemetry.tracer, () => ({
1682
1591
  kind: "compaction.model_fallback",
1683
1592
  version: 1,
1684
- taskId,
1593
+ taskId: rs.telemetry.taskId,
1685
1594
  contentTokens: info.contentTokens,
1686
1595
  headroomTokens: info.headroomTokens ?? 0,
1687
1596
  truncationRatio: info.truncationRatio,
@@ -1690,10 +1599,10 @@ export class Runner {
1690
1599
  }));
1691
1600
  }
1692
1601
  else {
1693
- emitTrace(tracer, () => ({
1602
+ emitTrace(rs.telemetry.tracer, () => ({
1694
1603
  kind: "compaction.clamp_disclosure",
1695
1604
  version: 1,
1696
- taskId,
1605
+ taskId: rs.telemetry.taskId,
1697
1606
  truncationRatio: info.truncationRatio,
1698
1607
  reason: info.reason ?? "tolerance",
1699
1608
  ...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
@@ -1710,13 +1619,13 @@ export class Runner {
1710
1619
  if (prepared.abortController.signal.aborted || prepared.suspendRef.token !== undefined)
1711
1620
  return [];
1712
1621
  if (finalVerificationOn &&
1713
- (finalVerifyInjections === 0 || (finalVerifyInjections === 1 && groundingSignalPreR9 && !groundingSignalPostR9)) &&
1714
- wroteThisRun &&
1622
+ (rs.counters.finalVerifyInjections === 0 || (rs.counters.finalVerifyInjections === 1 && rs.counters.groundingSignalPreR9 && !rs.counters.groundingSignalPostR9)) &&
1623
+ rs.counters.wroteThisRun &&
1715
1624
  prepared.outputRef.set !== true &&
1716
1625
  !finalizeInjected &&
1717
- !(effectiveMaxTurns !== undefined && effectiveMaxTurns > 0 && stats.turns >= effectiveMaxTurns - 1)) {
1718
- finalVerifyInjections += 1;
1719
- if (finalVerifyInjections === 2) {
1626
+ !(rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns - 1)) {
1627
+ rs.counters.finalVerifyInjections += 1;
1628
+ if (rs.counters.finalVerifyInjections === 2) {
1720
1629
  return [
1721
1630
  {
1722
1631
  role: "user",
@@ -1860,7 +1769,7 @@ export class Runner {
1860
1769
  minTokens: 0,
1861
1770
  force: true,
1862
1771
  overheadTokens: prepared.promptOverheadTokens,
1863
- onInputTruncated: (info) => emitTrace(tracer, () => ({ kind: "compaction.input_truncated", version: 1, taskId, label: info.label, droppedChars: info.droppedChars, keptChars: info.keptChars, ts: Date.now() })),
1772
+ onInputTruncated: (info) => emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.input_truncated", version: 1, taskId: rs.telemetry.taskId, label: info.label, droppedChars: info.droppedChars, keptChars: info.keptChars, ts: Date.now() })),
1864
1773
  workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
1865
1774
  ? {
1866
1775
  readFile: prepared.readTaskFile,
@@ -1903,12 +1812,12 @@ export class Runner {
1903
1812
  if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
1904
1813
  const pd = comp.phaseDurations;
1905
1814
  const pdDur = comp.durationMs;
1906
- emitTrace(tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
1815
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
1907
1816
  }
1908
1817
  prepared.cacheBreakDetector?.notifyCompaction();
1909
- if (attachState !== undefined) {
1910
- attachState.postCompactPending = true;
1911
- rebaseCadenceWindows(attachState, cadenceTurns);
1818
+ if (rs.attach.attachState !== undefined) {
1819
+ rs.attach.attachState.postCompactPending = true;
1820
+ rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
1912
1821
  }
1913
1822
  }
1914
1823
  else {
@@ -1964,31 +1873,31 @@ export class Runner {
1964
1873
  }
1965
1874
  if (due) {
1966
1875
  void prepared.harness.steer(due.text, { engineMinted: true }).catch(() => { });
1967
- nudgesSent += 1;
1876
+ rs.counters.nudgesSent += 1;
1968
1877
  boundarySteered = true;
1969
1878
  queue.push({ type: "steering_injected", source: "deadline_nudge", preview: due.text.slice(0, 220), ...ident() });
1970
1879
  }
1971
1880
  }
1972
1881
  const walltimeHit = walltimeMonotonicDeadline !== undefined && performance.now() >= walltimeMonotonicDeadline;
1973
1882
  const preemptHit = spec.preemptSignal?.aborted === true;
1974
- if (preemptHit && !prepared.suspendForResource && !preemptIgnoredReported) {
1975
- preemptIgnoredReported = true;
1976
- emitTrace(tracer, () => ({ kind: "preempt.ignored", version: 1, taskId, ts: Date.now() }));
1883
+ if (preemptHit && !prepared.suspendForResource && !rs.counters.preemptIgnoredReported) {
1884
+ rs.counters.preemptIgnoredReported = true;
1885
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "preempt.ignored", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
1977
1886
  }
1978
1887
  if (prepared.suspendForResource &&
1979
- (turnsExceeded || budgetHit !== undefined || walltimeHit || preemptHit) &&
1888
+ (rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || walltimeHit || preemptHit) &&
1980
1889
  prepared.suspendRef.token === undefined) {
1981
1890
  const reason = preemptHit
1982
1891
  ? "preempt"
1983
- : budgetHit !== undefined
1892
+ : rs.limits.budgetHit !== undefined
1984
1893
  ? "budget"
1985
1894
  : walltimeHit
1986
1895
  ? "walltime"
1987
1896
  : "turns";
1988
- const sliceSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - taskStartMonotonic) };
1897
+ const sliceSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) };
1989
1898
  if (await prepared.suspendForResource(reason, sliceSpend)) {
1990
- turnsExceeded = false;
1991
- budgetHit = undefined;
1899
+ rs.limits.turnsExceeded = false;
1900
+ rs.limits.budgetHit = undefined;
1992
1901
  }
1993
1902
  else {
1994
1903
  void prepared.harness.abort();
@@ -2020,7 +1929,7 @@ export class Runner {
2020
1929
  await prepared.suspendForReview(reason);
2021
1930
  return undefined;
2022
1931
  }
2023
- emitTrace(tracer, () => ({ kind: "review.dropped", version: 1, taskId, ts: Date.now() }));
1932
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "review.dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2024
1933
  }
2025
1934
  if (prepared.lspDiagnostics && !prepared.lspDiagnostics.registry.isEmpty() && !prepared.abortController.signal.aborted) {
2026
1935
  const files = prepared.lspDiagnostics.registry.drain();
@@ -2033,18 +1942,18 @@ export class Runner {
2033
1942
  }
2034
1943
  let boundaryAttachmentBytes = 0;
2035
1944
  let attachmentsPayload;
2036
- if ((dateState !== undefined || instrState !== undefined || attachState !== undefined) &&
1945
+ if ((rs.attach.dateState !== undefined || rs.attach.instrState !== undefined || rs.attach.attachState !== undefined) &&
2037
1946
  !boundarySteered &&
2038
1947
  !finalizeInjected &&
2039
- finalVerifyInjections === 0 &&
1948
+ rs.counters.finalVerifyInjections === 0 &&
2040
1949
  !prepared.abortController.signal.aborted) {
2041
1950
  const due = [];
2042
- if (attachState !== undefined) {
2043
- const changedFilesOn = Boolean(attachmentsCfg?.changedFiles);
1951
+ if (rs.attach.attachState !== undefined) {
1952
+ const changedFilesOn = Boolean(rs.attach.attachmentsCfg?.changedFiles);
2044
1953
  let changed;
2045
1954
  if (changedFilesOn && prepared.detectExternalChanges !== undefined) {
2046
- const maxFiles = typeof attachmentsCfg?.changedFiles === "object"
2047
- ? (attachmentsCfg.changedFiles.maxFiles ?? CHANGED_FILES_MAX)
1955
+ const maxFiles = typeof rs.attach.attachmentsCfg?.changedFiles === "object"
1956
+ ? (rs.attach.attachmentsCfg.changedFiles.maxFiles ?? CHANGED_FILES_MAX)
2048
1957
  : CHANGED_FILES_MAX;
2049
1958
  const SCAN_CEILING_MS = 2_000;
2050
1959
  const scan = await Promise.race([
@@ -2055,53 +1964,53 @@ export class Runner {
2055
1964
  }),
2056
1965
  ]);
2057
1966
  for (const p of scan.evicted)
2058
- attachState.surfacedMtime.delete(p);
1967
+ rs.attach.attachState.surfacedMtime.delete(p);
2059
1968
  changed = scan.changed;
2060
1969
  }
2061
- const backgroundTasksOn = attachmentsCfg?.backgroundTasks === true;
2062
- const bgTasks = backgroundTasksOn && attachState.postCompactPending ? prepared.listBackgroundTasks() : undefined;
2063
- const toolsDeltaOn = attachmentsCfg?.toolsDelta === true;
1970
+ const backgroundTasksOn = rs.attach.attachmentsCfg?.backgroundTasks === true;
1971
+ const bgTasks = backgroundTasksOn && rs.attach.attachState.postCompactPending ? prepared.listBackgroundTasks() : undefined;
1972
+ const toolsDeltaOn = rs.attach.attachmentsCfg?.toolsDelta === true;
2064
1973
  const pendingTools = toolsDeltaOn && prepared.toolsDeltaRef !== undefined && prepared.toolsDeltaRef.pending.length > 0
2065
1974
  ? [...prepared.toolsDeltaRef.pending]
2066
1975
  : undefined;
2067
- const mcpInstructionsOn = attachmentsCfg?.mcpInstructions === true;
1976
+ const mcpInstructionsOn = rs.attach.attachmentsCfg?.mcpInstructions === true;
2068
1977
  const mcpDelta = prepared.mcp.instructionsDelta;
2069
1978
  const mcpAdds = mcpInstructionsOn && mcpDelta.pendingAdds.length > 0 ? [...mcpDelta.pendingAdds] : undefined;
2070
1979
  const mcpRemovals = mcpInstructionsOn && mcpDelta.pendingRemovals.length > 0 ? [...mcpDelta.pendingRemovals] : undefined;
2071
1980
  const mcpDropped = mcpInstructionsOn && prepared.mcp.droppedTools.length > 0 ? selectMcpDroppedBatch(prepared.mcp.droppedTools) : undefined;
2072
- const budgetUsdOn = attachmentsCfg?.budgetUsd === true && maxCostMicroUsd !== undefined && lastTurnHadToolCalls;
2073
- due.push(...collectDueAttachments(attachState, {
1981
+ const budgetUsdOn = rs.attach.attachmentsCfg?.budgetUsd === true && rs.budget.maxCostMicroUsd !== undefined && rs.turn.lastTurnHadToolCalls;
1982
+ due.push(...collectDueAttachments(rs.attach.attachState, {
2074
1983
  turn: stats.turns,
2075
- cadenceTurn: cadenceTurns,
1984
+ cadenceTurn: rs.counters.cadenceTurns,
2076
1985
  todoMounted: todoToolMounted,
2077
1986
  taskListMounted: taskToolsMounted,
2078
1987
  planActive: prepared.planModeRef.active,
2079
1988
  config: {
2080
- todoReminder: attachmentsCfg?.todoReminder === true,
2081
- ...(attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: attachmentsCfg.todoReminderMode } : {}),
1989
+ todoReminder: rs.attach.attachmentsCfg?.todoReminder === true,
1990
+ ...(rs.attach.attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: rs.attach.attachmentsCfg.todoReminderMode } : {}),
2082
1991
  changedFiles: changedFilesOn,
2083
- planModeReminder: attachmentsCfg?.planModeReminder === true,
1992
+ planModeReminder: rs.attach.attachmentsCfg?.planModeReminder === true,
2084
1993
  budgetUsd: budgetUsdOn,
2085
1994
  backgroundTasks: backgroundTasksOn,
2086
1995
  toolsDelta: toolsDeltaOn,
2087
- agentListing: agentListingOn,
2088
- skillsListing: skillsListingOn,
1996
+ agentListing: rs.attach.agentListingOn,
1997
+ skillsListing: rs.attach.skillsListingOn,
2089
1998
  mcpInstructions: mcpInstructionsOn,
2090
1999
  },
2091
2000
  ...(changed !== undefined ? { changedFiles: changed } : {}),
2092
- ...(budgetUsdOn && maxCostMicroUsd !== undefined
2093
- ? { budgetUsd: { used: stats.costMicroUsd / 1e6, total: maxCostMicroUsd / 1e6 } }
2001
+ ...(budgetUsdOn && rs.budget.maxCostMicroUsd !== undefined
2002
+ ? { budgetUsd: { used: stats.costMicroUsd / 1e6, total: rs.budget.maxCostMicroUsd / 1e6 } }
2094
2003
  : {}),
2095
2004
  ...(bgTasks !== undefined ? { backgroundTasks: bgTasks } : {}),
2096
2005
  ...(pendingTools !== undefined ? { newTools: pendingTools } : {}),
2097
- ...(agentListingOn && prepared.agentListing !== undefined
2006
+ ...(rs.attach.agentListingOn && prepared.agentListing !== undefined
2098
2007
  ? {
2099
2008
  agentListing: prepared.agentListing.entries,
2100
2009
  agentToolName: prepared.agentListing.toolName,
2101
2010
  ...(prepared.agentListing.models !== undefined ? { agentModels: prepared.agentListing.models } : {}),
2102
2011
  }
2103
2012
  : {}),
2104
- ...(skillsListingOn && prepared.skillsListing !== undefined ? { skillsListing: prepared.skillsListing.entries } : {}),
2013
+ ...(rs.attach.skillsListingOn && prepared.skillsListing !== undefined ? { skillsListing: prepared.skillsListing.entries } : {}),
2105
2014
  ...(mcpAdds !== undefined || mcpRemovals !== undefined
2106
2015
  ? { mcpInstructions: { added: mcpAdds ?? [], removed: mcpRemovals ?? [] } }
2107
2016
  : {}),
@@ -2121,41 +2030,41 @@ export class Runner {
2121
2030
  due.some((a) => a.source === "mcp_dropped_tools" && a.body === renderMcpDroppedTools(mcpDropped))) {
2122
2031
  prepared.mcp.droppedTools.splice(0, mcpDropped.length);
2123
2032
  }
2124
- if (agentListingOn && prepared.agentListing !== undefined && due.some((a) => a.source === "agent_listing")) {
2033
+ if (rs.attach.agentListingOn && prepared.agentListing !== undefined && due.some((a) => a.source === "agent_listing")) {
2125
2034
  const listing = prepared.agentListing;
2126
- const intactBody = renderAgentListingDelta(attachState, listing.entries, listing.toolName, listing.models);
2035
+ const intactBody = renderAgentListingDelta(rs.attach.attachState, listing.entries, listing.toolName, listing.models);
2127
2036
  if (intactBody !== undefined && due.some((a) => a.source === "agent_listing" && a.body === intactBody)) {
2128
- commitAgentListing(attachState, listing.entries, listing.models);
2037
+ commitAgentListing(rs.attach.attachState, listing.entries, listing.models);
2129
2038
  prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
2130
2039
  if (listing.models !== undefined)
2131
2040
  prepared.announcedListingsRef.models = [...listing.models];
2132
2041
  await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
2133
2042
  }
2134
2043
  }
2135
- if (skillsListingOn && prepared.skillsListing !== undefined && due.some((a) => a.source === "skills_listing")) {
2044
+ if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined && due.some((a) => a.source === "skills_listing")) {
2136
2045
  const listing = prepared.skillsListing;
2137
- const intactBody = renderSkillsListingDelta(attachState, listing.entries);
2046
+ const intactBody = renderSkillsListingDelta(rs.attach.attachState, listing.entries);
2138
2047
  if (intactBody !== undefined && due.some((a) => a.source === "skills_listing" && a.body === intactBody)) {
2139
- commitSkillsListing(attachState, listing.entries);
2048
+ commitSkillsListing(rs.attach.attachState, listing.entries);
2140
2049
  prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
2141
2050
  await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
2142
2051
  }
2143
2052
  }
2144
2053
  }
2145
- if (dateState !== undefined && lastTurnHadToolCalls) {
2054
+ if (rs.attach.dateState !== undefined && rs.turn.lastTurnHadToolCalls) {
2146
2055
  const today = prepared.dateChange.today();
2147
- const cand = collectDateChange(dateState, today);
2056
+ const cand = collectDateChange(rs.attach.dateState, today);
2148
2057
  if (cand !== undefined) {
2149
2058
  due.unshift(cand);
2150
- dateState.announcedDate = today;
2059
+ rs.attach.dateState.announcedDate = today;
2151
2060
  }
2152
2061
  }
2153
- if (instrState !== undefined && instrProbe !== undefined && lastTurnHadToolCalls) {
2062
+ if (rs.attach.instrState !== undefined && rs.attach.instrProbe !== undefined && rs.turn.lastTurnHadToolCalls) {
2154
2063
  let probed = null;
2155
2064
  try {
2156
2065
  const PROBE_CEILING_MS = 2_000;
2157
2066
  probed = await Promise.race([
2158
- instrProbe(prepared.instructionSources),
2067
+ rs.attach.instrProbe(prepared.instructionSources),
2159
2068
  new Promise((resolve) => {
2160
2069
  const t = setTimeout(() => resolve(null), PROBE_CEILING_MS);
2161
2070
  t.unref?.();
@@ -2165,10 +2074,10 @@ export class Runner {
2165
2074
  catch {
2166
2075
  probed = null;
2167
2076
  }
2168
- const cand = collectInstructionsChange(instrState, probed);
2077
+ const cand = collectInstructionsChange(rs.attach.instrState, probed);
2169
2078
  if (cand !== undefined) {
2170
2079
  due.splice(due.length > 0 && due[0].source === "date_change" ? 1 : 0, 0, cand.attachment);
2171
- commitInstructionsChange(instrState, cand.announced);
2080
+ commitInstructionsChange(rs.attach.instrState, cand.announced);
2172
2081
  }
2173
2082
  }
2174
2083
  if (due.length > 0) {
@@ -2186,7 +2095,7 @@ export class Runner {
2186
2095
  const batch = toolBatch;
2187
2096
  toolBatch = [];
2188
2097
  batchArgs?.clear();
2189
- if (!boundarySteered && !finalizeInjected && finalVerifyInjections === 0 && !prepared.abortController.signal.aborted) {
2098
+ if (!boundarySteered && !finalizeInjected && rs.counters.finalVerifyInjections === 0 && !prepared.abortController.signal.aborted) {
2190
2099
  try {
2191
2100
  const r = await postToolBatchHook(batch);
2192
2101
  if (r?.additionalContext) {
@@ -2220,17 +2129,17 @@ export class Runner {
2220
2129
  prepared.suspendRef.token !== undefined ||
2221
2130
  prepared.reviewRef.token !== undefined) {
2222
2131
  if (forceManual) {
2223
- emitTrace(tracer, () => ({ kind: "compaction.mooted", version: 1, taskId, reason: "task_ending", ts: Date.now() }));
2132
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "task_ending", ts: Date.now() }));
2224
2133
  drainManualCompact("mooted");
2225
2134
  }
2226
2135
  return undefined;
2227
2136
  }
2228
2137
  if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES) {
2229
2138
  if (forceManual) {
2230
- emitTrace(tracer, () => ({
2139
+ emitTrace(rs.telemetry.tracer, () => ({
2231
2140
  kind: "compaction.failed",
2232
2141
  version: 1,
2233
- taskId,
2142
+ taskId: rs.telemetry.taskId,
2234
2143
  trigger: "manual",
2235
2144
  reason: "compaction circuit breaker open (3 consecutive failures this task) — manual request drained without an attempt",
2236
2145
  ts: Date.now(),
@@ -2275,7 +2184,7 @@ export class Runner {
2275
2184
  const trimPressure = trimPressureArmed && !trimForceBackoff;
2276
2185
  let manualOutcome = "failed";
2277
2186
  if (finalizeInjected && forceManual) {
2278
- emitTrace(tracer, () => ({ kind: "compaction.manual_in_finalize", version: 1, taskId, ts: Date.now() }));
2187
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.manual_in_finalize", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2279
2188
  }
2280
2189
  const manualInstructions = claimedManual?.instructions;
2281
2190
  try {
@@ -2297,7 +2206,7 @@ export class Runner {
2297
2206
  ...(manualCancelSignal !== undefined ? { manualCancelSignal } : {}),
2298
2207
  minTokens: forceManual ? 0 : compactionFloor,
2299
2208
  force: forceManual,
2300
- onInputTruncated: (info) => emitTrace(tracer, () => ({ kind: "compaction.input_truncated", version: 1, taskId, label: info.label, droppedChars: info.droppedChars, keptChars: info.keptChars, ts: Date.now() })),
2209
+ onInputTruncated: (info) => emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.input_truncated", version: 1, taskId: rs.telemetry.taskId, label: info.label, droppedChars: info.droppedChars, keptChars: info.keptChars, ts: Date.now() })),
2301
2210
  forceUnderThreshold: trimPressure,
2302
2211
  overheadTokens: prepared.promptOverheadTokens,
2303
2212
  workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
@@ -2318,10 +2227,10 @@ export class Runner {
2318
2227
  ...this.compactionHookOptions(spec, prepared.sessionId, trimPressure ? "forced" : forceManual ? "manual" : "auto"),
2319
2228
  });
2320
2229
  if (comp.blocked) {
2321
- emitTrace(tracer, () => ({
2230
+ emitTrace(rs.telemetry.tracer, () => ({
2322
2231
  kind: "compaction.blocked",
2323
2232
  version: 1,
2324
- taskId,
2233
+ taskId: rs.telemetry.taskId,
2325
2234
  trigger: forceManual ? "manual" : "auto",
2326
2235
  ts: Date.now(),
2327
2236
  }));
@@ -2335,10 +2244,10 @@ export class Runner {
2335
2244
  }
2336
2245
  if (comp.suppressedByFloor) {
2337
2246
  const s = comp.suppressedByFloor;
2338
- emitTrace(tracer, () => ({
2247
+ emitTrace(rs.telemetry.tracer, () => ({
2339
2248
  kind: "compaction.suppressed",
2340
2249
  version: 1,
2341
- taskId,
2250
+ taskId: rs.telemetry.taskId,
2342
2251
  estTokens: s.estTokens,
2343
2252
  floor: s.floor,
2344
2253
  ts: Date.now(),
@@ -2347,10 +2256,10 @@ export class Runner {
2347
2256
  manualOutcome = comp.compacted ? "compacted" : comp.blocked ? "blocked" : comp.disabled ? "disabled" : "noop";
2348
2257
  if (forceManual && !comp.compacted && !comp.blocked) {
2349
2258
  if (comp.disabled) {
2350
- emitTrace(tracer, () => ({ kind: "compaction.disabled", version: 1, taskId, ts: Date.now() }));
2259
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.disabled", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2351
2260
  }
2352
2261
  else {
2353
- emitTrace(tracer, () => ({ kind: "compaction.noop", version: 1, taskId, ts: Date.now() }));
2262
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.noop", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2354
2263
  }
2355
2264
  }
2356
2265
  if (comp.compacted && !forceManual) {
@@ -2379,24 +2288,23 @@ export class Runner {
2379
2288
  if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
2380
2289
  const pd = comp.phaseDurations;
2381
2290
  const pdDur = comp.durationMs;
2382
- emitTrace(tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
2291
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
2383
2292
  }
2384
2293
  prepared.cacheBreakDetector?.notifyCompaction();
2385
- if (attachState !== undefined) {
2386
- attachState.postCompactPending = true;
2387
- rebaseCadenceWindows(attachState, cadenceTurns);
2294
+ if (rs.attach.attachState !== undefined) {
2295
+ rs.attach.attachState.postCompactPending = true;
2296
+ rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
2388
2297
  }
2389
2298
  if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
2390
2299
  const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
2391
2300
  compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
2392
2301
  }
2393
- await this.maybeDynamicRecall(prepared);
2394
2302
  }
2395
2303
  }
2396
2304
  catch (err) {
2397
2305
  const manualCancelAbort = !prepared.abortController.signal.aborted && isCompactionManualCancel(err);
2398
2306
  if (manualCancelAbort) {
2399
- emitTrace(tracer, () => ({ kind: "compaction.mooted", version: 1, taskId, reason: "cancelled", ts: Date.now() }));
2307
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "cancelled", ts: Date.now() }));
2400
2308
  manualOutcome = "mooted";
2401
2309
  }
2402
2310
  else if (!prepared.abortController.signal.aborted) {
@@ -2405,10 +2313,10 @@ export class Runner {
2405
2313
  compactionBreaker.failures += 1;
2406
2314
  this.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
2407
2315
  const msg = String(err instanceof Error ? err.message : err);
2408
- emitTrace(tracer, () => ({
2316
+ emitTrace(rs.telemetry.tracer, () => ({
2409
2317
  kind: "compaction.failed",
2410
2318
  version: 1,
2411
- taskId,
2319
+ taskId: rs.telemetry.taskId,
2412
2320
  trigger: trimPressure ? "forced" : forceManual ? "manual" : "auto",
2413
2321
  reason: walltimeAbort
2414
2322
  ? "walltime soft-deadline abort (engine-initiated write-out protection; not counted toward the breaker)"
@@ -2462,13 +2370,13 @@ export class Runner {
2462
2370
  const walltimeExhaustedResume = effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0;
2463
2371
  if (!walltimeExhaustedResume && resume.outcome.gate !== "wake") {
2464
2372
  await this.applyResumeDecision(prepared, resume, (e) => queue.push(e), emitCommitted, (toolName, details) => {
2465
- if (attachState === undefined)
2373
+ if (rs.attach.attachState === undefined)
2466
2374
  return;
2467
2375
  const family = writeFamilyOf(toolName);
2468
2376
  if (family !== undefined)
2469
- stampWriteAnchor(attachState, family, cadenceTurns);
2377
+ stampWriteAnchor(rs.attach.attachState, family, rs.counters.cadenceTurns);
2470
2378
  if (details !== undefined)
2471
- reduceToolEnd(attachState, details);
2379
+ reduceToolEnd(rs.attach.attachState, details);
2472
2380
  }, (toolCallId) => {
2473
2381
  startedToolCallIds.add(toolCallId);
2474
2382
  if (resume !== undefined && resume.cp.pendingAction.kind === "tool_approval" && toolCallId === resume.cp.pendingAction.toolCallId) {
@@ -2482,8 +2390,8 @@ export class Runner {
2482
2390
  void prepared.harness.abort();
2483
2391
  prepared.abortController.abort();
2484
2392
  }
2485
- else if (maxCostMicroUsd !== undefined && maxCostMicroUsd <= 0) {
2486
- budgetHit = "exceeded";
2393
+ else if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd <= 0) {
2394
+ rs.limits.budgetHit = "exceeded";
2487
2395
  }
2488
2396
  else {
2489
2397
  let continuation = resumeContinuation(resume);
@@ -2524,9 +2432,9 @@ export class Runner {
2524
2432
  }
2525
2433
  }
2526
2434
  const firstFrames = [];
2527
- if (attachState !== undefined) {
2528
- const attach = attachState;
2529
- if (agentListingOn && prepared.agentListing !== undefined) {
2435
+ if (rs.attach.attachState !== undefined) {
2436
+ const attach = rs.attach.attachState;
2437
+ if (rs.attach.agentListingOn && prepared.agentListing !== undefined) {
2530
2438
  const listing = prepared.agentListing;
2531
2439
  const body = renderAgentListingDelta(attach, listing.entries, listing.toolName, listing.models);
2532
2440
  if (body !== undefined) {
@@ -2542,7 +2450,7 @@ export class Runner {
2542
2450
  });
2543
2451
  }
2544
2452
  }
2545
- if (skillsListingOn && prepared.skillsListing !== undefined) {
2453
+ if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined) {
2546
2454
  const listing = prepared.skillsListing;
2547
2455
  const body = renderSkillsListingDelta(attach, listing.entries);
2548
2456
  if (body !== undefined) {
@@ -2560,7 +2468,7 @@ export class Runner {
2560
2468
  effectiveObjective = `${firstFrames.map((f) => `<system-reminder>\n${sanitizeUntrustedText(f.body)}\n</system-reminder>`).join("\n")}\n${effectiveObjective}`;
2561
2469
  }
2562
2470
  }
2563
- const precallMicroUsd = maxCostMicroUsd === undefined
2471
+ const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
2564
2472
  ? 0
2565
2473
  : computeCostMicroUsd({
2566
2474
  totalInputTokens: Math.ceil(effectiveObjective.length / 4),
@@ -2568,12 +2476,12 @@ export class Runner {
2568
2476
  cacheWriteTokens: 0,
2569
2477
  cacheWriteTokensLong: 0,
2570
2478
  outputTokens: prepared.model.maxTokens ?? 4096,
2571
- }, pricing);
2572
- const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? remainingMicroUsd : maxCostMicroUsd;
2479
+ }, rs.telemetry.pricing);
2480
+ const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? rs.budget.remainingMicroUsd : rs.budget.maxCostMicroUsd;
2573
2481
  if (promptBlocked) {
2574
2482
  }
2575
2483
  else if (precallCeilingMicroUsd !== undefined && precallMicroUsd > precallCeilingMicroUsd) {
2576
- budgetHit = "precall";
2484
+ rs.limits.budgetHit = "precall";
2577
2485
  }
2578
2486
  else if (effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0) {
2579
2487
  timeout.fired = true;
@@ -2637,7 +2545,7 @@ export class Runner {
2637
2545
  const reviewPaused = prepared.reviewRef.token !== undefined && prepared.reviewRef.gate !== undefined;
2638
2546
  const durablyPaused = suspended || reviewPaused;
2639
2547
  const interrupted = !durablyPaused &&
2640
- (threw !== undefined || timeout.fired || turnsExceeded || budgetHit !== undefined || abortedLive || final?.stopReason === "aborted");
2548
+ (threw !== undefined || timeout.fired || rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || abortedLive || final?.stopReason === "aborted");
2641
2549
  if (interrupted) {
2642
2550
  try {
2643
2551
  const report = await reconcileInterruptedSession(prepared.session, prepared.toolEffects, undefined, startedToolCallIds);
@@ -2662,8 +2570,8 @@ export class Runner {
2662
2570
  });
2663
2571
  }
2664
2572
  }
2665
- if (attachState?.postCompactPending === true && attachmentsCfg?.backgroundTasks === true) {
2666
- emitTrace(tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId, ts: Date.now() }));
2573
+ if (rs.attach.attachState?.postCompactPending === true && rs.attach.attachmentsCfg?.backgroundTasks === true) {
2574
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2667
2575
  }
2668
2576
  const comp = await this.finish(spec, prepared, {
2669
2577
  skipCompaction: durablyPaused || compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES,
@@ -2691,7 +2599,7 @@ export class Runner {
2691
2599
  if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
2692
2600
  const pd = comp.phaseDurations;
2693
2601
  const pdDur = comp.durationMs;
2694
- emitTrace(tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
2602
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
2695
2603
  }
2696
2604
  prepared.cacheBreakDetector?.notifyCompaction();
2697
2605
  }
@@ -2700,7 +2608,6 @@ export class Runner {
2700
2608
  tokens: prepared.nestedStats.tokens,
2701
2609
  turns: prepared.nestedStats.turns,
2702
2610
  tasks: prepared.nestedStats.tasks,
2703
- costUsd: prepared.nestedStats.costUsd || undefined,
2704
2611
  costMicroUsd: prepared.nestedStats.costMicroUsd || undefined,
2705
2612
  };
2706
2613
  }
@@ -2710,9 +2617,9 @@ export class Runner {
2710
2617
  this.deps.onError?.(new Error(`prompt-cache: usage/api mismatch — cachedTokens (${stats.cachedTokens}) > promptTokens (${stats.promptTokens}); the cache family is likely wrong. Does model.api match the brain serving it? Override with model.params.promptCacheFamily = "input-includes-cached" | "input-excludes-cached".`), { phase: "prompt-cache", sessionId: prepared.sessionId });
2711
2618
  }
2712
2619
  stats.cacheHitRate = Math.min(1, rawHit);
2713
- if (!cacheBreakReported && stats.turns >= 2 && stats.promptTokens >= 8000 && stats.cacheHitRate < 0.15) {
2714
- const cause = degraded
2715
- ? `a mid-task model switch (${degraded.from} → ${degraded.to}, degradation) reset the prefix cache — this is the likely cause`
2620
+ if (!rs.telemetry.cacheBreakReported && stats.turns >= 2 && stats.promptTokens >= 8000 && stats.cacheHitRate < 0.15) {
2621
+ const cause = rs.degrade.degraded
2622
+ ? `a mid-task model switch (${rs.degrade.degraded.from} → ${rs.degrade.degraded.to}, degradation) reset the prefix cache — this is the likely cause`
2716
2623
  : `the cacheable prefix looks unstable. Keep volatile content (memory/timestamps/ids) out of the prompt prefix and tool order stable, or it's a server-side/TTL miss`;
2717
2624
  this.deps.onError?.(new Error(`prompt-cache: low prefix-cache hit rate ${(stats.cacheHitRate * 100).toFixed(0)}% over ${stats.turns} turns (${stats.promptTokens} prompt tokens) — ${cause}. See design/09.`), { phase: "prompt-cache", sessionId: prepared.sessionId });
2718
2625
  }
@@ -2720,7 +2627,6 @@ export class Runner {
2720
2627
  if (prepared.resourceLedger &&
2721
2628
  (resume?.cp.gate.kind === "resource_limit" || prepared.suspendRef.gate?.kind === "resource_limit")) {
2722
2629
  stats.costMicroUsd += prepared.resourceLedger.spentMicroUsd;
2723
- stats.costUsd += prepared.resourceLedger.spentMicroUsd / 1e6;
2724
2630
  stats.tokens += prepared.resourceLedger.spentTokens;
2725
2631
  stats.turns += prepared.resourceLedger.spentTurns;
2726
2632
  }
@@ -2736,34 +2642,34 @@ export class Runner {
2736
2642
  const toolCuts = prepared.callCapRef?.toolCuts ?? 0;
2737
2643
  const pendingCutTools = prepared.callCapRef?.pendingCutTools ?? 0;
2738
2644
  const toolCutKills = prepared.cutKills?.kills ?? 0;
2739
- if (finalizeInjected || nudgesSent > 0 || capShrinks > 0 || callCutoffs > 0 || toolClamps > 0 || toolCuts > 0 || toolCutKills > 0 || pendingCutTools > 0 || finalVerifyInjections > 0 || attachmentsInjected > 0 || repetitionCuts > 0 || repetitionSpared > 0) {
2645
+ if (finalizeInjected || rs.counters.nudgesSent > 0 || capShrinks > 0 || rs.counters.callCutoffs > 0 || toolClamps > 0 || toolCuts > 0 || toolCutKills > 0 || pendingCutTools > 0 || rs.counters.finalVerifyInjections > 0 || attachmentsInjected > 0 || rs.counters.repetitionCuts > 0 || rs.counters.repetitionSpared > 0) {
2740
2646
  stats.mechanisms = {
2741
2647
  ...(finalizeInjected ? { finalizeInjected: true } : {}),
2742
- ...(nudgesSent > 0 ? { nudgesSent } : {}),
2648
+ ...(rs.counters.nudgesSent > 0 ? { nudgesSent: rs.counters.nudgesSent } : {}),
2743
2649
  ...(capShrinks > 0 ? { capShrinks } : {}),
2744
- ...(callCutoffs > 0 ? { callCutoffs } : {}),
2650
+ ...(rs.counters.callCutoffs > 0 ? { callCutoffs: rs.counters.callCutoffs } : {}),
2745
2651
  ...(toolClamps > 0 ? { toolClamps } : {}),
2746
2652
  ...(toolCuts > 0 ? { toolCuts } : {}),
2747
2653
  ...(toolCutKills > 0 ? { toolCutKills } : {}),
2748
2654
  ...(pendingCutTools > 0 ? { pendingCutTools } : {}),
2749
- ...(finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
2750
- ...(finalVerifyInjections > 0 ? { finalVerifyInjections } : {}),
2655
+ ...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
2656
+ ...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjections: rs.counters.finalVerifyInjections } : {}),
2751
2657
  ...(attachmentsInjected > 0 ? { attachmentsInjected } : {}),
2752
- ...(repetitionCuts > 0 ? { repetitionCuts } : {}),
2753
- ...(repetitionSpared > 0 ? { repetitionSpared } : {}),
2754
- ...(repetitionEvents.length > 0 ? { repetitionEvents } : {}),
2658
+ ...(rs.counters.repetitionCuts > 0 ? { repetitionCuts: rs.counters.repetitionCuts } : {}),
2659
+ ...(rs.counters.repetitionSpared > 0 ? { repetitionSpared: rs.counters.repetitionSpared } : {}),
2660
+ ...(rs.counters.repetitionEvents.length > 0 ? { repetitionEvents: rs.counters.repetitionEvents } : {}),
2755
2661
  };
2756
2662
  }
2757
2663
  const result = assembleResult(spec, prepared.sessionId, final, stats, {
2758
2664
  threw,
2759
2665
  model: prepared.model.id,
2760
2666
  abortedForTimeout: timeout.fired,
2761
- abortedForTurns: turnsExceeded,
2667
+ abortedForTurns: rs.limits.turnsExceeded,
2762
2668
  abortedLive,
2763
- budgetHit,
2669
+ budgetHit: rs.limits.budgetHit,
2764
2670
  blockedReason: prepared.blockedRef.reason,
2765
2671
  conflict: prepared.conflictRef.hit,
2766
- outputInvalid,
2672
+ outputInvalid: rs.degrade.outputInvalid,
2767
2673
  suspendLoop: prepared.suspendLoopRef.hit,
2768
2674
  suspendRef: prepared.suspendRef.token !== undefined && prepared.suspendRef.gate !== undefined
2769
2675
  ? { token: prepared.suspendRef.token, gate: prepared.suspendRef.gate }
@@ -2815,8 +2721,8 @@ export class Runner {
2815
2721
  final.errorMessage !== DEGENERATE_MESSAGE &&
2816
2722
  final.errorMessage !== WALLTIME_CUTOFF_MESSAGE &&
2817
2723
  threw === undefined &&
2818
- budgetHit === undefined &&
2819
- !outputInvalid &&
2724
+ rs.limits.budgetHit === undefined &&
2725
+ !rs.degrade.outputInvalid &&
2820
2726
  !prepared.suspendLoopRef.hit &&
2821
2727
  !abortedLive &&
2822
2728
  result.errorCode !== "conflict") {
@@ -2835,8 +2741,8 @@ export class Runner {
2835
2741
  }
2836
2742
  }
2837
2743
  }
2838
- if (degraded)
2839
- result.degraded = degraded;
2744
+ if (rs.degrade.degraded)
2745
+ result.degraded = rs.degrade.degraded;
2840
2746
  if (prepared.outputRef.set)
2841
2747
  result.structuredOutput = prepared.outputRef.value;
2842
2748
  const committedToken = prepared.suspendRef.token ?? prepared.reviewRef.token;
@@ -2867,16 +2773,16 @@ export class Runner {
2867
2773
  }
2868
2774
  if (prepared.memoryEngineSession)
2869
2775
  await prepared.memoryEngineSession.harvest();
2870
- emitTrace(tracer, () => ({
2776
+ emitTrace(rs.telemetry.tracer, () => ({
2871
2777
  kind: "task.end",
2872
2778
  version: 1,
2873
- taskId,
2779
+ taskId: rs.telemetry.taskId,
2874
2780
  status: result.status,
2875
2781
  errorCode: result.errorCode,
2876
2782
  turns: stats.turns,
2877
2783
  tokens: stats.tokens,
2878
2784
  costMicroUsd: stats.costMicroUsd,
2879
- durationMs: Date.now() - taskStart,
2785
+ durationMs: Date.now() - rs.telemetry.taskStart,
2880
2786
  ...(prepared.outputRef.set ? { hasStructuredOutput: true } : {}),
2881
2787
  ...(stats.mechanisms !== undefined
2882
2788
  ? { mechanisms: (({ repetitionEvents: _events, ...scalars }) => scalars)(stats.mechanisms) }
@@ -2891,10 +2797,10 @@ export class Runner {
2891
2797
  if (parentToolCallId !== undefined && (result.status === "completed" || result.status === "failed" || result.status === "timeout" || result.status === "blocked")) {
2892
2798
  const terminalTick = {
2893
2799
  type: "task_progress",
2894
- taskId,
2800
+ taskId: rs.telemetry.taskId,
2895
2801
  ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
2896
2802
  ...(subagentName ? { name: subagentName } : {}),
2897
- usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - taskStart },
2803
+ usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
2898
2804
  status: result.status === "completed" ? "completed" : "failed",
2899
2805
  ...ident(),
2900
2806
  };
@@ -2910,9 +2816,6 @@ export class Runner {
2910
2816
  if (spec.rewindFiles && result.status === "completed") {
2911
2817
  await this.snapshotTurn(prepared);
2912
2818
  }
2913
- if (prepared.consolidation && prepared.consolidation.notes.length > 0) {
2914
- onConsolidation(this.consolidateMemory(spec, prepared, result));
2915
- }
2916
2819
  if (spec.suggestNextPrompts && result.status === "completed") {
2917
2820
  onSuggestions(this.suggestNextPrompts(spec, prepared, result));
2918
2821
  }
@@ -2927,8 +2830,8 @@ export class Runner {
2927
2830
  await defaultTaskRegistry.settleKilledForOwner({ owner: spec.taskId ?? prepared.sessionId, scope: spec.principal ?? "default", sessionId: prepared.sessionId }, {
2928
2831
  source: abortedLive &&
2929
2832
  !timeout.fired &&
2930
- !turnsExceeded &&
2931
- budgetHit === undefined &&
2833
+ !rs.limits.turnsExceeded &&
2834
+ rs.limits.budgetHit === undefined &&
2932
2835
  prepared.suspendRef.token === undefined &&
2933
2836
  prepared.reviewRef.token === undefined
2934
2837
  ? "user"
@@ -2981,67 +2884,6 @@ export class Runner {
2981
2884
  await this.teardownOwnedEnv(prepared);
2982
2885
  }
2983
2886
  }
2984
- async consolidateMemory(spec, prepared, result) {
2985
- const consolidation = prepared.consolidation;
2986
- if (!consolidation || !this.deps.memoryStore) {
2987
- return;
2988
- }
2989
- const { scope, notes, settings } = consolidation;
2990
- const onWarn = (err) => {
2991
- try {
2992
- this.deps.onError?.(err, { phase: "memory", sessionId: prepared.sessionId });
2993
- }
2994
- catch {
2995
- }
2996
- };
2997
- const ac = new AbortController();
2998
- const timer = settings.timeoutSec > 0 ? setTimeout(() => ac.abort(), settings.timeoutSec * 1000) : undefined;
2999
- timer?.unref?.();
3000
- try {
3001
- let model = prepared.model;
3002
- let thinking = prepared.thinking;
3003
- try {
3004
- const resolved = resolveTaskModel({ modelRole: settings.role, roles: spec.roles }, this.deps);
3005
- model = resolved.model;
3006
- thinking = resolved.thinking;
3007
- }
3008
- catch {
3009
- }
3010
- const pricing = this.deps.pricing?.[model.id] ?? modelCostToPricing(model.cost);
3011
- const stats = await runMemoryConsolidation({
3012
- store: this.deps.memoryStore,
3013
- scope,
3014
- notes,
3015
- band: settings.band,
3016
- searchLimit: settings.searchLimit,
3017
- maxNotes: settings.maxNotes,
3018
- onWarn,
3019
- llm: {
3020
- brain: this.deps.brain,
3021
- model,
3022
- pricing,
3023
- thinking,
3024
- getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
3025
- signal: ac.signal,
3026
- },
3027
- });
3028
- await advanceCursorAfterInline(this.deps.memoryStore, scope, stats?.consolidatedIds ?? [], stats?.addedIds ?? [], stats?.failedIds ?? [], onWarn);
3029
- if (stats) {
3030
- result.stats.memory = { tokens: stats.tokens, costMicroUsd: stats.costMicroUsd, applied: stats.applied };
3031
- if (result.stats.costBreakdown) {
3032
- result.stats.costBreakdown.memoryConsolidationMicroUsd = stats.costMicroUsd;
3033
- }
3034
- }
3035
- }
3036
- catch (err) {
3037
- onWarn(err);
3038
- }
3039
- finally {
3040
- if (timer) {
3041
- clearTimeout(timer);
3042
- }
3043
- }
3044
- }
3045
2887
  async suggestNextPrompts(spec, prepared, result) {
3046
2888
  const cfg = typeof spec.suggestNextPrompts === "object" ? spec.suggestNextPrompts : {};
3047
2889
  const count = cfg.count && cfg.count > 0 ? Math.min(cfg.count, SUGGESTIONS_MAX_COUNT) : SUGGESTIONS_DEFAULT_COUNT;
@@ -3471,40 +3313,6 @@ export class Runner {
3471
3313
  consecutiveProviderReuse: prepared.compactionReuseRef.consecutive,
3472
3314
  };
3473
3315
  }
3474
- async maybeDynamicRecall(prepared) {
3475
- const dr = prepared.dynamicRecall;
3476
- if (!dr)
3477
- return;
3478
- try {
3479
- const cfg = this.deps.dynamicRecall;
3480
- const { messages } = await prepared.session.buildContext();
3481
- const query = buildRecallQuery(messages, cfg?.recentMessageWindow, cfg?.maxQueryChars);
3482
- if (query.length === 0)
3483
- return;
3484
- const outcome = await runDynamicRecall({
3485
- store: this.deps.memoryStore,
3486
- scopes: dr.scopes,
3487
- selector: dr.selector,
3488
- query,
3489
- recentTools: dr.recentTools,
3490
- maxSelected: this.deps.memoryRecall?.maxSelected,
3491
- maxLinked: this.deps.memoryRecall?.maxLinked,
3492
- timeoutMs: this.deps.memoryRecall?.timeoutMs,
3493
- signal: prepared.abortController.signal,
3494
- priorSurfacedIds: dr.surfacedIds,
3495
- priorSurfacedKeys: dr.surfacedKeys,
3496
- });
3497
- if (!outcome.block)
3498
- return;
3499
- for (const id of outcome.surfacedIds)
3500
- dr.surfacedIds.add(id);
3501
- for (const k of outcome.surfacedKeys)
3502
- dr.surfacedKeys.add(k);
3503
- await prepared.harness.steer(outcome.block, { provenance: "engine-note" });
3504
- }
3505
- catch {
3506
- }
3507
- }
3508
3316
  compactionHookOptions(spec, sessionId, trigger) {
3509
3317
  const hooks = spec.hooks ?? this.deps.hooks;
3510
3318
  const pre = hooks?.preCompact;