@sema-agent/core 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/agents/observer.d.ts +14 -0
  2. package/dist/agents/observer.js +66 -12
  3. package/dist/agents/send-message-tool.js +164 -88
  4. package/dist/agents/subagent.d.ts +11 -4
  5. package/dist/agents/subagent.js +166 -62
  6. package/dist/core/context-edit.js +16 -3
  7. package/dist/core/file-snapshot-store.js +10 -1
  8. package/dist/core/memory-engine/dual-root.js +2 -0
  9. package/dist/core/memory-engine/engine.d.ts +4 -0
  10. package/dist/core/memory-engine/engine.js +6 -1
  11. package/dist/core/runner/prepare-memory.d.ts +4 -0
  12. package/dist/core/runner/prepare-memory.js +4 -1
  13. package/dist/core/runner/prepare-task.d.ts +9 -2
  14. package/dist/core/runner/prepare-task.js +68 -13
  15. package/dist/core/runner/runtask.js +919 -865
  16. package/dist/core/runner/synthetic-tools.d.ts +1 -0
  17. package/dist/core/runner/synthetic-tools.js +18 -15
  18. package/dist/core/runner/turn-attachments.d.ts +27 -3
  19. package/dist/core/runner/turn-attachments.js +101 -12
  20. package/dist/core/task-registry-agent.d.ts +9 -1
  21. package/dist/core/task-registry-agent.js +23 -2
  22. package/dist/core/task-registry-monitor.js +79 -24
  23. package/dist/core/task-registry-shared.d.ts +13 -1
  24. package/dist/core/task-registry-shared.js +21 -0
  25. package/dist/core/task-registry.d.ts +2 -0
  26. package/dist/core/task-registry.js +24 -26
  27. package/dist/core/tool-result-budget.js +2 -2
  28. package/dist/core/tool-result-store.d.ts +2 -0
  29. package/dist/core/tool-result-store.js +27 -2
  30. package/dist/core/types.d.ts +4 -2
  31. package/dist/core/workflow-journal-store.d.ts +13 -0
  32. package/dist/engine/session/import-validate.js +29 -0
  33. package/dist/engine/session/memory-repo.js +5 -0
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/orchestration/workflow-size-guideline.d.ts +6 -1
  37. package/dist/orchestration/workflow-size-guideline.js +19 -9
  38. package/dist/orchestration/workflow.d.ts +1 -0
  39. package/dist/orchestration/workflow.js +44 -1
  40. package/dist/prompt-assembly/assemble.js +3 -7
  41. package/dist/prompt-assembly/event-registry.js +1 -1
  42. package/dist/prompt-assembly/packs/sema-default.js +8 -5
  43. package/dist/prompts/coordinator.d.ts +1 -1
  44. package/dist/prompts/coordinator.js +45 -0
  45. package/dist/prompts/default.d.ts +4 -5
  46. package/dist/prompts/default.js +16 -18
  47. package/dist/prompts/simple-sections.d.ts +3 -1
  48. package/dist/prompts/simple-sections.js +11 -1
  49. package/dist/stores/cc/task-list-store.js +3 -3
  50. package/dist/stores/file/memory-store.d.ts +3 -0
  51. package/dist/stores/file/memory-store.js +39 -12
  52. package/dist/stores/file/tool-result-store.js +16 -2
  53. package/dist/stores/file/workflow-journal-store.d.ts +23 -0
  54. package/dist/stores/file/workflow-journal-store.js +140 -3
  55. package/dist/tools/fs/bash-readonly-classifier.js +21 -2
  56. package/dist/tools/fs/fs-bash.d.ts +1 -0
  57. package/dist/tools/fs/fs-bash.js +10 -3
  58. package/dist/tools/fs/fs-read.js +12 -12
  59. package/dist/tools/fs/fs-search-tools.js +42 -7
  60. package/dist/tools/fs/fs-write.js +18 -6
  61. package/dist/tools/fs/index.d.ts +1 -0
  62. package/dist/tools/fs/index.js +2 -1
  63. package/dist/tools/fs/safety.d.ts +4 -0
  64. package/dist/tools/fs/safety.js +113 -10
  65. package/dist/tools/fs/search.d.ts +1 -0
  66. package/dist/tools/fs/search.js +23 -3
  67. package/dist/tools/monitor.js +1 -1
  68. package/dist/tools/task-list.d.ts +1 -0
  69. package/dist/tools/task-list.js +13 -2
  70. package/dist/tools/web.js +36 -6
  71. package/package.json +3 -2
@@ -26,6 +26,7 @@ import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
26
26
  import { assembleResult, errorCodeOf } from "./assemble-result.js";
27
27
  import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, advanceCadenceClock, agentListingDeltaHeader, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderMcpDroppedTools, renderMcpInstructionsDelta, renderOrphanedBackgroundTasks, selectMcpDroppedBatch, renderSkillsListingDelta, renderToolsDelta, stampWriteAnchor } from "./turn-attachments.js";
28
28
  import { prepareTask } from "./prepare-task.js";
29
+ import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
29
30
  import { hasVerifiableStructureSignal } from "./grounding-signal.js";
30
31
  import { hasDestroy, isIsolated } from "../remote-env.js";
31
32
  import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
@@ -48,9 +49,9 @@ function createRunState() {
48
49
  degrade: { degradeToModel: undefined, degraded: undefined, outputErrorStreak: 0, outputInvalid: false, recordDegraded: () => { } },
49
50
  limits: { turnsExceeded: false, budgetHit: undefined, outputRetryCap: 0, effectiveMaxTurns: undefined },
50
51
  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 },
52
+ turn: { callStartAt: undefined, firstTokenAt: undefined, turnUsage: undefined, turnUsageMissing: false, turnStopReason: undefined, lastTurnHadToolCalls: false, toolBatch: [] },
53
+ counters: { nudgesSent: 0, nudgeIdx: 0, finalizeInjected: false, walltimeSyncBackstopFired: false, compactionFloor: 0, trimForceBackoff: false, callCutoffs: 0, repetitionCuts: 0, repetitionSpared: 0, repetitionEvents: [], REPETITION_EVENTS_CAP: 0, preemptIgnoredReported: false, wroteThisRun: false, finalVerifyInjections: 0, groundingSignalPreR9: false, groundingSignalPostR9: false, cadenceTurns: 0 },
54
+ attach: { attachmentsCfg: undefined, agentListingOn: false, skillsListingOn: false, attachState: undefined, dateState: undefined, instrProbe: undefined, instrState: undefined, attachmentsInjected: 0 },
54
55
  };
55
56
  }
56
57
  const MAX_CONSECUTIVE_COMPACTION_FAILURES = 3;
@@ -150,6 +151,8 @@ function writeFamilyOfCanonical(name) {
150
151
  return "task";
151
152
  if (name === "TodoWrite")
152
153
  return "todo";
154
+ if (name === TOOL_SEARCH_NAME)
155
+ return "tool_search";
153
156
  return undefined;
154
157
  }
155
158
  function toolResultMsg(toolCallId, toolName, text, isError) {
@@ -217,6 +220,888 @@ function resumeContinuation(resume) {
217
220
  `conversation above. Do NOT restart the task or re-run any tool you already ran; continue from this ` +
218
221
  `exact point, building on the existing results, and finish the remaining work.`);
219
222
  }
223
+ function makeTurnBoundary(prepared, stats, rs, deps) {
224
+ const { spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted, turnToolSpan, walltimeDeadlineMs, walltimeMonotonicDeadline, nudgeSchedule, buildFinalizeText, timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact, runnerHooks } = deps;
225
+ const onTurnBoundary = async (event) => {
226
+ if (prepared.callCapRef && turnToolSpan.startMin !== undefined && turnToolSpan.endMax !== undefined) {
227
+ recordToolCycleSample(prepared.callCapRef.state, turnToolSpan.endMax - turnToolSpan.startMin);
228
+ }
229
+ turnToolSpan.startMin = undefined;
230
+ turnToolSpan.endMax = undefined;
231
+ let boundarySteered = false;
232
+ if (prepared.suspendForResource === undefined) {
233
+ const nowMs = Date.now();
234
+ if (!rs.counters.finalizeInjected &&
235
+ spec.limits?.gracefulFinalize !== false &&
236
+ walltimeDeadlineMs !== undefined &&
237
+ prepared.callCapRef !== undefined) {
238
+ const remainingMs = walltimeDeadlineMs - nowMs;
239
+ if (remainingMs > 0 && remainingMs < estimateCycleMs(prepared.callCapRef.state) + (effectiveCushionMs(prepared.callCapRef) ?? 0)) {
240
+ rs.counters.finalizeInjected = true;
241
+ prepared.callCapRef.finalizeMode = true;
242
+ void prepared.harness.setThinkingLevel("off").catch(() => { });
243
+ prepared.turnSnapshot?.refreshRequestPolicy({ thinkingLevel: "off" });
244
+ rs.counters.nudgeIdx = nudgeSchedule.length;
245
+ const finalizeText = buildFinalizeText();
246
+ void prepared.harness.steer(finalizeText, { engineMinted: true }).catch(() => { });
247
+ boundarySteered = true;
248
+ queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
249
+ }
250
+ }
251
+ let due;
252
+ while (rs.counters.nudgeIdx < nudgeSchedule.length && nowMs >= nudgeSchedule[rs.counters.nudgeIdx].atMs) {
253
+ due = nudgeSchedule[rs.counters.nudgeIdx++];
254
+ }
255
+ if (due) {
256
+ void prepared.harness.steer(due.text, { engineMinted: true }).catch(() => { });
257
+ rs.counters.nudgesSent += 1;
258
+ boundarySteered = true;
259
+ queue.push({ type: "steering_injected", source: "deadline_nudge", preview: due.text.slice(0, 220), ...ident() });
260
+ }
261
+ }
262
+ const walltimeHit = walltimeMonotonicDeadline !== undefined && performance.now() >= walltimeMonotonicDeadline;
263
+ const preemptHit = spec.preemptSignal?.aborted === true;
264
+ if (preemptHit && !prepared.suspendForResource && !rs.counters.preemptIgnoredReported) {
265
+ rs.counters.preemptIgnoredReported = true;
266
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "preempt.ignored", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
267
+ }
268
+ if (prepared.suspendForResource &&
269
+ (rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || walltimeHit || preemptHit) &&
270
+ prepared.suspendRef.token === undefined) {
271
+ const reason = preemptHit
272
+ ? "preempt"
273
+ : rs.limits.budgetHit !== undefined
274
+ ? "budget"
275
+ : walltimeHit
276
+ ? "walltime"
277
+ : "turns";
278
+ const sliceSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) };
279
+ if (await prepared.suspendForResource(reason, sliceSpend)) {
280
+ rs.limits.turnsExceeded = false;
281
+ rs.limits.budgetHit = undefined;
282
+ }
283
+ else {
284
+ void prepared.harness.abort();
285
+ prepared.abortController.abort();
286
+ }
287
+ return undefined;
288
+ }
289
+ if (walltimeMonotonicDeadline !== undefined &&
290
+ prepared.suspendForResource === undefined &&
291
+ performance.now() >= walltimeMonotonicDeadline) {
292
+ if (!timeout.fired) {
293
+ timeout.fired = true;
294
+ timeout.clear();
295
+ timeout.latenessMs = Math.max(timeout.latenessMs ?? 0, performance.now() - walltimeMonotonicDeadline);
296
+ rs.counters.walltimeSyncBackstopFired = true;
297
+ void prepared.harness.abort();
298
+ prepared.abortController.abort();
299
+ }
300
+ return undefined;
301
+ }
302
+ if (prepared.reviewRequestRef.pending !== undefined) {
303
+ const { reason } = prepared.reviewRequestRef.pending;
304
+ prepared.reviewRequestRef.pending = undefined;
305
+ const honorable = prepared.suspendForReview !== undefined &&
306
+ !prepared.abortController.signal.aborted &&
307
+ prepared.suspendRef.token === undefined &&
308
+ prepared.reviewRef.token === undefined;
309
+ if (honorable && prepared.suspendForReview) {
310
+ await prepared.suspendForReview(reason);
311
+ return undefined;
312
+ }
313
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "review.dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
314
+ }
315
+ if (prepared.lspDiagnostics && !prepared.lspDiagnostics.registry.isEmpty() && !prepared.abortController.signal.aborted) {
316
+ const files = prepared.lspDiagnostics.registry.drain();
317
+ if (files.length > 0) {
318
+ queue.push({ type: "diagnostics", files, isNew: true, ...ident() });
319
+ const block = formatDiagnosticsBlock(files);
320
+ void prepared.harness.steer(block, { provenance: "engine-note" }).catch(() => undefined);
321
+ boundarySteered = true;
322
+ }
323
+ }
324
+ let boundaryAttachmentBytes = 0;
325
+ let attachmentsPayload;
326
+ if ((rs.attach.dateState !== undefined || rs.attach.instrState !== undefined || rs.attach.attachState !== undefined) &&
327
+ !boundarySteered &&
328
+ !rs.counters.finalizeInjected &&
329
+ rs.counters.finalVerifyInjections === 0 &&
330
+ !prepared.abortController.signal.aborted) {
331
+ const due = [];
332
+ if (rs.attach.attachState !== undefined) {
333
+ const changedFilesOn = Boolean(rs.attach.attachmentsCfg?.changedFiles);
334
+ let changed;
335
+ if (changedFilesOn && prepared.detectExternalChanges !== undefined) {
336
+ const maxFiles = typeof rs.attach.attachmentsCfg?.changedFiles === "object"
337
+ ? (rs.attach.attachmentsCfg.changedFiles.maxFiles ?? CHANGED_FILES_MAX)
338
+ : CHANGED_FILES_MAX;
339
+ const SCAN_CEILING_MS = 2_000;
340
+ const scan = await Promise.race([
341
+ prepared.detectExternalChanges(maxFiles),
342
+ new Promise((resolve) => {
343
+ const t = setTimeout(() => resolve({ changed: [], evicted: [] }), SCAN_CEILING_MS);
344
+ t.unref?.();
345
+ }),
346
+ ]);
347
+ for (const p of scan.evicted)
348
+ rs.attach.attachState.surfacedMtime.delete(p);
349
+ changed = scan.changed;
350
+ }
351
+ const backgroundTasksOn = rs.attach.attachmentsCfg?.backgroundTasks === true;
352
+ const bgTasks = backgroundTasksOn && rs.attach.attachState.postCompactPending ? prepared.listBackgroundTasks() : undefined;
353
+ const toolsDeltaOn = rs.attach.attachmentsCfg?.toolsDelta === true;
354
+ const tdRef = toolsDeltaOn ? prepared.toolsDeltaRef : undefined;
355
+ const pendingTools = tdRef !== undefined && tdRef.pending.length > 0 ? [...tdRef.pending] : undefined;
356
+ const pendingRemovedTools = tdRef !== undefined && tdRef.pendingRemoved.length > 0 ? [...tdRef.pendingRemoved] : undefined;
357
+ const pendingReaddedTools = tdRef !== undefined && tdRef.pendingReadded.length > 0 ? [...tdRef.pendingReadded] : undefined;
358
+ const pendingFailedMcp = tdRef !== undefined && tdRef.pendingFailed.length > 0 ? tdRef.pendingFailed.map((f) => ({ ...f })) : undefined;
359
+ const mcpToolsDelta = pendingRemovedTools !== undefined || pendingReaddedTools !== undefined || pendingFailedMcp !== undefined
360
+ ? {
361
+ ...(pendingRemovedTools !== undefined ? { removed: pendingRemovedTools } : {}),
362
+ ...(pendingReaddedTools !== undefined ? { readded: pendingReaddedTools } : {}),
363
+ ...(pendingFailedMcp !== undefined ? { failedServers: pendingFailedMcp } : {}),
364
+ }
365
+ : undefined;
366
+ const mcpInstructionsOn = rs.attach.attachmentsCfg?.mcpInstructions === true;
367
+ const mcpDelta = prepared.mcp.instructionsDelta;
368
+ const mcpAdds = mcpInstructionsOn && mcpDelta.pendingAdds.length > 0 ? [...mcpDelta.pendingAdds] : undefined;
369
+ const mcpRemovals = mcpInstructionsOn && mcpDelta.pendingRemovals.length > 0 ? [...mcpDelta.pendingRemovals] : undefined;
370
+ const mcpDropped = mcpInstructionsOn && prepared.mcp.droppedTools.length > 0 ? selectMcpDroppedBatch(prepared.mcp.droppedTools) : undefined;
371
+ const budgetUsdOn = rs.attach.attachmentsCfg?.budgetUsd === true && rs.budget.maxCostMicroUsd !== undefined && rs.turn.lastTurnHadToolCalls;
372
+ const toolSearchReminderOn = rs.attach.attachmentsCfg?.toolSearchReminder === true && prepared.deferredToolNames !== undefined;
373
+ const undiscoveredTools = toolSearchReminderOn
374
+ ? [...prepared.deferredToolNames].filter((n) => !prepared.activeTools.has(n)).sort()
375
+ : undefined;
376
+ due.push(...collectDueAttachments(rs.attach.attachState, {
377
+ turn: stats.turns,
378
+ cadenceTurn: rs.counters.cadenceTurns,
379
+ todoMounted: todoToolMounted,
380
+ taskListMounted: taskToolsMounted,
381
+ planActive: prepared.planModeRef.active,
382
+ config: {
383
+ todoReminder: rs.attach.attachmentsCfg?.todoReminder === true,
384
+ ...(rs.attach.attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: rs.attach.attachmentsCfg.todoReminderMode } : {}),
385
+ toolSearchReminder: toolSearchReminderOn,
386
+ changedFiles: changedFilesOn,
387
+ planModeReminder: rs.attach.attachmentsCfg?.planModeReminder === true,
388
+ budgetUsd: budgetUsdOn,
389
+ backgroundTasks: backgroundTasksOn,
390
+ toolsDelta: toolsDeltaOn,
391
+ agentListing: rs.attach.agentListingOn,
392
+ skillsListing: rs.attach.skillsListingOn,
393
+ mcpInstructions: mcpInstructionsOn,
394
+ },
395
+ ...(undiscoveredTools !== undefined && undiscoveredTools.length > 0 ? { undiscoveredTools } : {}),
396
+ ...(changed !== undefined ? { changedFiles: changed } : {}),
397
+ ...(budgetUsdOn && rs.budget.maxCostMicroUsd !== undefined
398
+ ? { budgetUsd: { used: stats.costMicroUsd / 1e6, total: rs.budget.maxCostMicroUsd / 1e6 } }
399
+ : {}),
400
+ ...(bgTasks !== undefined ? { backgroundTasks: bgTasks } : {}),
401
+ ...(pendingTools !== undefined ? { newTools: pendingTools } : {}),
402
+ ...(mcpToolsDelta !== undefined ? { mcpToolsDelta } : {}),
403
+ ...(rs.attach.agentListingOn && prepared.agentListing !== undefined
404
+ ? {
405
+ agentListing: prepared.agentListing.entries,
406
+ agentToolName: prepared.agentListing.toolName,
407
+ ...(prepared.agentListing.models !== undefined ? { agentModels: prepared.agentListing.models } : {}),
408
+ }
409
+ : {}),
410
+ ...(rs.attach.skillsListingOn && prepared.skillsListing !== undefined ? { skillsListing: prepared.skillsListing.entries } : {}),
411
+ ...(mcpAdds !== undefined || mcpRemovals !== undefined
412
+ ? { mcpInstructions: { added: mcpAdds ?? [], removed: mcpRemovals ?? [] } }
413
+ : {}),
414
+ ...(mcpDropped !== undefined ? { mcpDroppedTools: mcpDropped } : {}),
415
+ }));
416
+ if (pendingTools !== undefined || mcpToolsDelta !== undefined) {
417
+ const exact = renderToolsDelta({ ...(pendingTools !== undefined ? { added: pendingTools } : {}), ...(mcpToolsDelta ?? {}) });
418
+ if (exact !== undefined && due.some((a) => a.source === "tools_delta" && a.body === exact)) {
419
+ const ref = prepared.toolsDeltaRef;
420
+ if (pendingTools !== undefined)
421
+ ref.pending.splice(0, pendingTools.length);
422
+ if (pendingRemovedTools !== undefined)
423
+ ref.pendingRemoved.splice(0, pendingRemovedTools.length);
424
+ if (pendingReaddedTools !== undefined)
425
+ ref.pendingReadded.splice(0, pendingReaddedTools.length);
426
+ if (pendingFailedMcp !== undefined)
427
+ ref.pendingFailed.splice(0, pendingFailedMcp.length);
428
+ }
429
+ }
430
+ if ((mcpAdds !== undefined || mcpRemovals !== undefined) &&
431
+ due.some((a) => a.source === "mcp_instructions" && a.body === renderMcpInstructionsDelta(mcpAdds ?? [], mcpRemovals ?? []))) {
432
+ if (mcpAdds !== undefined)
433
+ mcpDelta.pendingAdds.splice(0, mcpAdds.length);
434
+ if (mcpRemovals !== undefined)
435
+ mcpDelta.pendingRemovals.splice(0, mcpRemovals.length);
436
+ }
437
+ if (mcpDropped !== undefined &&
438
+ due.some((a) => a.source === "mcp_dropped_tools" && a.body === renderMcpDroppedTools(mcpDropped))) {
439
+ prepared.mcp.droppedTools.splice(0, mcpDropped.length);
440
+ }
441
+ if (rs.attach.agentListingOn && prepared.agentListing !== undefined && due.some((a) => a.source === "agent_listing")) {
442
+ const listing = prepared.agentListing;
443
+ const intactBody = renderAgentListingDelta(rs.attach.attachState, listing.entries, listing.toolName, listing.models);
444
+ if (intactBody !== undefined && due.some((a) => a.source === "agent_listing" && a.body === intactBody)) {
445
+ commitAgentListing(rs.attach.attachState, listing.entries, listing.models);
446
+ prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
447
+ if (listing.models !== undefined)
448
+ prepared.announcedListingsRef.models = [...listing.models];
449
+ await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
450
+ }
451
+ }
452
+ if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined && due.some((a) => a.source === "skills_listing")) {
453
+ const listing = prepared.skillsListing;
454
+ const intactBody = renderSkillsListingDelta(rs.attach.attachState, listing.entries);
455
+ if (intactBody !== undefined && due.some((a) => a.source === "skills_listing" && a.body === intactBody)) {
456
+ commitSkillsListing(rs.attach.attachState, listing.entries);
457
+ prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
458
+ await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
459
+ }
460
+ }
461
+ }
462
+ if (rs.attach.dateState !== undefined && rs.turn.lastTurnHadToolCalls) {
463
+ const today = prepared.dateChange.today();
464
+ const cand = collectDateChange(rs.attach.dateState, today);
465
+ if (cand !== undefined) {
466
+ due.unshift(cand);
467
+ rs.attach.dateState.announcedDate = today;
468
+ }
469
+ }
470
+ if (rs.attach.instrState !== undefined && rs.attach.instrProbe !== undefined && rs.turn.lastTurnHadToolCalls) {
471
+ let probed = null;
472
+ try {
473
+ const PROBE_CEILING_MS = 2_000;
474
+ probed = await Promise.race([
475
+ rs.attach.instrProbe(prepared.instructionSources),
476
+ new Promise((resolve) => {
477
+ const t = setTimeout(() => resolve(null), PROBE_CEILING_MS);
478
+ t.unref?.();
479
+ }),
480
+ ]);
481
+ }
482
+ catch {
483
+ probed = null;
484
+ }
485
+ const cand = collectInstructionsChange(rs.attach.instrState, probed);
486
+ if (cand !== undefined) {
487
+ due.splice(due.length > 0 && due[0].source === "date_change" ? 1 : 0, 0, cand.attachment);
488
+ commitInstructionsChange(rs.attach.instrState, cand.announced);
489
+ }
490
+ }
491
+ if (due.length > 0) {
492
+ attachmentsPayload = due.map((a) => `<system-reminder>\n${sanitizeUntrustedText(a.body)}\n</system-reminder>`).join("\n");
493
+ for (const a of due)
494
+ boundaryAttachmentBytes += Buffer.byteLength(a.body, "utf8");
495
+ rs.attach.attachmentsInjected += due.length;
496
+ for (const a of due) {
497
+ queue.push({ type: "steering_injected", source: a.source, preview: a.body.slice(0, 220), ...ident() });
498
+ }
499
+ }
500
+ }
501
+ let batchContextBlock;
502
+ if (postToolBatchHook !== undefined && rs.turn.toolBatch.length > 0) {
503
+ const batch = rs.turn.toolBatch;
504
+ rs.turn.toolBatch = [];
505
+ batchArgs?.clear();
506
+ if (!boundarySteered && !rs.counters.finalizeInjected && rs.counters.finalVerifyInjections === 0 && !prepared.abortController.signal.aborted) {
507
+ try {
508
+ const r = await postToolBatchHook(batch);
509
+ if (r?.additionalContext) {
510
+ const body = sanitizeUntrustedText(r.additionalContext);
511
+ const budget = ATTACHMENT_BYTE_CAP - boundaryAttachmentBytes;
512
+ if (budget >= BATCH_CONTEXT_MIN_KEEP_BYTES) {
513
+ const clipped = clipToByteBudget(body, budget) === body
514
+ ? body
515
+ : `${clipToByteBudget(body, budget - Buffer.byteLength(BATCH_TRUNCATION_MARKER, "utf8"))}${BATCH_TRUNCATION_MARKER}`;
516
+ batchContextBlock = `<system-reminder>\n${clipped}\n</system-reminder>`;
517
+ }
518
+ }
519
+ }
520
+ catch (err) {
521
+ try {
522
+ runnerHooks.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
523
+ }
524
+ catch {
525
+ }
526
+ }
527
+ }
528
+ }
529
+ if (attachmentsPayload !== undefined || batchContextBlock !== undefined) {
530
+ const payload = attachmentsPayload !== undefined && batchContextBlock !== undefined ? `${attachmentsPayload}\n${batchContextBlock}` : (attachmentsPayload ?? batchContextBlock);
531
+ void prepared.harness.steer(payload, { engineMinted: true }).catch(() => { });
532
+ }
533
+ const forceManual = manualCompactRef.requested;
534
+ if ((!withinTaskCompaction && !forceManual) ||
535
+ (rs.counters.finalizeInjected && !forceManual) ||
536
+ prepared.abortController.signal.aborted ||
537
+ prepared.suspendRef.token !== undefined ||
538
+ prepared.reviewRef.token !== undefined) {
539
+ if (forceManual) {
540
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "task_ending", ts: Date.now() }));
541
+ drainManualCompact("mooted");
542
+ }
543
+ return undefined;
544
+ }
545
+ if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES) {
546
+ if (forceManual) {
547
+ emitTrace(rs.telemetry.tracer, () => ({
548
+ kind: "compaction.failed",
549
+ version: 1,
550
+ taskId: rs.telemetry.taskId,
551
+ trigger: "manual",
552
+ reason: "compaction circuit breaker open (3 consecutive failures this task) — manual request drained without an attempt",
553
+ ts: Date.now(),
554
+ }));
555
+ drainManualCompact("failed");
556
+ }
557
+ return undefined;
558
+ }
559
+ const claimedManual = forceManual
560
+ ? (() => {
561
+ const waiters = manualCompactRef.waiters.splice(0);
562
+ let instructions;
563
+ for (let i = waiters.length - 1; i >= 0; i--) {
564
+ if (waiters[i].instructions !== undefined) {
565
+ instructions = waiters[i].instructions;
566
+ break;
567
+ }
568
+ }
569
+ return { instructions, waiters };
570
+ })()
571
+ : undefined;
572
+ if (forceManual) {
573
+ manualCompactRef.requested = false;
574
+ }
575
+ const resolveClaimedWaiters = (outcome) => {
576
+ if (claimedManual !== undefined)
577
+ for (const w of claimedManual.waiters.splice(0))
578
+ w.resolve(outcome);
579
+ };
580
+ const claimedCancelSignals = claimedManual?.waiters.flatMap((w) => (w.signal !== undefined ? [w.signal] : [])) ?? [];
581
+ const manualCancelSignal = claimedCancelSignals.length === 0
582
+ ? undefined
583
+ : claimedCancelSignals.length === 1
584
+ ? claimedCancelSignals[0]
585
+ : AbortSignal.any(claimedCancelSignals);
586
+ const compactionPassSignal = claimedCancelSignals.length > 0
587
+ ? AbortSignal.any([prepared.abortController.signal, ...claimedCancelSignals])
588
+ : prepared.abortController.signal;
589
+ const trimPressureArmed = prepared.trimPressureRef.droppedMessages;
590
+ if (trimPressureArmed)
591
+ prepared.trimPressureRef.droppedMessages = false;
592
+ const trimPressure = trimPressureArmed && !rs.counters.trimForceBackoff;
593
+ let manualOutcome = "failed";
594
+ if (rs.counters.finalizeInjected && forceManual) {
595
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.manual_in_finalize", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
596
+ }
597
+ const manualInstructions = claimedManual?.instructions;
598
+ try {
599
+ const comp = await maybeCompact({
600
+ session: prepared.session,
601
+ epochDeclaredSections: prepared.epochDeclaredSections,
602
+ ...(() => {
603
+ const ca = prepared.centerCompactionCandidate?.();
604
+ return ca !== undefined ? { centerAdoption: ca } : {};
605
+ })(),
606
+ model: event.model,
607
+ compactionModel: prepared.compModel,
608
+ brain: compactionBrain,
609
+ getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
610
+ thinking: prepared.thinking,
611
+ settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
612
+ customInstructions: manualInstructions ?? spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
613
+ signal: compactionPassSignal,
614
+ ...(manualCancelSignal !== undefined ? { manualCancelSignal } : {}),
615
+ minTokens: forceManual ? 0 : rs.counters.compactionFloor,
616
+ force: forceManual,
617
+ 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() })),
618
+ forceUnderThreshold: trimPressure,
619
+ overheadTokens: prepared.promptOverheadTokens,
620
+ workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
621
+ ? {
622
+ readFile: prepared.readTaskFile,
623
+ ...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
624
+ ...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
625
+ ...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
626
+ ...(prepared.instructionSources
627
+ ? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
628
+ : {}),
629
+ ...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
630
+ }
631
+ : undefined,
632
+ ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
633
+ ...runnerHooks.seamCCompactionOptions(prepared),
634
+ ...windowSafetyOptions(event.model),
635
+ ...runnerHooks.compactionHookOptions(spec, prepared.sessionId, trimPressure ? "forced" : forceManual ? "manual" : "auto"),
636
+ });
637
+ if (comp.blocked) {
638
+ emitTrace(rs.telemetry.tracer, () => ({
639
+ kind: "compaction.blocked",
640
+ version: 1,
641
+ taskId: rs.telemetry.taskId,
642
+ trigger: forceManual ? "manual" : "auto",
643
+ ts: Date.now(),
644
+ }));
645
+ }
646
+ else {
647
+ compactionBreaker.failures = 0;
648
+ }
649
+ rs.counters.trimForceBackoff = nextTrimForceBackoff(rs.counters.trimForceBackoff, comp, trimPressure);
650
+ if (comp.contextUsage) {
651
+ queue.push({ type: "context_usage", ...comp.contextUsage, ...ident() });
652
+ }
653
+ if (comp.suppressedByFloor) {
654
+ const s = comp.suppressedByFloor;
655
+ emitTrace(rs.telemetry.tracer, () => ({
656
+ kind: "compaction.suppressed",
657
+ version: 1,
658
+ taskId: rs.telemetry.taskId,
659
+ estTokens: s.estTokens,
660
+ floor: s.floor,
661
+ ts: Date.now(),
662
+ }));
663
+ }
664
+ manualOutcome = comp.compacted ? "compacted" : comp.blocked ? "blocked" : comp.disabled ? "disabled" : "noop";
665
+ if (forceManual && !comp.compacted && !comp.blocked) {
666
+ if (comp.disabled) {
667
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.disabled", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
668
+ }
669
+ else {
670
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.noop", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
671
+ }
672
+ }
673
+ if (comp.compacted && !forceManual) {
674
+ if (recordCompactionAndCheckRapidRefill(rapidRefill, stats.turns)) {
675
+ compactionBreaker.failures = MAX_CONSECUTIVE_COMPACTION_FAILURES;
676
+ runnerHooks.onError?.(new Error("compaction.rapid_refill: the context refilled within <3 turns of compaction 3 times in a row — compaction disabled for the rest of this task (thrash spiral; the transcript is dominated by incompressible content)"), { phase: "compaction", sessionId: prepared.sessionId });
677
+ }
678
+ }
679
+ runnerHooks.recordCompactionReuse(prepared, comp);
680
+ if (comp.compacted) {
681
+ queue.push({
682
+ type: "compacted",
683
+ trigger: forceManual ? "manual" : "auto",
684
+ tokensBefore: comp.tokensBefore ?? 0,
685
+ ...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
686
+ ...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
687
+ ...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
688
+ ...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
689
+ ...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
690
+ ...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
691
+ ...(comp.modelFallback ? { modelFallback: true } : {}),
692
+ ...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
693
+ ...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
694
+ ...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
695
+ });
696
+ if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
697
+ const pd = comp.phaseDurations;
698
+ const pdDur = comp.durationMs;
699
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
700
+ }
701
+ prepared.cacheBreakDetector?.notifyCompaction();
702
+ if (rs.attach.attachState !== undefined) {
703
+ rs.attach.attachState.postCompactPending = true;
704
+ rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
705
+ }
706
+ if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
707
+ const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
708
+ rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
709
+ }
710
+ }
711
+ }
712
+ catch (err) {
713
+ const manualCancelAbort = !prepared.abortController.signal.aborted && isCompactionManualCancel(err);
714
+ if (manualCancelAbort) {
715
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "cancelled", ts: Date.now() }));
716
+ manualOutcome = "mooted";
717
+ }
718
+ else if (!prepared.abortController.signal.aborted) {
719
+ const walltimeAbort = isCompactionWalltimeAbort(err);
720
+ if (!walltimeAbort)
721
+ compactionBreaker.failures += 1;
722
+ runnerHooks.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
723
+ const msg = String(err instanceof Error ? err.message : err);
724
+ emitTrace(rs.telemetry.tracer, () => ({
725
+ kind: "compaction.failed",
726
+ version: 1,
727
+ taskId: rs.telemetry.taskId,
728
+ trigger: trimPressure ? "forced" : forceManual ? "manual" : "auto",
729
+ reason: walltimeAbort
730
+ ? "walltime soft-deadline abort (engine-initiated write-out protection; not counted toward the breaker)"
731
+ : msg.length > 512
732
+ ? `${msg.slice(0, 512)}…`
733
+ : msg,
734
+ ts: Date.now(),
735
+ }));
736
+ manualOutcome = "failed";
737
+ }
738
+ else {
739
+ manualOutcome = "mooted";
740
+ }
741
+ }
742
+ if (forceManual)
743
+ resolveClaimedWaiters(manualOutcome);
744
+ return undefined;
745
+ };
746
+ return onTurnBoundary;
747
+ }
748
+ function makeHarnessHandlers(prepared, stats, rs, deps) {
749
+ const { spec, queue, internals, buildFinalizeText, ident, nudgeSchedule, parentToolCallId, subagentName, pushContent, emitCommitted, startedToolCallIds, toolStartAt, turnToolSpan, writeFamilyOf, toolLabels, postToolBatchHook, batchArgs } = deps;
750
+ let lastWorkspaceCwd = prepared.cwdRef?.current;
751
+ const onMessageUpdate = (event) => {
752
+ const ev = event.assistantMessageEvent;
753
+ if (rs.turn.callStartAt === undefined)
754
+ rs.turn.callStartAt = Date.now();
755
+ if (ev.type === "text_delta" && ev.delta) {
756
+ if (rs.turn.firstTokenAt === undefined)
757
+ rs.turn.firstTokenAt = Date.now();
758
+ pushContent({ type: "text_delta", delta: ev.delta, ...ident() });
759
+ }
760
+ else if (ev.type === "thinking_delta" && ev.delta) {
761
+ pushContent({ type: "reasoning_delta", delta: ev.delta, ...ident() });
762
+ }
763
+ if (rs.budget.streamCancel && !rs.limits.budgetHit && (ev.type === "text_delta" || ev.type === "thinking_delta") && ev.delta) {
764
+ rs.budget.callOutputChars += ev.delta.length;
765
+ if (rs.budget.callOutputChars - rs.budget.lastStreamBudgetCheck >= 256) {
766
+ rs.budget.lastStreamBudgetCheck = rs.budget.callOutputChars;
767
+ if (rs.budget.projectedOverBudget()) {
768
+ rs.limits.budgetHit = "exceeded";
769
+ void prepared.harness.abort();
770
+ prepared.abortController.abort();
771
+ }
772
+ }
773
+ }
774
+ };
775
+ const onMessageEnd = (event) => {
776
+ const m = event.message;
777
+ if (event.entryId !== undefined && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")) {
778
+ emitCommitted(event.entryId, m.role, m.role === "toolResult" ? m.toolCallId : undefined);
779
+ }
780
+ if (rs.degrade.degraded === undefined && m.role === "assistant") {
781
+ const deg = readDegradation(m);
782
+ if (deg)
783
+ rs.degrade.recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
784
+ }
785
+ if (m.role === "assistant" && "usage" in m && m.usage) {
786
+ if (m.usageMissing)
787
+ rs.turn.turnUsageMissing = true;
788
+ const u = m.usage;
789
+ stats.tokens += u.totalTokens || 0;
790
+ const { totalInputTokens: turnInput, costMicroUsd: turnCostMicroUsd } = usageCostMicroUsd(rs.telemetry.cacheFamily, u, rs.telemetry.pricing);
791
+ stats.promptTokens += turnInput;
792
+ stats.cachedTokens += u.cacheRead || 0;
793
+ stats.cacheWriteTokens += u.cacheWrite || 0;
794
+ stats.outputTokens += u.output || 0;
795
+ stats.costMicroUsd += turnCostMicroUsd;
796
+ rs.turn.turnUsage ??= { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
797
+ rs.turn.turnUsage.inputTokens += turnInput;
798
+ rs.turn.turnUsage.outputTokens += u.output || 0;
799
+ rs.turn.turnUsage.cacheReadTokens += u.cacheRead || 0;
800
+ rs.turn.turnUsage.cacheWriteTokens += u.cacheWrite || 0;
801
+ rs.turn.turnUsage.costMicroUsd += turnCostMicroUsd;
802
+ const callEndAt = Date.now();
803
+ const callIssuedAt = prepared.callIssuedAtRef?.current;
804
+ if (prepared.callIssuedAtRef)
805
+ prepared.callIssuedAtRef.current = undefined;
806
+ const capLast = prepared.callCapRef?.state.last;
807
+ if (prepared.callCapRef) {
808
+ recordCallSample(prepared.callCapRef.state, u.output || 0, rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0, {
809
+ contentValid: m.usageMissing === true && hasRealContent(m.content),
810
+ });
811
+ if (rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined) {
812
+ recordTtftSample(prepared.callCapRef.state, rs.turn.firstTokenAt - rs.turn.callStartAt);
813
+ }
814
+ prepared.callCapRef.state.last = undefined;
815
+ }
816
+ emitTrace(rs.telemetry.tracer, () => ({
817
+ kind: "brain.call",
818
+ version: 1,
819
+ taskId: rs.telemetry.taskId,
820
+ model: m.model,
821
+ provider: m.provider,
822
+ promptTokens: turnInput,
823
+ completionTokens: u.output || 0,
824
+ cacheRead: u.cacheRead || 0,
825
+ cacheWrite: u.cacheWrite || 0,
826
+ latencyMs: rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0,
827
+ firstTokenMs: rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined ? rs.turn.firstTokenAt - rs.turn.callStartAt : undefined,
828
+ ...(callIssuedAt !== undefined ? { callStartedAt: callIssuedAt } : {}),
829
+ costMicroUsd: turnCostMicroUsd,
830
+ ...(capLast !== undefined ? { callCap: capLast.cap, capThinkingSkipped: capLast.thinkingSkipped } : {}),
831
+ ...(m.role === "assistant" && typeof m.stopReason === "string"
832
+ ? { stopReason: m.stopReason }
833
+ : {}),
834
+ ts: callEndAt,
835
+ }));
836
+ if (prepared.callCapRef &&
837
+ prepared.callCapRef.reasoningObserved !== true &&
838
+ capLast !== undefined &&
839
+ m.role === "assistant" &&
840
+ m.stopReason === "length") {
841
+ const blocks = Array.isArray(m.content)
842
+ ? (m.content)
843
+ : [];
844
+ const productive = blocks.some((c) => c?.type === "toolCall") ||
845
+ blocks.some((c) => c?.type === "text" && typeof c.text === "string" && c.text.trim() !== "");
846
+ if (!productive)
847
+ prepared.callCapRef.reasoningObserved = true;
848
+ }
849
+ if (prepared.cacheBreakDetector && prepared.cacheFingerprint) {
850
+ const finding = prepared.cacheBreakDetector.observe({
851
+ turn: stats.turns + 1,
852
+ systemPrompt: prepared.cacheFingerprint.systemPrompt,
853
+ tools: prepared.cacheFingerprint.tools,
854
+ modelKey: `${m.provider}:${m.model}`,
855
+ cacheRead: u.cacheRead || 0,
856
+ });
857
+ if (finding) {
858
+ rs.telemetry.cacheBreakReported = true;
859
+ deps.runnerHooks.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 });
860
+ }
861
+ }
862
+ }
863
+ if (m.role === "assistant" && typeof m.stopReason === "string") {
864
+ rs.turn.turnStopReason = m.stopReason;
865
+ }
866
+ if (m.role === "assistant" && m.errorMessage === WALLTIME_CUTOFF_MESSAGE) {
867
+ rs.counters.callCutoffs += 1;
868
+ if (prepared.callCapRef)
869
+ prepared.callCapRef.finalizeMode = true;
870
+ if (!rs.counters.finalizeInjected) {
871
+ const finalizeText = buildFinalizeText();
872
+ void prepared.harness.steer(finalizeText, { engineMinted: true }).catch(() => { });
873
+ void prepared.harness.setThinkingLevel("off").catch(() => { });
874
+ prepared.turnSnapshot?.refreshRequestPolicy({ thinkingLevel: "off" });
875
+ rs.counters.finalizeInjected = true;
876
+ rs.counters.nudgeIdx = nudgeSchedule.length;
877
+ queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
878
+ }
879
+ }
880
+ if (m.role === "assistant") {
881
+ const rep = m.repetition;
882
+ if (rep !== undefined) {
883
+ const observed = [
884
+ ...(rep.cut !== undefined ? [{ action: "cut", e: rep.cut }] : []),
885
+ ...(rep.spared ?? []).map((e) => ({ action: "spared", e })),
886
+ ];
887
+ for (const { action, e } of observed) {
888
+ if (action === "cut")
889
+ rs.counters.repetitionCuts += 1;
890
+ else
891
+ rs.counters.repetitionSpared += 1;
892
+ if (rs.counters.repetitionEvents.length < rs.counters.REPETITION_EVENTS_CAP) {
893
+ rs.counters.repetitionEvents.push({ turn: stats.turns + 1, action, rule: e.rule, period: e.period, reps: e.reps, segment: e.segment });
894
+ }
895
+ emitTrace(rs.telemetry.tracer, () => ({
896
+ kind: "repetition.detected",
897
+ version: 1,
898
+ taskId: rs.telemetry.taskId,
899
+ turn: stats.turns + 1,
900
+ action,
901
+ rule: e.rule,
902
+ period: e.period,
903
+ reps: e.reps,
904
+ segment: e.segment,
905
+ ts: Date.now(),
906
+ }));
907
+ }
908
+ }
909
+ }
910
+ if (prepared.callCapRef &&
911
+ prepared.callCapRef.reasoningObserved !== true &&
912
+ m.role === "assistant" &&
913
+ Array.isArray(m.content) &&
914
+ m.content.some((c) => c?.type === "thinking")) {
915
+ prepared.callCapRef.reasoningObserved = true;
916
+ }
917
+ if (m.role === "assistant") {
918
+ const content = m.content;
919
+ if (rs.attach.attachState !== undefined)
920
+ rs.counters.cadenceTurns = advanceCadenceClock(rs.attach.attachState, rs.counters.cadenceTurns, content, writeFamilyOf);
921
+ rs.turn.lastTurnHadToolCalls = Array.isArray(content) && content.some((c) => c?.type === "toolCall");
922
+ }
923
+ rs.turn.callStartAt = undefined;
924
+ rs.turn.firstTokenAt = undefined;
925
+ rs.budget.callOutputChars = 0;
926
+ rs.budget.lastStreamBudgetCheck = 0;
927
+ };
928
+ const onToolStart = (event) => {
929
+ const startAt = Date.now();
930
+ toolStartAt.set(event.toolCallId, startAt);
931
+ startedToolCallIds.add(event.toolCallId);
932
+ batchArgs?.set(event.toolCallId, event.args);
933
+ stats.toolCalls += 1;
934
+ if (spec.finalVerification === true && !(rs.counters.finalVerifyInjections === 0 ? rs.counters.groundingSignalPreR9 : rs.counters.groundingSignalPostR9) && hasVerifiableStructureSignal(event.args)) {
935
+ if (rs.counters.finalVerifyInjections === 0)
936
+ rs.counters.groundingSignalPreR9 = true;
937
+ else
938
+ rs.counters.groundingSignalPostR9 = true;
939
+ }
940
+ const activityArg = primaryActivityArg(event.args);
941
+ internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) });
942
+ pushContent({
943
+ type: "tool_start",
944
+ toolCallId: event.toolCallId,
945
+ toolName: event.toolName,
946
+ ...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
947
+ args: event.args,
948
+ ...ident(),
949
+ });
950
+ };
951
+ const onToolEnd = (event) => {
952
+ const toolStarted = toolStartAt.get(event.toolCallId);
953
+ toolStartAt.delete(event.toolCallId);
954
+ const toolNow = Date.now();
955
+ if (prepared.callCapRef && toolStarted !== undefined) {
956
+ recordToolSample(prepared.callCapRef.state, toolNow - toolStarted);
957
+ turnToolSpan.startMin = turnToolSpan.startMin === undefined ? toolStarted : Math.min(turnToolSpan.startMin, toolStarted);
958
+ turnToolSpan.endMax = turnToolSpan.endMax === undefined ? toolNow : Math.max(turnToolSpan.endMax, toolNow);
959
+ }
960
+ if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
961
+ rs.counters.wroteThisRun = true;
962
+ }
963
+ if (rs.attach.attachState !== undefined && !event.isError) {
964
+ const det = event.result?.details;
965
+ if (det !== undefined)
966
+ reduceToolEnd(rs.attach.attachState, det);
967
+ }
968
+ if (postToolBatchHook !== undefined) {
969
+ if (prepared.blockedToolCalls.delete(event.toolCallId)) {
970
+ batchArgs?.delete(event.toolCallId);
971
+ }
972
+ else {
973
+ const digest = batchResponseDigest(event.result);
974
+ rs.turn.toolBatch.push({
975
+ toolName: event.toolName,
976
+ input: batchArgs?.get(event.toolCallId),
977
+ toolCallId: event.toolCallId,
978
+ ...(digest !== undefined ? { response: digest } : {}),
979
+ isError: event.isError,
980
+ });
981
+ batchArgs?.delete(event.toolCallId);
982
+ }
983
+ }
984
+ emitTrace(rs.telemetry.tracer, () => ({
985
+ kind: "tool.call",
986
+ version: 1,
987
+ taskId: rs.telemetry.taskId,
988
+ name: event.toolName,
989
+ durationMs: toolStarted !== undefined ? toolNow - toolStarted : 0,
990
+ ok: !event.isError,
991
+ ts: toolNow,
992
+ }));
993
+ internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow });
994
+ if (prepared.lspDiagnostics && !event.isError) {
995
+ const name = canonicalToolName(event.toolName);
996
+ if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
997
+ const d = event.result?.details;
998
+ const p = typeof d?.filePath === "string" ? d.filePath : typeof d?.notebookPath === "string" ? d.notebookPath : undefined;
999
+ if (p)
1000
+ prepared.lspDiagnostics.nudge(p);
1001
+ }
1002
+ }
1003
+ pushContent({
1004
+ type: "tool_end",
1005
+ toolCallId: event.toolCallId,
1006
+ toolName: event.toolName,
1007
+ ...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
1008
+ isError: event.isError,
1009
+ ...(() => {
1010
+ const o = toolOutputFrom(event.result);
1011
+ return o !== undefined
1012
+ ? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) }
1013
+ : {};
1014
+ })(),
1015
+ ...(() => {
1016
+ const st = structuredFrom(event.result);
1017
+ return st !== undefined ? { structured: st } : {};
1018
+ })(),
1019
+ ...ident(),
1020
+ });
1021
+ const cwdNow = prepared.cwdRef?.current;
1022
+ if (cwdNow !== undefined && cwdNow !== lastWorkspaceCwd) {
1023
+ lastWorkspaceCwd = cwdNow;
1024
+ queue.push({ type: "workspace_changed", cwd: cwdNow, ...ident() });
1025
+ }
1026
+ if (spec.outputSchema && event.toolName === OUTPUT_TOOL_NAME) {
1027
+ if (event.isError) {
1028
+ rs.degrade.outputErrorStreak += 1;
1029
+ if (rs.degrade.outputErrorStreak > rs.limits.outputRetryCap) {
1030
+ rs.degrade.outputInvalid = true;
1031
+ void prepared.harness.abort();
1032
+ prepared.abortController.abort();
1033
+ }
1034
+ }
1035
+ else {
1036
+ rs.degrade.outputErrorStreak = 0;
1037
+ }
1038
+ }
1039
+ };
1040
+ const onTurnEnd = () => {
1041
+ stats.turns += 1;
1042
+ const stopExtra = rs.turn.turnStopReason !== undefined ? { stopReason: rs.turn.turnStopReason } : {};
1043
+ queue.push(rs.turn.turnUsage
1044
+ ? { type: "turn_end", usage: rs.turn.turnUsage, ...(rs.turn.turnUsageMissing ? { usageMissing: true } : {}), ...stopExtra, ...ident() }
1045
+ : { type: "turn_end", usageMissing: true, ...stopExtra, ...ident() });
1046
+ rs.turn.turnUsageMissing = false;
1047
+ rs.turn.turnStopReason = undefined;
1048
+ if (parentToolCallId !== undefined) {
1049
+ const progress = {
1050
+ type: "task_progress",
1051
+ taskId: rs.telemetry.taskId,
1052
+ ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
1053
+ ...(subagentName ? { name: subagentName } : {}),
1054
+ usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
1055
+ status: "running",
1056
+ ...ident(),
1057
+ };
1058
+ queue.push(progress);
1059
+ try {
1060
+ internals?.onForwardEvent?.(progress);
1061
+ }
1062
+ catch {
1063
+ }
1064
+ }
1065
+ emitTrace(rs.telemetry.tracer, () => ({ kind: "turn.end", version: 1, taskId: rs.telemetry.taskId, turn: stats.turns, ts: Date.now() }));
1066
+ rs.turn.turnUsage = undefined;
1067
+ rs.turn.callStartAt = undefined;
1068
+ rs.turn.firstTokenAt = undefined;
1069
+ rs.budget.callOutputChars = 0;
1070
+ rs.budget.lastStreamBudgetCheck = 0;
1071
+ toolStartAt.clear();
1072
+ if (rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns) {
1073
+ rs.limits.turnsExceeded = true;
1074
+ if (prepared.suspendForResource === undefined) {
1075
+ void prepared.harness.abort();
1076
+ prepared.abortController.abort();
1077
+ }
1078
+ }
1079
+ else {
1080
+ if (rs.degrade.degraded === undefined &&
1081
+ spec.degrade &&
1082
+ rs.degrade.degradeToModel &&
1083
+ rs.budget.maxCostMicroUsd !== undefined &&
1084
+ stats.costMicroUsd >= spec.degrade.atCostFraction * rs.budget.maxCostMicroUsd) {
1085
+ void prepared.harness.setModel(rs.degrade.degradeToModel);
1086
+ rs.degrade.recordDegraded({ from: prepared.model.id, to: rs.degrade.degradeToModel.id, reason: "budget", atTurn: stats.turns }, rs.degrade.degradeToModel);
1087
+ if (prepared.callCapRef &&
1088
+ spec.limits?.maxOutputTokens === undefined &&
1089
+ rs.degrade.degradeToModel.maxTokens !== undefined &&
1090
+ (prepared.callCapRef.staticCapTokens === undefined || rs.degrade.degradeToModel.maxTokens < prepared.callCapRef.staticCapTokens)) {
1091
+ prepared.callCapRef.staticCapTokens = rs.degrade.degradeToModel.maxTokens;
1092
+ }
1093
+ }
1094
+ if (!rs.limits.budgetHit && rs.budget.overBudget()) {
1095
+ rs.limits.budgetHit = "exceeded";
1096
+ if (prepared.suspendForResource === undefined) {
1097
+ void prepared.harness.abort();
1098
+ prepared.abortController.abort();
1099
+ }
1100
+ }
1101
+ }
1102
+ };
1103
+ return { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd };
1104
+ }
220
1105
  export class Runner {
221
1106
  deps;
222
1107
  sessions;
@@ -1004,7 +1889,7 @@ export class Runner {
1004
1889
  catch {
1005
1890
  }
1006
1891
  }
1007
- let attachmentsInjected = 0;
1892
+ rs.attach.attachmentsInjected = 0;
1008
1893
  const todoToolMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TodoWrite");
1009
1894
  const taskToolsMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TaskCreate");
1010
1895
  const writeFamilyByName = new Map();
@@ -1091,8 +1976,8 @@ export class Runner {
1091
1976
  prepared.callCapRef.budgetMs = effectiveTimeoutMs;
1092
1977
  }
1093
1978
  }
1094
- let finalizeInjected = false;
1095
- let walltimeSyncBackstopFired = false;
1979
+ rs.counters.finalizeInjected = false;
1980
+ rs.counters.walltimeSyncBackstopFired = false;
1096
1981
  const nudgeSchedule = [];
1097
1982
  {
1098
1983
  const conf = spec.limits?.deadlineNudge;
@@ -1119,7 +2004,7 @@ export class Runner {
1119
2004
  }
1120
2005
  }
1121
2006
  }
1122
- let nudgeIdx = 0;
2007
+ rs.counters.nudgeIdx = 0;
1123
2008
  const buildFinalizeText = () => `<system-reminder>[deadline] the wall-clock budget is exhausted — this is the FINAL turn. ` +
1124
2009
  `Write your best current work to the deliverable path NOW, even if incomplete — persist to disk FIRST; everything else comes after. ` +
1125
2010
  `If a better version exists anywhere else (a draft, scratch or temp file), copy it to the deliverable path now. ` +
@@ -1148,7 +2033,6 @@ export class Runner {
1148
2033
  queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
1149
2034
  };
1150
2035
  const subagentName = parentToolCallId !== undefined && internals?.agentName !== undefined ? inlineUntrusted(internals.agentName.slice(0, 320), 80) : undefined;
1151
- let lastWorkspaceCwd = prepared.cwdRef?.current;
1152
2036
  const statusEmit = (s) => {
1153
2037
  queue.push({
1154
2038
  type: "status",
@@ -1176,362 +2060,16 @@ export class Runner {
1176
2060
  : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
1177
2061
  };
1178
2062
  const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, fn));
1179
- const onMessageUpdate = (event) => {
1180
- const ev = event.assistantMessageEvent;
1181
- if (rs.turn.callStartAt === undefined)
1182
- rs.turn.callStartAt = Date.now();
1183
- if (ev.type === "text_delta" && ev.delta) {
1184
- if (rs.turn.firstTokenAt === undefined)
1185
- rs.turn.firstTokenAt = Date.now();
1186
- pushContent({ type: "text_delta", delta: ev.delta, ...ident() });
1187
- }
1188
- else if (ev.type === "thinking_delta" && ev.delta) {
1189
- pushContent({ type: "reasoning_delta", delta: ev.delta, ...ident() });
1190
- }
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";
1197
- void prepared.harness.abort();
1198
- prepared.abortController.abort();
1199
- }
1200
- }
1201
- }
1202
- };
1203
- const onMessageEnd = (event) => {
1204
- const m = event.message;
1205
- if (event.entryId !== undefined && (m.role === "user" || m.role === "assistant" || m.role === "toolResult")) {
1206
- emitCommitted(event.entryId, m.role, m.role === "toolResult" ? m.toolCallId : undefined);
1207
- }
1208
- if (rs.degrade.degraded === undefined && m.role === "assistant") {
1209
- const deg = readDegradation(m);
1210
- if (deg)
1211
- rs.degrade.recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
1212
- }
1213
- if (m.role === "assistant" && "usage" in m && m.usage) {
1214
- if (m.usageMissing)
1215
- rs.turn.turnUsageMissing = true;
1216
- const u = m.usage;
1217
- stats.tokens += u.totalTokens || 0;
1218
- const { totalInputTokens: turnInput, costMicroUsd: turnCostMicroUsd } = usageCostMicroUsd(rs.telemetry.cacheFamily, u, rs.telemetry.pricing);
1219
- stats.promptTokens += turnInput;
1220
- stats.cachedTokens += u.cacheRead || 0;
1221
- stats.cacheWriteTokens += u.cacheWrite || 0;
1222
- stats.outputTokens += u.output || 0;
1223
- stats.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;
1230
- const callEndAt = Date.now();
1231
- const callIssuedAt = prepared.callIssuedAtRef?.current;
1232
- if (prepared.callIssuedAtRef)
1233
- prepared.callIssuedAtRef.current = undefined;
1234
- const capLast = prepared.callCapRef?.state.last;
1235
- if (prepared.callCapRef) {
1236
- recordCallSample(prepared.callCapRef.state, u.output || 0, rs.turn.callStartAt !== undefined ? callEndAt - rs.turn.callStartAt : 0, {
1237
- contentValid: m.usageMissing === true && hasRealContent(m.content),
1238
- });
1239
- if (rs.turn.callStartAt !== undefined && rs.turn.firstTokenAt !== undefined) {
1240
- recordTtftSample(prepared.callCapRef.state, rs.turn.firstTokenAt - rs.turn.callStartAt);
1241
- }
1242
- prepared.callCapRef.state.last = undefined;
1243
- }
1244
- emitTrace(rs.telemetry.tracer, () => ({
1245
- kind: "brain.call",
1246
- version: 1,
1247
- taskId: rs.telemetry.taskId,
1248
- model: m.model,
1249
- provider: m.provider,
1250
- promptTokens: turnInput,
1251
- completionTokens: u.output || 0,
1252
- cacheRead: u.cacheRead || 0,
1253
- cacheWrite: u.cacheWrite || 0,
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,
1256
- ...(callIssuedAt !== undefined ? { callStartedAt: callIssuedAt } : {}),
1257
- costMicroUsd: turnCostMicroUsd,
1258
- ...(capLast !== undefined ? { callCap: capLast.cap, capThinkingSkipped: capLast.thinkingSkipped } : {}),
1259
- ...(m.role === "assistant" && typeof m.stopReason === "string"
1260
- ? { stopReason: m.stopReason }
1261
- : {}),
1262
- ts: callEndAt,
1263
- }));
1264
- if (prepared.callCapRef &&
1265
- prepared.callCapRef.reasoningObserved !== true &&
1266
- capLast !== undefined &&
1267
- m.role === "assistant" &&
1268
- m.stopReason === "length") {
1269
- const blocks = Array.isArray(m.content)
1270
- ? (m.content)
1271
- : [];
1272
- const productive = blocks.some((c) => c?.type === "toolCall") ||
1273
- blocks.some((c) => c?.type === "text" && typeof c.text === "string" && c.text.trim() !== "");
1274
- if (!productive)
1275
- prepared.callCapRef.reasoningObserved = true;
1276
- }
1277
- if (prepared.cacheBreakDetector && prepared.cacheFingerprint) {
1278
- const finding = prepared.cacheBreakDetector.observe({
1279
- turn: stats.turns + 1,
1280
- systemPrompt: prepared.cacheFingerprint.systemPrompt,
1281
- tools: prepared.cacheFingerprint.tools,
1282
- modelKey: `${m.provider}:${m.model}`,
1283
- cacheRead: u.cacheRead || 0,
1284
- });
1285
- if (finding) {
1286
- rs.telemetry.cacheBreakReported = true;
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 });
1288
- }
1289
- }
1290
- }
1291
- if (m.role === "assistant" && typeof m.stopReason === "string") {
1292
- rs.turn.turnStopReason = m.stopReason;
1293
- }
1294
- if (m.role === "assistant" && m.errorMessage === WALLTIME_CUTOFF_MESSAGE) {
1295
- rs.counters.callCutoffs += 1;
1296
- if (prepared.callCapRef)
1297
- prepared.callCapRef.finalizeMode = true;
1298
- if (!finalizeInjected) {
1299
- const finalizeText = buildFinalizeText();
1300
- void prepared.harness.steer(finalizeText, { engineMinted: true }).catch(() => { });
1301
- void prepared.harness.setThinkingLevel("off").catch(() => { });
1302
- prepared.turnSnapshot?.refreshRequestPolicy({ thinkingLevel: "off" });
1303
- finalizeInjected = true;
1304
- nudgeIdx = nudgeSchedule.length;
1305
- queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
1306
- }
1307
- }
1308
- if (m.role === "assistant") {
1309
- const rep = m.repetition;
1310
- if (rep !== undefined) {
1311
- const observed = [
1312
- ...(rep.cut !== undefined ? [{ action: "cut", e: rep.cut }] : []),
1313
- ...(rep.spared ?? []).map((e) => ({ action: "spared", e })),
1314
- ];
1315
- for (const { action, e } of observed) {
1316
- if (action === "cut")
1317
- rs.counters.repetitionCuts += 1;
1318
- else
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 });
1322
- }
1323
- emitTrace(rs.telemetry.tracer, () => ({
1324
- kind: "repetition.detected",
1325
- version: 1,
1326
- taskId: rs.telemetry.taskId,
1327
- turn: stats.turns + 1,
1328
- action,
1329
- rule: e.rule,
1330
- period: e.period,
1331
- reps: e.reps,
1332
- segment: e.segment,
1333
- ts: Date.now(),
1334
- }));
1335
- }
1336
- }
1337
- }
1338
- if (prepared.callCapRef &&
1339
- prepared.callCapRef.reasoningObserved !== true &&
1340
- m.role === "assistant" &&
1341
- Array.isArray(m.content) &&
1342
- m.content.some((c) => c?.type === "thinking")) {
1343
- prepared.callCapRef.reasoningObserved = true;
1344
- }
1345
- if (m.role === "assistant") {
1346
- const content = m.content;
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;
1355
- };
1356
2063
  const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
1357
2064
  const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
1358
2065
  const batchArgs = postToolBatchHook ? new Map() : undefined;
1359
- let toolBatch = [];
1360
- const onToolStart = (event) => {
1361
- const startAt = Date.now();
1362
- toolStartAt.set(event.toolCallId, startAt);
1363
- startedToolCallIds.add(event.toolCallId);
1364
- batchArgs?.set(event.toolCallId, event.args);
1365
- stats.toolCalls += 1;
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;
1369
- else
1370
- rs.counters.groundingSignalPostR9 = true;
1371
- }
1372
- const activityArg = primaryActivityArg(event.args);
1373
- internals?.onActivity?.({ phase: "start", toolCallId: event.toolCallId, toolName: event.toolName, at: startAt, ...(activityArg !== undefined ? { arg: activityArg } : {}) });
1374
- pushContent({
1375
- type: "tool_start",
1376
- toolCallId: event.toolCallId,
1377
- toolName: event.toolName,
1378
- ...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
1379
- args: event.args,
1380
- ...ident(),
1381
- });
1382
- };
1383
- const onToolEnd = (event) => {
1384
- const toolStarted = toolStartAt.get(event.toolCallId);
1385
- toolStartAt.delete(event.toolCallId);
1386
- const toolNow = Date.now();
1387
- if (prepared.callCapRef && toolStarted !== undefined) {
1388
- recordToolSample(prepared.callCapRef.state, toolNow - toolStarted);
1389
- turnToolSpan.startMin = turnToolSpan.startMin === undefined ? toolStarted : Math.min(turnToolSpan.startMin, toolStarted);
1390
- turnToolSpan.endMax = turnToolSpan.endMax === undefined ? toolNow : Math.max(turnToolSpan.endMax, toolNow);
1391
- }
1392
- if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
1393
- rs.counters.wroteThisRun = true;
1394
- }
1395
- if (rs.attach.attachState !== undefined && !event.isError) {
1396
- const det = event.result?.details;
1397
- if (det !== undefined)
1398
- reduceToolEnd(rs.attach.attachState, det);
1399
- }
1400
- if (postToolBatchHook !== undefined) {
1401
- if (prepared.blockedToolCalls.delete(event.toolCallId)) {
1402
- batchArgs?.delete(event.toolCallId);
1403
- }
1404
- else {
1405
- const digest = batchResponseDigest(event.result);
1406
- toolBatch.push({
1407
- toolName: event.toolName,
1408
- input: batchArgs?.get(event.toolCallId),
1409
- toolCallId: event.toolCallId,
1410
- ...(digest !== undefined ? { response: digest } : {}),
1411
- isError: event.isError,
1412
- });
1413
- batchArgs?.delete(event.toolCallId);
1414
- }
1415
- }
1416
- emitTrace(rs.telemetry.tracer, () => ({
1417
- kind: "tool.call",
1418
- version: 1,
1419
- taskId: rs.telemetry.taskId,
1420
- name: event.toolName,
1421
- durationMs: toolStarted !== undefined ? toolNow - toolStarted : 0,
1422
- ok: !event.isError,
1423
- ts: toolNow,
1424
- }));
1425
- internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow });
1426
- if (prepared.lspDiagnostics && !event.isError) {
1427
- const name = canonicalToolName(event.toolName);
1428
- if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
1429
- const d = event.result?.details;
1430
- const p = typeof d?.filePath === "string" ? d.filePath : typeof d?.notebookPath === "string" ? d.notebookPath : undefined;
1431
- if (p)
1432
- prepared.lspDiagnostics.nudge(p);
1433
- }
1434
- }
1435
- pushContent({
1436
- type: "tool_end",
1437
- toolCallId: event.toolCallId,
1438
- toolName: event.toolName,
1439
- ...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
1440
- isError: event.isError,
1441
- ...(() => {
1442
- const o = toolOutputFrom(event.result);
1443
- return o !== undefined
1444
- ? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) }
1445
- : {};
1446
- })(),
1447
- ...(() => {
1448
- const st = structuredFrom(event.result);
1449
- return st !== undefined ? { structured: st } : {};
1450
- })(),
1451
- ...ident(),
1452
- });
1453
- const cwdNow = prepared.cwdRef?.current;
1454
- if (cwdNow !== undefined && cwdNow !== lastWorkspaceCwd) {
1455
- lastWorkspaceCwd = cwdNow;
1456
- queue.push({ type: "workspace_changed", cwd: cwdNow, ...ident() });
1457
- }
1458
- if (spec.outputSchema && event.toolName === OUTPUT_TOOL_NAME) {
1459
- if (event.isError) {
1460
- rs.degrade.outputErrorStreak += 1;
1461
- if (rs.degrade.outputErrorStreak > rs.limits.outputRetryCap) {
1462
- rs.degrade.outputInvalid = true;
1463
- void prepared.harness.abort();
1464
- prepared.abortController.abort();
1465
- }
1466
- }
1467
- else {
1468
- rs.degrade.outputErrorStreak = 0;
1469
- }
1470
- }
1471
- };
1472
- const onTurnEnd = () => {
1473
- stats.turns += 1;
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() }
1477
- : { type: "turn_end", usageMissing: true, ...stopExtra, ...ident() });
1478
- rs.turn.turnUsageMissing = false;
1479
- rs.turn.turnStopReason = undefined;
1480
- if (parentToolCallId !== undefined) {
1481
- const progress = {
1482
- type: "task_progress",
1483
- taskId: rs.telemetry.taskId,
1484
- ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
1485
- ...(subagentName ? { name: subagentName } : {}),
1486
- usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
1487
- status: "running",
1488
- ...ident(),
1489
- };
1490
- queue.push(progress);
1491
- try {
1492
- internals?.onForwardEvent?.(progress);
1493
- }
1494
- catch {
1495
- }
1496
- }
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;
1503
- toolStartAt.clear();
1504
- if (rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns) {
1505
- rs.limits.turnsExceeded = true;
1506
- if (prepared.suspendForResource === undefined) {
1507
- void prepared.harness.abort();
1508
- prepared.abortController.abort();
1509
- }
1510
- }
1511
- else {
1512
- if (rs.degrade.degraded === undefined &&
1513
- spec.degrade &&
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);
1519
- if (prepared.callCapRef &&
1520
- spec.limits?.maxOutputTokens === undefined &&
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;
1524
- }
1525
- }
1526
- if (!rs.limits.budgetHit && rs.budget.overBudget()) {
1527
- rs.limits.budgetHit = "exceeded";
1528
- if (prepared.suspendForResource === undefined) {
1529
- void prepared.harness.abort();
1530
- prepared.abortController.abort();
1531
- }
1532
- }
1533
- }
1534
- };
2066
+ rs.turn.toolBatch = [];
2067
+ const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd } = makeHarnessHandlers(prepared, stats, rs, {
2068
+ spec, queue, internals, buildFinalizeText, ident, nudgeSchedule, parentToolCallId, subagentName,
2069
+ pushContent, emitCommitted, startedToolCallIds, toolStartAt, turnToolSpan, writeFamilyOf,
2070
+ toolLabels, postToolBatchHook, batchArgs,
2071
+ runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
2072
+ });
1535
2073
  const recordCompactionUsage = (m, msg) => {
1536
2074
  const u = msg?.usage;
1537
2075
  if (!u)
@@ -1622,7 +2160,7 @@ export class Runner {
1622
2160
  (rs.counters.finalVerifyInjections === 0 || (rs.counters.finalVerifyInjections === 1 && rs.counters.groundingSignalPreR9 && !rs.counters.groundingSignalPostR9)) &&
1623
2161
  rs.counters.wroteThisRun &&
1624
2162
  prepared.outputRef.set !== true &&
1625
- !finalizeInjected &&
2163
+ !rs.counters.finalizeInjected &&
1626
2164
  !(rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns - 1)) {
1627
2165
  rs.counters.finalVerifyInjections += 1;
1628
2166
  if (rs.counters.finalVerifyInjections === 2) {
@@ -1792,7 +2330,7 @@ export class Runner {
1792
2330
  this.recordCompactionReuse(prepared, comp);
1793
2331
  if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
1794
2332
  const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
1795
- compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
2333
+ rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
1796
2334
  }
1797
2335
  queue.push({
1798
2336
  type: "compacted",
@@ -1834,507 +2372,23 @@ export class Runner {
1834
2372
  },
1835
2373
  });
1836
2374
  const rapidRefill = createRapidRefillState();
1837
- let compactionFloor = 0;
1838
- let trimForceBackoff = false;
1839
2375
  const drainManualCompact = (outcome) => {
1840
2376
  manualCompactRef.requested = false;
1841
2377
  for (const w of manualCompactRef.waiters.splice(0))
1842
2378
  w.resolve(outcome);
1843
2379
  };
1844
- const onTurnBoundary = async (event) => {
1845
- if (prepared.callCapRef && turnToolSpan.startMin !== undefined && turnToolSpan.endMax !== undefined) {
1846
- recordToolCycleSample(prepared.callCapRef.state, turnToolSpan.endMax - turnToolSpan.startMin);
1847
- }
1848
- turnToolSpan.startMin = undefined;
1849
- turnToolSpan.endMax = undefined;
1850
- let boundarySteered = false;
1851
- if (prepared.suspendForResource === undefined) {
1852
- const nowMs = Date.now();
1853
- if (!finalizeInjected &&
1854
- spec.limits?.gracefulFinalize !== false &&
1855
- walltimeDeadlineMs !== undefined &&
1856
- prepared.callCapRef !== undefined) {
1857
- const remainingMs = walltimeDeadlineMs - nowMs;
1858
- if (remainingMs > 0 && remainingMs < estimateCycleMs(prepared.callCapRef.state) + (effectiveCushionMs(prepared.callCapRef) ?? 0)) {
1859
- finalizeInjected = true;
1860
- prepared.callCapRef.finalizeMode = true;
1861
- void prepared.harness.setThinkingLevel("off").catch(() => { });
1862
- prepared.turnSnapshot?.refreshRequestPolicy({ thinkingLevel: "off" });
1863
- nudgeIdx = nudgeSchedule.length;
1864
- const finalizeText = buildFinalizeText();
1865
- void prepared.harness.steer(finalizeText, { engineMinted: true }).catch(() => { });
1866
- boundarySteered = true;
1867
- queue.push({ type: "steering_injected", source: "finalize", preview: finalizeText.slice(0, 220), ...ident() });
1868
- }
1869
- }
1870
- let due;
1871
- while (nudgeIdx < nudgeSchedule.length && nowMs >= nudgeSchedule[nudgeIdx].atMs) {
1872
- due = nudgeSchedule[nudgeIdx++];
1873
- }
1874
- if (due) {
1875
- void prepared.harness.steer(due.text, { engineMinted: true }).catch(() => { });
1876
- rs.counters.nudgesSent += 1;
1877
- boundarySteered = true;
1878
- queue.push({ type: "steering_injected", source: "deadline_nudge", preview: due.text.slice(0, 220), ...ident() });
1879
- }
1880
- }
1881
- const walltimeHit = walltimeMonotonicDeadline !== undefined && performance.now() >= walltimeMonotonicDeadline;
1882
- const preemptHit = spec.preemptSignal?.aborted === true;
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() }));
1886
- }
1887
- if (prepared.suspendForResource &&
1888
- (rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || walltimeHit || preemptHit) &&
1889
- prepared.suspendRef.token === undefined) {
1890
- const reason = preemptHit
1891
- ? "preempt"
1892
- : rs.limits.budgetHit !== undefined
1893
- ? "budget"
1894
- : walltimeHit
1895
- ? "walltime"
1896
- : "turns";
1897
- const sliceSpend = { costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) };
1898
- if (await prepared.suspendForResource(reason, sliceSpend)) {
1899
- rs.limits.turnsExceeded = false;
1900
- rs.limits.budgetHit = undefined;
1901
- }
1902
- else {
1903
- void prepared.harness.abort();
1904
- prepared.abortController.abort();
1905
- }
1906
- return undefined;
1907
- }
1908
- if (walltimeMonotonicDeadline !== undefined &&
1909
- prepared.suspendForResource === undefined &&
1910
- performance.now() >= walltimeMonotonicDeadline) {
1911
- if (!timeout.fired) {
1912
- timeout.fired = true;
1913
- timeout.clear();
1914
- timeout.latenessMs = Math.max(timeout.latenessMs ?? 0, performance.now() - walltimeMonotonicDeadline);
1915
- walltimeSyncBackstopFired = true;
1916
- void prepared.harness.abort();
1917
- prepared.abortController.abort();
1918
- }
1919
- return undefined;
1920
- }
1921
- if (prepared.reviewRequestRef.pending !== undefined) {
1922
- const { reason } = prepared.reviewRequestRef.pending;
1923
- prepared.reviewRequestRef.pending = undefined;
1924
- const honorable = prepared.suspendForReview !== undefined &&
1925
- !prepared.abortController.signal.aborted &&
1926
- prepared.suspendRef.token === undefined &&
1927
- prepared.reviewRef.token === undefined;
1928
- if (honorable && prepared.suspendForReview) {
1929
- await prepared.suspendForReview(reason);
1930
- return undefined;
1931
- }
1932
- emitTrace(rs.telemetry.tracer, () => ({ kind: "review.dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
1933
- }
1934
- if (prepared.lspDiagnostics && !prepared.lspDiagnostics.registry.isEmpty() && !prepared.abortController.signal.aborted) {
1935
- const files = prepared.lspDiagnostics.registry.drain();
1936
- if (files.length > 0) {
1937
- queue.push({ type: "diagnostics", files, isNew: true, ...ident() });
1938
- const block = formatDiagnosticsBlock(files);
1939
- void prepared.harness.steer(block, { provenance: "engine-note" }).catch(() => undefined);
1940
- boundarySteered = true;
1941
- }
1942
- }
1943
- let boundaryAttachmentBytes = 0;
1944
- let attachmentsPayload;
1945
- if ((rs.attach.dateState !== undefined || rs.attach.instrState !== undefined || rs.attach.attachState !== undefined) &&
1946
- !boundarySteered &&
1947
- !finalizeInjected &&
1948
- rs.counters.finalVerifyInjections === 0 &&
1949
- !prepared.abortController.signal.aborted) {
1950
- const due = [];
1951
- if (rs.attach.attachState !== undefined) {
1952
- const changedFilesOn = Boolean(rs.attach.attachmentsCfg?.changedFiles);
1953
- let changed;
1954
- if (changedFilesOn && prepared.detectExternalChanges !== undefined) {
1955
- const maxFiles = typeof rs.attach.attachmentsCfg?.changedFiles === "object"
1956
- ? (rs.attach.attachmentsCfg.changedFiles.maxFiles ?? CHANGED_FILES_MAX)
1957
- : CHANGED_FILES_MAX;
1958
- const SCAN_CEILING_MS = 2_000;
1959
- const scan = await Promise.race([
1960
- prepared.detectExternalChanges(maxFiles),
1961
- new Promise((resolve) => {
1962
- const t = setTimeout(() => resolve({ changed: [], evicted: [] }), SCAN_CEILING_MS);
1963
- t.unref?.();
1964
- }),
1965
- ]);
1966
- for (const p of scan.evicted)
1967
- rs.attach.attachState.surfacedMtime.delete(p);
1968
- changed = scan.changed;
1969
- }
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;
1973
- const pendingTools = toolsDeltaOn && prepared.toolsDeltaRef !== undefined && prepared.toolsDeltaRef.pending.length > 0
1974
- ? [...prepared.toolsDeltaRef.pending]
1975
- : undefined;
1976
- const mcpInstructionsOn = rs.attach.attachmentsCfg?.mcpInstructions === true;
1977
- const mcpDelta = prepared.mcp.instructionsDelta;
1978
- const mcpAdds = mcpInstructionsOn && mcpDelta.pendingAdds.length > 0 ? [...mcpDelta.pendingAdds] : undefined;
1979
- const mcpRemovals = mcpInstructionsOn && mcpDelta.pendingRemovals.length > 0 ? [...mcpDelta.pendingRemovals] : undefined;
1980
- const mcpDropped = mcpInstructionsOn && prepared.mcp.droppedTools.length > 0 ? selectMcpDroppedBatch(prepared.mcp.droppedTools) : undefined;
1981
- const budgetUsdOn = rs.attach.attachmentsCfg?.budgetUsd === true && rs.budget.maxCostMicroUsd !== undefined && rs.turn.lastTurnHadToolCalls;
1982
- due.push(...collectDueAttachments(rs.attach.attachState, {
1983
- turn: stats.turns,
1984
- cadenceTurn: rs.counters.cadenceTurns,
1985
- todoMounted: todoToolMounted,
1986
- taskListMounted: taskToolsMounted,
1987
- planActive: prepared.planModeRef.active,
1988
- config: {
1989
- todoReminder: rs.attach.attachmentsCfg?.todoReminder === true,
1990
- ...(rs.attach.attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: rs.attach.attachmentsCfg.todoReminderMode } : {}),
1991
- changedFiles: changedFilesOn,
1992
- planModeReminder: rs.attach.attachmentsCfg?.planModeReminder === true,
1993
- budgetUsd: budgetUsdOn,
1994
- backgroundTasks: backgroundTasksOn,
1995
- toolsDelta: toolsDeltaOn,
1996
- agentListing: rs.attach.agentListingOn,
1997
- skillsListing: rs.attach.skillsListingOn,
1998
- mcpInstructions: mcpInstructionsOn,
1999
- },
2000
- ...(changed !== undefined ? { changedFiles: changed } : {}),
2001
- ...(budgetUsdOn && rs.budget.maxCostMicroUsd !== undefined
2002
- ? { budgetUsd: { used: stats.costMicroUsd / 1e6, total: rs.budget.maxCostMicroUsd / 1e6 } }
2003
- : {}),
2004
- ...(bgTasks !== undefined ? { backgroundTasks: bgTasks } : {}),
2005
- ...(pendingTools !== undefined ? { newTools: pendingTools } : {}),
2006
- ...(rs.attach.agentListingOn && prepared.agentListing !== undefined
2007
- ? {
2008
- agentListing: prepared.agentListing.entries,
2009
- agentToolName: prepared.agentListing.toolName,
2010
- ...(prepared.agentListing.models !== undefined ? { agentModels: prepared.agentListing.models } : {}),
2011
- }
2012
- : {}),
2013
- ...(rs.attach.skillsListingOn && prepared.skillsListing !== undefined ? { skillsListing: prepared.skillsListing.entries } : {}),
2014
- ...(mcpAdds !== undefined || mcpRemovals !== undefined
2015
- ? { mcpInstructions: { added: mcpAdds ?? [], removed: mcpRemovals ?? [] } }
2016
- : {}),
2017
- ...(mcpDropped !== undefined ? { mcpDroppedTools: mcpDropped } : {}),
2018
- }));
2019
- if (pendingTools !== undefined && due.some((a) => a.source === "tools_delta" && a.body === renderToolsDelta(pendingTools))) {
2020
- prepared.toolsDeltaRef.pending.splice(0, pendingTools.length);
2021
- }
2022
- if ((mcpAdds !== undefined || mcpRemovals !== undefined) &&
2023
- due.some((a) => a.source === "mcp_instructions" && a.body === renderMcpInstructionsDelta(mcpAdds ?? [], mcpRemovals ?? []))) {
2024
- if (mcpAdds !== undefined)
2025
- mcpDelta.pendingAdds.splice(0, mcpAdds.length);
2026
- if (mcpRemovals !== undefined)
2027
- mcpDelta.pendingRemovals.splice(0, mcpRemovals.length);
2028
- }
2029
- if (mcpDropped !== undefined &&
2030
- due.some((a) => a.source === "mcp_dropped_tools" && a.body === renderMcpDroppedTools(mcpDropped))) {
2031
- prepared.mcp.droppedTools.splice(0, mcpDropped.length);
2032
- }
2033
- if (rs.attach.agentListingOn && prepared.agentListing !== undefined && due.some((a) => a.source === "agent_listing")) {
2034
- const listing = prepared.agentListing;
2035
- const intactBody = renderAgentListingDelta(rs.attach.attachState, listing.entries, listing.toolName, listing.models);
2036
- if (intactBody !== undefined && due.some((a) => a.source === "agent_listing" && a.body === intactBody)) {
2037
- commitAgentListing(rs.attach.attachState, listing.entries, listing.models);
2038
- prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
2039
- if (listing.models !== undefined)
2040
- prepared.announcedListingsRef.models = [...listing.models];
2041
- await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
2042
- }
2043
- }
2044
- if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined && due.some((a) => a.source === "skills_listing")) {
2045
- const listing = prepared.skillsListing;
2046
- const intactBody = renderSkillsListingDelta(rs.attach.attachState, listing.entries);
2047
- if (intactBody !== undefined && due.some((a) => a.source === "skills_listing" && a.body === intactBody)) {
2048
- commitSkillsListing(rs.attach.attachState, listing.entries);
2049
- prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
2050
- await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
2051
- }
2052
- }
2053
- }
2054
- if (rs.attach.dateState !== undefined && rs.turn.lastTurnHadToolCalls) {
2055
- const today = prepared.dateChange.today();
2056
- const cand = collectDateChange(rs.attach.dateState, today);
2057
- if (cand !== undefined) {
2058
- due.unshift(cand);
2059
- rs.attach.dateState.announcedDate = today;
2060
- }
2061
- }
2062
- if (rs.attach.instrState !== undefined && rs.attach.instrProbe !== undefined && rs.turn.lastTurnHadToolCalls) {
2063
- let probed = null;
2064
- try {
2065
- const PROBE_CEILING_MS = 2_000;
2066
- probed = await Promise.race([
2067
- rs.attach.instrProbe(prepared.instructionSources),
2068
- new Promise((resolve) => {
2069
- const t = setTimeout(() => resolve(null), PROBE_CEILING_MS);
2070
- t.unref?.();
2071
- }),
2072
- ]);
2073
- }
2074
- catch {
2075
- probed = null;
2076
- }
2077
- const cand = collectInstructionsChange(rs.attach.instrState, probed);
2078
- if (cand !== undefined) {
2079
- due.splice(due.length > 0 && due[0].source === "date_change" ? 1 : 0, 0, cand.attachment);
2080
- commitInstructionsChange(rs.attach.instrState, cand.announced);
2081
- }
2082
- }
2083
- if (due.length > 0) {
2084
- attachmentsPayload = due.map((a) => `<system-reminder>\n${sanitizeUntrustedText(a.body)}\n</system-reminder>`).join("\n");
2085
- for (const a of due)
2086
- boundaryAttachmentBytes += Buffer.byteLength(a.body, "utf8");
2087
- attachmentsInjected += due.length;
2088
- for (const a of due) {
2089
- queue.push({ type: "steering_injected", source: a.source, preview: a.body.slice(0, 220), ...ident() });
2090
- }
2091
- }
2092
- }
2093
- let batchContextBlock;
2094
- if (postToolBatchHook !== undefined && toolBatch.length > 0) {
2095
- const batch = toolBatch;
2096
- toolBatch = [];
2097
- batchArgs?.clear();
2098
- if (!boundarySteered && !finalizeInjected && rs.counters.finalVerifyInjections === 0 && !prepared.abortController.signal.aborted) {
2099
- try {
2100
- const r = await postToolBatchHook(batch);
2101
- if (r?.additionalContext) {
2102
- const body = sanitizeUntrustedText(r.additionalContext);
2103
- const budget = ATTACHMENT_BYTE_CAP - boundaryAttachmentBytes;
2104
- if (budget >= BATCH_CONTEXT_MIN_KEEP_BYTES) {
2105
- const clipped = clipToByteBudget(body, budget) === body
2106
- ? body
2107
- : `${clipToByteBudget(body, budget - Buffer.byteLength(BATCH_TRUNCATION_MARKER, "utf8"))}${BATCH_TRUNCATION_MARKER}`;
2108
- batchContextBlock = `<system-reminder>\n${clipped}\n</system-reminder>`;
2109
- }
2110
- }
2111
- }
2112
- catch (err) {
2113
- try {
2114
- this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
2115
- }
2116
- catch {
2117
- }
2118
- }
2119
- }
2120
- }
2121
- if (attachmentsPayload !== undefined || batchContextBlock !== undefined) {
2122
- const payload = attachmentsPayload !== undefined && batchContextBlock !== undefined ? `${attachmentsPayload}\n${batchContextBlock}` : (attachmentsPayload ?? batchContextBlock);
2123
- void prepared.harness.steer(payload, { engineMinted: true }).catch(() => { });
2124
- }
2125
- const forceManual = manualCompactRef.requested;
2126
- if ((!withinTaskCompaction && !forceManual) ||
2127
- (finalizeInjected && !forceManual) ||
2128
- prepared.abortController.signal.aborted ||
2129
- prepared.suspendRef.token !== undefined ||
2130
- prepared.reviewRef.token !== undefined) {
2131
- if (forceManual) {
2132
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "task_ending", ts: Date.now() }));
2133
- drainManualCompact("mooted");
2134
- }
2135
- return undefined;
2136
- }
2137
- if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES) {
2138
- if (forceManual) {
2139
- emitTrace(rs.telemetry.tracer, () => ({
2140
- kind: "compaction.failed",
2141
- version: 1,
2142
- taskId: rs.telemetry.taskId,
2143
- trigger: "manual",
2144
- reason: "compaction circuit breaker open (3 consecutive failures this task) — manual request drained without an attempt",
2145
- ts: Date.now(),
2146
- }));
2147
- drainManualCompact("failed");
2148
- }
2149
- return undefined;
2150
- }
2151
- const claimedManual = forceManual
2152
- ? (() => {
2153
- const waiters = manualCompactRef.waiters.splice(0);
2154
- let instructions;
2155
- for (let i = waiters.length - 1; i >= 0; i--) {
2156
- if (waiters[i].instructions !== undefined) {
2157
- instructions = waiters[i].instructions;
2158
- break;
2159
- }
2160
- }
2161
- return { instructions, waiters };
2162
- })()
2163
- : undefined;
2164
- if (forceManual) {
2165
- manualCompactRef.requested = false;
2166
- }
2167
- const resolveClaimedWaiters = (outcome) => {
2168
- if (claimedManual !== undefined)
2169
- for (const w of claimedManual.waiters.splice(0))
2170
- w.resolve(outcome);
2171
- };
2172
- const claimedCancelSignals = claimedManual?.waiters.flatMap((w) => (w.signal !== undefined ? [w.signal] : [])) ?? [];
2173
- const manualCancelSignal = claimedCancelSignals.length === 0
2174
- ? undefined
2175
- : claimedCancelSignals.length === 1
2176
- ? claimedCancelSignals[0]
2177
- : AbortSignal.any(claimedCancelSignals);
2178
- const compactionPassSignal = claimedCancelSignals.length > 0
2179
- ? AbortSignal.any([prepared.abortController.signal, ...claimedCancelSignals])
2180
- : prepared.abortController.signal;
2181
- const trimPressureArmed = prepared.trimPressureRef.droppedMessages;
2182
- if (trimPressureArmed)
2183
- prepared.trimPressureRef.droppedMessages = false;
2184
- const trimPressure = trimPressureArmed && !trimForceBackoff;
2185
- let manualOutcome = "failed";
2186
- if (finalizeInjected && forceManual) {
2187
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.manual_in_finalize", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2188
- }
2189
- const manualInstructions = claimedManual?.instructions;
2190
- try {
2191
- const comp = await maybeCompact({
2192
- session: prepared.session,
2193
- epochDeclaredSections: prepared.epochDeclaredSections,
2194
- ...(() => {
2195
- const ca = prepared.centerCompactionCandidate?.();
2196
- return ca !== undefined ? { centerAdoption: ca } : {};
2197
- })(),
2198
- model: event.model,
2199
- compactionModel: prepared.compModel,
2200
- brain: compactionBrain,
2201
- getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
2202
- thinking: prepared.thinking,
2203
- settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
2204
- customInstructions: manualInstructions ?? spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
2205
- signal: compactionPassSignal,
2206
- ...(manualCancelSignal !== undefined ? { manualCancelSignal } : {}),
2207
- minTokens: forceManual ? 0 : compactionFloor,
2208
- force: forceManual,
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() })),
2210
- forceUnderThreshold: trimPressure,
2211
- overheadTokens: prepared.promptOverheadTokens,
2212
- workingFileAttachments: spec.compaction?.attachWorkingFiles !== false && prepared.readTaskFile
2213
- ? {
2214
- readFile: prepared.readTaskFile,
2215
- ...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
2216
- ...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
2217
- ...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
2218
- ...(prepared.instructionSources
2219
- ? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
2220
- : {}),
2221
- ...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
2222
- }
2223
- : undefined,
2224
- ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
2225
- ...this.seamCCompactionOptions(prepared),
2226
- ...windowSafetyOptions(event.model),
2227
- ...this.compactionHookOptions(spec, prepared.sessionId, trimPressure ? "forced" : forceManual ? "manual" : "auto"),
2228
- });
2229
- if (comp.blocked) {
2230
- emitTrace(rs.telemetry.tracer, () => ({
2231
- kind: "compaction.blocked",
2232
- version: 1,
2233
- taskId: rs.telemetry.taskId,
2234
- trigger: forceManual ? "manual" : "auto",
2235
- ts: Date.now(),
2236
- }));
2237
- }
2238
- else {
2239
- compactionBreaker.failures = 0;
2240
- }
2241
- trimForceBackoff = nextTrimForceBackoff(trimForceBackoff, comp, trimPressure);
2242
- if (comp.contextUsage) {
2243
- queue.push({ type: "context_usage", ...comp.contextUsage, ...ident() });
2244
- }
2245
- if (comp.suppressedByFloor) {
2246
- const s = comp.suppressedByFloor;
2247
- emitTrace(rs.telemetry.tracer, () => ({
2248
- kind: "compaction.suppressed",
2249
- version: 1,
2250
- taskId: rs.telemetry.taskId,
2251
- estTokens: s.estTokens,
2252
- floor: s.floor,
2253
- ts: Date.now(),
2254
- }));
2255
- }
2256
- manualOutcome = comp.compacted ? "compacted" : comp.blocked ? "blocked" : comp.disabled ? "disabled" : "noop";
2257
- if (forceManual && !comp.compacted && !comp.blocked) {
2258
- if (comp.disabled) {
2259
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.disabled", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2260
- }
2261
- else {
2262
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.noop", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2263
- }
2264
- }
2265
- if (comp.compacted && !forceManual) {
2266
- if (recordCompactionAndCheckRapidRefill(rapidRefill, stats.turns)) {
2267
- compactionBreaker.failures = MAX_CONSECUTIVE_COMPACTION_FAILURES;
2268
- this.deps.onError?.(new Error("compaction.rapid_refill: the context refilled within <3 turns of compaction 3 times in a row — compaction disabled for the rest of this task (thrash spiral; the transcript is dominated by incompressible content)"), { phase: "compaction", sessionId: prepared.sessionId });
2269
- }
2270
- }
2271
- this.recordCompactionReuse(prepared, comp);
2272
- if (comp.compacted) {
2273
- queue.push({
2274
- type: "compacted",
2275
- trigger: forceManual ? "manual" : "auto",
2276
- tokensBefore: comp.tokensBefore ?? 0,
2277
- ...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
2278
- ...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
2279
- ...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
2280
- ...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
2281
- ...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
2282
- ...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
2283
- ...(comp.modelFallback ? { modelFallback: true } : {}),
2284
- ...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
2285
- ...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
2286
- ...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
2287
- });
2288
- if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
2289
- const pd = comp.phaseDurations;
2290
- const pdDur = comp.durationMs;
2291
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
2292
- }
2293
- prepared.cacheBreakDetector?.notifyCompaction();
2294
- if (rs.attach.attachState !== undefined) {
2295
- rs.attach.attachState.postCompactPending = true;
2296
- rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
2297
- }
2298
- if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
2299
- const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
2300
- compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
2301
- }
2302
- }
2303
- }
2304
- catch (err) {
2305
- const manualCancelAbort = !prepared.abortController.signal.aborted && isCompactionManualCancel(err);
2306
- if (manualCancelAbort) {
2307
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.mooted", version: 1, taskId: rs.telemetry.taskId, reason: "cancelled", ts: Date.now() }));
2308
- manualOutcome = "mooted";
2309
- }
2310
- else if (!prepared.abortController.signal.aborted) {
2311
- const walltimeAbort = isCompactionWalltimeAbort(err);
2312
- if (!walltimeAbort)
2313
- compactionBreaker.failures += 1;
2314
- this.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
2315
- const msg = String(err instanceof Error ? err.message : err);
2316
- emitTrace(rs.telemetry.tracer, () => ({
2317
- kind: "compaction.failed",
2318
- version: 1,
2319
- taskId: rs.telemetry.taskId,
2320
- trigger: trimPressure ? "forced" : forceManual ? "manual" : "auto",
2321
- reason: walltimeAbort
2322
- ? "walltime soft-deadline abort (engine-initiated write-out protection; not counted toward the breaker)"
2323
- : msg.length > 512
2324
- ? `${msg.slice(0, 512)}…`
2325
- : msg,
2326
- ts: Date.now(),
2327
- }));
2328
- manualOutcome = "failed";
2329
- }
2330
- else {
2331
- manualOutcome = "mooted";
2332
- }
2333
- }
2334
- if (forceManual)
2335
- resolveClaimedWaiters(manualOutcome);
2336
- return undefined;
2337
- };
2380
+ const onTurnBoundary = makeTurnBoundary(prepared, stats, rs, {
2381
+ spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted, turnToolSpan,
2382
+ walltimeDeadlineMs, walltimeMonotonicDeadline, nudgeSchedule, buildFinalizeText,
2383
+ timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction,
2384
+ compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact,
2385
+ runnerHooks: {
2386
+ onError: this.deps.onError,
2387
+ seamCCompactionOptions: (p) => this.seamCCompactionOptions(p),
2388
+ compactionHookOptions: (s, sid, trig) => this.compactionHookOptions(s, sid, trig),
2389
+ recordCompactionReuse: (p, c) => this.recordCompactionReuse(p, c),
2390
+ },
2391
+ });
2338
2392
  const unsubBoundary = prepared.harness.on("turn_boundary", onTurnBoundary);
2339
2393
  const unsub = prepared.harness.subscribe((event) => {
2340
2394
  switch (event.type) {
@@ -2497,7 +2551,7 @@ export class Runner {
2497
2551
  f.commit();
2498
2552
  queue.push({ type: "steering_injected", source: f.source, preview: f.body.slice(0, 220), ...ident() });
2499
2553
  }
2500
- attachmentsInjected += firstFrames.length;
2554
+ rs.attach.attachmentsInjected += firstFrames.length;
2501
2555
  if (firstFrames.length > 0)
2502
2556
  await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
2503
2557
  const enginePrefixChars = effectiveObjective.length - spec.objective.length;
@@ -2575,7 +2629,7 @@ export class Runner {
2575
2629
  }
2576
2630
  const comp = await this.finish(spec, prepared, {
2577
2631
  skipCompaction: durablyPaused || compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES,
2578
- minTokens: compactionFloor,
2632
+ minTokens: rs.counters.compactionFloor,
2579
2633
  brain: compactionBrain,
2580
2634
  windowSafety: windowSafetyOptions(prepared.model),
2581
2635
  });
@@ -2642,9 +2696,9 @@ export class Runner {
2642
2696
  const toolCuts = prepared.callCapRef?.toolCuts ?? 0;
2643
2697
  const pendingCutTools = prepared.callCapRef?.pendingCutTools ?? 0;
2644
2698
  const toolCutKills = prepared.cutKills?.kills ?? 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) {
2699
+ if (rs.counters.finalizeInjected || rs.counters.nudgesSent > 0 || capShrinks > 0 || rs.counters.callCutoffs > 0 || toolClamps > 0 || toolCuts > 0 || toolCutKills > 0 || pendingCutTools > 0 || rs.counters.finalVerifyInjections > 0 || rs.attach.attachmentsInjected > 0 || rs.counters.repetitionCuts > 0 || rs.counters.repetitionSpared > 0) {
2646
2700
  stats.mechanisms = {
2647
- ...(finalizeInjected ? { finalizeInjected: true } : {}),
2701
+ ...(rs.counters.finalizeInjected ? { finalizeInjected: true } : {}),
2648
2702
  ...(rs.counters.nudgesSent > 0 ? { nudgesSent: rs.counters.nudgesSent } : {}),
2649
2703
  ...(capShrinks > 0 ? { capShrinks } : {}),
2650
2704
  ...(rs.counters.callCutoffs > 0 ? { callCutoffs: rs.counters.callCutoffs } : {}),
@@ -2654,7 +2708,7 @@ export class Runner {
2654
2708
  ...(pendingCutTools > 0 ? { pendingCutTools } : {}),
2655
2709
  ...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
2656
2710
  ...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjections: rs.counters.finalVerifyInjections } : {}),
2657
- ...(attachmentsInjected > 0 ? { attachmentsInjected } : {}),
2711
+ ...(rs.attach.attachmentsInjected > 0 ? { attachmentsInjected: rs.attach.attachmentsInjected } : {}),
2658
2712
  ...(rs.counters.repetitionCuts > 0 ? { repetitionCuts: rs.counters.repetitionCuts } : {}),
2659
2713
  ...(rs.counters.repetitionSpared > 0 ? { repetitionSpared: rs.counters.repetitionSpared } : {}),
2660
2714
  ...(rs.counters.repetitionEvents.length > 0 ? { repetitionEvents: rs.counters.repetitionEvents } : {}),
@@ -2790,7 +2844,7 @@ export class Runner {
2790
2844
  ...(timeout.latenessMs !== undefined && timeout.latenessMs > TIMER_LATENESS_REPORT_MS
2791
2845
  ? { timerLatenessMs: timeout.latenessMs }
2792
2846
  : {}),
2793
- ...(walltimeSyncBackstopFired ? { walltimeSyncBackstop: true } : {}),
2847
+ ...(rs.counters.walltimeSyncBackstopFired ? { walltimeSyncBackstop: true } : {}),
2794
2848
  ts: Date.now(),
2795
2849
  }));
2796
2850
  setResult(result);