@sema-agent/core 7.11.1 → 7.12.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 (78) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/core/auto-mode-arming.d.ts +10 -14
  3. package/dist/core/auto-mode-arming.js +3 -9
  4. package/dist/core/auto-mode-defaults.d.ts +0 -2
  5. package/dist/core/auto-mode-defaults.js +0 -1
  6. package/dist/core/auto-mode-rebuild.d.ts +6 -13
  7. package/dist/core/auto-mode-rebuild.js +0 -2
  8. package/dist/core/auto-mode.d.ts +30 -89
  9. package/dist/core/auto-mode.js +12 -59
  10. package/dist/core/checkpoint-store.d.ts +1 -3
  11. package/dist/core/gate-fold.js +1 -9
  12. package/dist/core/gate-lanes.js +15 -9
  13. package/dist/core/hooks.d.ts +6 -0
  14. package/dist/core/runner/compaction-knobs.d.ts +45 -0
  15. package/dist/core/runner/compaction-knobs.js +3 -0
  16. package/dist/core/runner/contracts.d.ts +49 -10
  17. package/dist/core/runner/denial-limit-arms.d.ts +10 -13
  18. package/dist/core/runner/denial-limit-arms.js +9 -7
  19. package/dist/core/runner/gate-exit.js +9 -1
  20. package/dist/core/runner/prepare-caps-and-workflow.d.ts +1 -1
  21. package/dist/core/runner/prepare-caps-and-workflow.js +0 -5
  22. package/dist/core/runner/prepare-suspend-saga.d.ts +0 -2
  23. package/dist/core/runner/prepare-suspend-saga.js +2 -10
  24. package/dist/core/runner/prepare-task.js +1 -1
  25. package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -1
  26. package/dist/core/runner/prepare-wiring-manifest.js +1 -8
  27. package/dist/core/runner/run-attachment-seats.d.ts +22 -0
  28. package/dist/core/runner/run-attachment-seats.js +187 -0
  29. package/dist/core/runner/run-brain-sinks.d.ts +29 -0
  30. package/dist/core/runner/run-brain-sinks.js +61 -0
  31. package/dist/core/runner/run-clock-and-content.d.ts +52 -0
  32. package/dist/core/runner/run-clock-and-content.js +26 -0
  33. package/dist/core/runner/run-compaction-machinery.d.ts +35 -0
  34. package/dist/core/runner/run-compaction-machinery.js +98 -0
  35. package/dist/core/runner/run-git-lane.d.ts +64 -0
  36. package/dist/core/runner/run-git-lane.js +102 -0
  37. package/dist/core/runner/run-identity-wiring.d.ts +77 -0
  38. package/dist/core/runner/run-identity-wiring.js +85 -0
  39. package/dist/core/runner/run-leg.d.ts +106 -0
  40. package/dist/core/runner/run-leg.js +462 -0
  41. package/dist/core/runner/run-notification-lane.d.ts +55 -0
  42. package/dist/core/runner/run-notification-lane.js +128 -0
  43. package/dist/core/runner/run-reasoning-seat.d.ts +27 -0
  44. package/dist/core/runner/run-reasoning-seat.js +48 -0
  45. package/dist/core/runner/run-recovery-lanes.d.ts +54 -0
  46. package/dist/core/runner/run-recovery-lanes.js +180 -0
  47. package/dist/core/runner/run-settle-and-teardown.d.ts +109 -0
  48. package/dist/core/runner/run-settle-and-teardown.js +324 -0
  49. package/dist/core/runner/run-stop-and-final-verify.d.ts +32 -0
  50. package/dist/core/runner/run-stop-and-final-verify.js +159 -0
  51. package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +38 -0
  52. package/dist/core/runner/run-telemetry-and-budget-seats.js +159 -0
  53. package/dist/core/runner/run-terminal-adoption.d.ts +99 -0
  54. package/dist/core/runner/run-terminal-adoption.js +120 -0
  55. package/dist/core/runner/run-tool-mount-facts.d.ts +26 -0
  56. package/dist/core/runner/run-tool-mount-facts.js +58 -0
  57. package/dist/core/runner/run-turn-boundary.d.ts +0 -35
  58. package/dist/core/runner/run-turn-boundary.js +1 -3
  59. package/dist/core/runner/runtask.d.ts +14 -3
  60. package/dist/core/runner/runtask.js +125 -2102
  61. package/dist/core/runner-deps.d.ts +4 -14
  62. package/dist/core/store-contracts/workflow-journal-store-contract.d.ts +7 -0
  63. package/dist/core/store-contracts/workflow-journal-store-contract.js +85 -0
  64. package/dist/core/tool-policy.d.ts +37 -93
  65. package/dist/core/tool-policy.js +1 -11
  66. package/dist/core/trace.d.ts +6 -7
  67. package/dist/core/wiring-manifest.d.ts +5 -22
  68. package/dist/core/wiring-manifest.js +3 -11
  69. package/dist/core/workflow-journal-store.d.ts +35 -4
  70. package/dist/core/workflow-journal-store.js +19 -2
  71. package/dist/index.d.ts +3 -2
  72. package/dist/index.js +3 -2
  73. package/dist/orchestration/workflow.js +2 -0
  74. package/dist/stores/file/workflow-journal-store.d.ts +7 -10
  75. package/dist/stores/file/workflow-journal-store.js +2 -4
  76. package/dist/tools/fs/fs-bash.js +3 -2
  77. package/package.json +1 -1
  78. package/test/export-surface.snapshot.json +10 -10
@@ -1,69 +1,45 @@
1
- import { createSafeNotifier, observeThenableRejection } from "../safe-notify.js";
2
- import { mintSystemReminder, openSystemReminder } from "../reminder-mint.js";
3
- import { deliverDelegationLifecycle, deliverEngineNotice, undrainedUserInputNotices } from "../types.js";
4
- import { planRejectionClears, resolveTriggerWindow } from "../context-edit.js";
5
- import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
1
+ import { deliverEngineNotice } from "../types.js";
2
+ import { DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
6
3
  import { snapshotActorAssertion } from "../../internal/llm.js";
7
- import { CheckpointError, remainingBudgetMicroUsd, readPendingSteerQueue, remainingTokens, resolveCheckpointStore, realApprovalOrgFact } from "../checkpoint-store.js";
8
- import { GIT_STATUS_ECHO_PREVIEW, stripGitStatusUnits } from "./git-status-frame.js";
4
+ import { CheckpointError, remainingBudgetMicroUsd, realApprovalOrgFact } from "../checkpoint-store.js";
9
5
  import { settleExecutionRecord } from "./execution-record.js";
10
- import { engineVersion } from "../version.js";
11
- import { CONFIG_CATALOG_VERSION, declarationReasons, resolveEffectiveConfig } from "../../config/catalog.js";
12
- import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
13
- import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact, sanitizeCompactionSettings } from "../auto-compaction.js";
6
+ import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact } from "../auto-compaction.js";
14
7
  import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
15
8
  import { boundInputHashOf } from "../canonical-json.js";
16
- import { computeCostMicroUsd, isModelPriced, malformedPricingField, modelCostToPricing } from "../pricing.js";
9
+ import { modelCostToPricing } from "../pricing.js";
17
10
  import { emitTrace } from "../trace.js";
18
11
  import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
19
12
  import { emitTaskOutcome } from "../task-outcome.js";
20
- import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
21
- import { redactThenCut } from "../../agents/subagent-steps.js";
22
- import { resolveReasoning } from "../../brain/reasoning.js";
23
13
  import { adjudicateDerivedRoute, authCarrierFingerprint, fallbackToPrimaryNotice, normalizeBaseUrl, sameRouteIdentity } from "../../brain/route-adjudicator.js";
24
- import { runWithBrainTelemetry, runWithReasoningWireFacts, runWithStatusSink } from "../../brain/status-sink.js";
25
- import { expandTiers, resolveModel, resolveTaskModel } from "../roles.js";
14
+ import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
15
+ import { expandTiers, resolveTaskModel } from "../roles.js";
26
16
  import { screenSwappableDeps } from "../swappable-deps.js";
27
- import { AutoModeBreakerLedger } from "../auto-mode.js";
28
17
  import { runSideQuery } from "../side-query.js";
29
18
  import { generatePromptSuggestions } from "./prompt-suggestions.js";
30
19
  import { PushQueue } from "../push-queue.js";
31
20
  import { TtlSessionStore } from "../session-store.js";
32
- import { toImageContent } from "./image.js";
33
- import { SKILLS_LISTING_PROBE_HEADER, resolveOutputRetries } from "./synthetic-tools.js";
34
- import { cacheFamilyOf, usageCostMicroUsd } from "./usage-accounting.js";
35
- import { assembleResult, errorCodeOf } from "./assemble-result.js";
36
- import { amendTerminal, terminalProjection } from "./terminal-projection.js";
37
- import { AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, agentListingDeltaHeader, attachmentEnvelopeTags, agentListingInitialHeader, replayAnnouncedListing, replayAnnouncedModels, commitAgentListing, commitSkillsListing, createAttachmentState, rebaseCadenceWindows, reduceToolEnd, renderAgentListingDelta, renderOrphanedBackgroundTasks, renderSkillsListingDelta, stampWriteAnchor } from "./turn-attachments.js";
21
+ import { errorCodeOf } from "./assemble-result.js";
38
22
  import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
39
23
  import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
40
- import { drainForwardedFramesBeforeDone, forwardsSubagentEvents } from "./prepare-run-refs.js";
41
24
  import { callFaceSeat, judgeParkedToolIdentity, rosterEntryNamed, toolCallFaceOf } from "../tool-roster.js";
42
25
  import { normalizePersistedRuleHit } from "../gate-lanes.js";
43
26
  import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
44
27
  import { SessionReadFileStates } from "./prepare-hands-readface.js";
45
- import { settleTeardownLeg } from "./teardown-bounded.js";
46
- import { hasDestroy, isIsolated } from "../remote-env.js";
47
- import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
28
+ import { hasDestroy } from "../remote-env.js";
48
29
  import { formatHookFeedback, hookSeatExpiredError, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
49
- import { buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
50
- import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
51
- import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
30
+ import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "../untrusted-text.js";
52
31
  import { RunnerSharedToolResultStore } from "../tool-result-store.js";
53
32
  import { PAUSE_REGISTRY } from "../pause-registry.js";
54
33
  import { refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.js";
55
34
  import { mintGateOutcome } from "./gate-exit.js";
56
- import { defaultTaskRegistry } from "../task-registry.js";
57
- import { discloseDroppedPending, isDelegatedAgentTerminal, isTerminalTaskNotification, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
35
+ import { PendingSessionNotifications } from "../task-notification.js";
58
36
  import { ToolDetachHub } from "../tool-detach.js";
59
- import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
60
37
  import { createRunState } from "./initial-run-state.js";
61
- import { nextHumanInputSeq } from "./steer-admission.js";
62
- import { reconciledToolEndBody, toolEndBodyFrom, toolResultMsg, writeFamilyOfCanonical } from "./tool-end-body.js";
63
- import { answerFaceForRedeemedCall, deepJsonEqual, DEFERRED_REISSUE, pendingContentAskCallId, resumeContinuation, resumeDecisionWasNegative } from "./decide-continuation.js";
64
- import { awaitChargeWithSlowDisclosure, DEFAULT_PRECALL_OUTPUT_TOKENS, discloseUnevaluableWindow, platformLimitTerminal, resolveMaxTurns, startTimeout, TIMER_LATENESS_REPORT_MS } from "./clock-and-limits.js";
65
- import { gitRestateOption, resolveGitLegDelivery, wrapGitFrame } from "./git-leg-delivery.js";
66
- import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES, createTurnBoundary } from "./run-turn-boundary.js";
38
+ import { reconciledToolEndBody, toolEndBodyFrom, toolResultMsg } from "./tool-end-body.js";
39
+ import { answerFaceForRedeemedCall, deepJsonEqual, DEFERRED_REISSUE } from "./decide-continuation.js";
40
+ import { discloseUnevaluableWindow } from "./clock-and-limits.js";
41
+ import { gitRestateOption } from "./git-leg-delivery.js";
42
+ import { createTurnBoundary } from "./run-turn-boundary.js";
67
43
  import { createHarnessHandlers } from "./run-harness-handlers.js";
68
44
  import { resumeAdmission } from "./resume-admission.js";
69
45
  import { resumeReviewOutcome } from "./resume-review-outcome.js";
@@ -78,7 +54,21 @@ import { streamReap } from "./stream-reap.js";
78
54
  import { streamSteerVerb } from "./stream-steer-verb.js";
79
55
  import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
80
56
  import { streamHaltVerbs } from "./stream-halt-verbs.js";
81
- const STOP_HOOK_BLOCK_CAP = 8;
57
+ import { runNotificationLane } from "./run-notification-lane.js";
58
+ import { runLeg } from "./run-leg.js";
59
+ import { runTerminalAdoption } from "./run-terminal-adoption.js";
60
+ import { runSettleAndTeardown } from "./run-settle-and-teardown.js";
61
+ import { runIdentityWiring } from "./run-identity-wiring.js";
62
+ import { runTelemetryAndBudgetSeats } from "./run-telemetry-and-budget-seats.js";
63
+ import { runAttachmentSeats } from "./run-attachment-seats.js";
64
+ import { runToolMountFacts } from "./run-tool-mount-facts.js";
65
+ import { runReasoningSeat } from "./run-reasoning-seat.js";
66
+ import { runClockAndContent } from "./run-clock-and-content.js";
67
+ import { runBrainSinks } from "./run-brain-sinks.js";
68
+ import { runCompactionMachinery } from "./run-compaction-machinery.js";
69
+ import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
70
+ import { runRecoveryLanes } from "./run-recovery-lanes.js";
71
+ import { runGitLane } from "./run-git-lane.js";
82
72
  const ORG_DISCLOSURE_MAX_CHARS = 600;
83
73
  const SUGGESTIONS_DEFAULT_COUNT = 3;
84
74
  const SUGGESTIONS_MAX_COUNT = 8;
@@ -89,7 +79,6 @@ export class Runner {
89
79
  sessions;
90
80
  sessionLocks = new Map();
91
81
  sessionReadStates = new SessionReadFileStates();
92
- autoModeBreakerLedger = new AutoModeBreakerLedger();
93
82
  pendingSessionNotifications = new PendingSessionNotifications();
94
83
  parentConstraintRegistry = new Map();
95
84
  suspendedEnvReaps = new Map();
@@ -329,33 +318,48 @@ export class Runner {
329
318
  ? await this.acquireSessionLock(spec.sessionId)
330
319
  : undefined;
331
320
  try {
332
- await this.runLocked(spec, queue, (r) => {
333
- resultValue = r;
334
- const pcs = internals?.inheritedGate?.parentConstraints;
335
- if (resume !== undefined)
336
- this.locallyClaimedTokens.delete(resume.cp.token);
337
- const rToken = r.terminal.kind === "paused" ? r.terminal.token : undefined;
338
- if (resume !== undefined && rToken !== resume.cp.token) {
339
- this.parentConstraintRegistry.delete(resume.cp.token);
340
- this.suspendedEnvReaps.delete(resume.cp.token);
341
- }
342
- if (r.terminal.kind === "paused" && PAUSE_REGISTRY[r.terminal.gate.kind].taskStatus === "suspended" && pcs !== undefined && pcs.length > 0) {
343
- if (this.parentConstraintRegistry.size >= Runner.PARENT_CONSTRAINT_REGISTRY_CAP) {
344
- const oldest = this.parentConstraintRegistry.keys().next().value;
345
- if (oldest !== undefined)
346
- this.parentConstraintRegistry.delete(oldest);
321
+ await this.runLocked({
322
+ spec,
323
+ queue,
324
+ setResult: (r) => {
325
+ resultValue = r;
326
+ const pcs = internals?.inheritedGate?.parentConstraints;
327
+ if (resume !== undefined)
328
+ this.locallyClaimedTokens.delete(resume.cp.token);
329
+ const rToken = r.terminal.kind === "paused" ? r.terminal.token : undefined;
330
+ if (resume !== undefined && rToken !== resume.cp.token) {
331
+ this.parentConstraintRegistry.delete(resume.cp.token);
332
+ this.suspendedEnvReaps.delete(resume.cp.token);
347
333
  }
348
- this.parentConstraintRegistry.set(r.terminal.token, pcs);
349
- }
350
- }, (p) => {
351
- suggestionsDone = p;
352
- }, (h) => {
353
- handle = h;
354
- publishReady(h);
355
- }, (s) => {
356
- reapHandle = s;
357
- this.suspendedEnvReaps.set(s.token, s);
358
- }, manualCompactRef, taskIdRef, resume, { ...(internals ?? {}), detachHub }, notifyRef, captureOptOutRef, entryActor, entryTracer);
334
+ if (r.terminal.kind === "paused" && PAUSE_REGISTRY[r.terminal.gate.kind].taskStatus === "suspended" && pcs !== undefined && pcs.length > 0) {
335
+ if (this.parentConstraintRegistry.size >= Runner.PARENT_CONSTRAINT_REGISTRY_CAP) {
336
+ const oldest = this.parentConstraintRegistry.keys().next().value;
337
+ if (oldest !== undefined)
338
+ this.parentConstraintRegistry.delete(oldest);
339
+ }
340
+ this.parentConstraintRegistry.set(r.terminal.token, pcs);
341
+ }
342
+ },
343
+ onSuggestions: (p) => {
344
+ suggestionsDone = p;
345
+ },
346
+ onReady: (h) => {
347
+ handle = h;
348
+ publishReady(h);
349
+ },
350
+ onSuspend: (s) => {
351
+ reapHandle = s;
352
+ this.suspendedEnvReaps.set(s.token, s);
353
+ },
354
+ manualCompactRef,
355
+ taskIdRef,
356
+ resume,
357
+ internals: { ...(internals ?? {}), detachHub },
358
+ notifyRef,
359
+ captureOptOutRef,
360
+ entryActor,
361
+ entryTracer,
362
+ });
359
363
  }
360
364
  finally {
361
365
  releaseLock?.();
@@ -410,692 +414,37 @@ export class Runner {
410
414
  destroy,
411
415
  };
412
416
  }
413
- async runLocked(spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, entryActor, entryTracer) {
414
- const prepareResume = resume
415
- ? {
416
- leafId: resume.cp.leafId,
417
- suspendedBatch: new Set(resume.cp.pendingAction.kind === "tool_approval" ? resume.cp.pendingAction.batchToolCallIds : []),
418
- seed: resume.cp.state,
419
- priorSuspendCount: resume.cp.suspendCount,
420
- priorLedger: resume.cp.resourceLedger,
421
- priorHumanReview: resume.cp.humanReview,
422
- workspaceHandle: resume.cp.state.workspaceHandle,
423
- executesApprovedAction: resume.cp.pendingAction.kind === "tool_approval" &&
424
- resume.outcome.gate !== "wake" &&
425
- resume.outcome.decision === "allow",
426
- ...(resume.outcome.gate !== "wake" && resume.outcome.decision === "allow" && pendingContentAskCallId(resume.cp) !== undefined
427
- ? {
428
- redeemedContentAskCallId: pendingContentAskCallId(resume.cp),
429
- redeemedContentAskQuestionsHash: boundInputHashOf(resume.cp.pendingAction.args?.questions),
430
- }
431
- : {}),
432
- }
433
- : undefined;
434
- const taskNotificationQueue = new SystemInjectionQueue();
435
- const detachHub = internals?.detachHub ?? new ToolDetachHub();
436
- let notificationHarness;
437
- let notificationIdent = () => ({});
438
- let notificationLaneLive = true;
439
- let notificationSessionId;
440
- let descendantAnchorDisclosed = false;
441
- const parkTaskNotification = (payload, priority) => {
442
- if (notificationSessionId !== undefined)
443
- this.pendingSessionNotifications.pend(notificationSessionId, payload, priority);
444
- if (!isDelegatedAgentTerminal(payload))
445
- return;
446
- const uplink = internals?.parentNotify;
447
- if (uplink !== undefined) {
448
- try {
449
- uplink(payload, { priority });
450
- }
451
- catch (e) {
452
- this.deps.onError?.(e, { phase: "degraded", sessionId: notificationSessionId ?? "", classification: "descendant-terminal-uplink" });
453
- }
454
- return;
455
- }
456
- const rootAnchor = internals?.rootSessionId;
457
- if (rootAnchor !== undefined && rootAnchor !== notificationSessionId) {
458
- this.pendingSessionNotifications.pend(rootAnchor, payload, priority);
459
- return;
460
- }
461
- if (internals?.parentSessionId !== undefined && !descendantAnchorDisclosed) {
462
- descendantAnchorDisclosed = true;
463
- this.deps.onError?.(new Error(`background-agent terminal ${payload.task_id} parked on this run's own session — the delegation tree's root session is not reachable from this run (no uplink, no root anchor)`), { phase: "degraded", sessionId: notificationSessionId ?? "", classification: "descendant-terminal-unrouted" });
464
- }
465
- };
466
- const unsubscribeTaskNotifications = taskNotificationQueue.subscribe((item) => {
467
- queue.push({ type: "task_notification", notification: item.payload, priority: item.priority, ...notificationIdent() });
468
- if (notificationHarness && prepared.batchHaltRef.current === undefined) {
469
- const xml = renderTaskNotificationXml(item.payload);
470
- const noteOptions = {
471
- provenance: "engine-note",
472
- enginePayload: item.payload,
473
- ...(item.payload.task_type !== "external" && isTerminalTaskNotification(item.payload) ? { capPreferred: true } : {}),
474
- };
475
- const deliver = item.priority === "later"
476
- ? notificationHarness.followUp(xml, noteOptions)
477
- : notificationHarness.steer(xml, { ...noteOptions, ...(item.priority === "now" ? { immediate: true } : {}) });
478
- void deliver.then(() => item.onDisposition?.("queued"), () => {
479
- parkTaskNotification(item.payload, item.priority);
480
- item.onDisposition?.("parked");
481
- });
482
- }
483
- else {
484
- parkTaskNotification(item.payload, item.priority);
485
- item.onDisposition?.("parked");
486
- }
417
+ runLocked(input) {
418
+ const { spec, queue, resume, internals, taskIdRef } = input;
419
+ return runNotificationLane({
420
+ spec, queue, resume, internals, taskIdRef,
421
+ runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, pendingSessionNotifications: this.pendingSessionNotifications,
422
+ prepareTask,
423
+ next: (lane) => this.runSeatLanes(input, lane),
487
424
  });
488
- const upstreamTaskNotification = internals?.onTaskNotification;
489
- const deliveredAtTurnOpen = new Set();
490
- const injectTaskNotification = (notification, opts) => {
491
- if (deliveredAtTurnOpen.has(taskNotificationDedupKey(notification)))
492
- return Promise.resolve("dropped_duplicate");
493
- if (!notificationLaneLive) {
494
- parkTaskNotification(notification, opts?.priority ?? "later");
495
- return Promise.resolve("parked");
496
- }
497
- return new Promise((resolve) => {
498
- const accepted = taskNotificationQueue.enqueue({
499
- kind: "task_notification",
500
- priority: opts?.priority ?? "later",
501
- dedupKey: taskNotificationDedupKey(notification),
502
- payload: notification,
503
- onDisposition: resolve,
504
- });
505
- if (!accepted)
506
- resolve("queued");
507
- });
508
- };
509
- const peerSelfRef = internals?.peerSelfRef ?? createPeerSelfRef(internals?.registryScope ?? spec.principal ?? "default");
510
- const peerInboundChainRef = internals?.peerInboundChainRef ?? createPeerInboundChainRef();
511
- const modelCatalog = this.deps.models;
512
- const prepared = await prepareTask(spec, this.deps, this.sessions, prepareResume, {
513
- ...(internals ?? {}),
514
- peerSelfRef,
515
- peerInboundChainRef,
516
- detachHub,
517
- sessionReadStates: this.sessionReadStates,
518
- autoModeBreakerLedger: this.autoModeBreakerLedger,
519
- onTaskNotification: (notification, opts) => {
520
- try {
521
- upstreamTaskNotification?.(notification, opts);
522
- }
523
- catch {
524
- }
525
- injectTaskNotification(notification, opts);
526
- },
527
- }, this, taskIdRef);
528
- notificationHarness = prepared.harness;
529
- notificationSessionId = prepared.sessionId;
530
- if (taskIdRef)
531
- taskIdRef.effectiveMemoryScopes = prepared.effectiveMemoryScopes;
532
- if (taskIdRef)
533
- taskIdRef.editedFiles = prepared.editedFilesSnapshot;
534
- const runSourceTaskId = spec.taskId ?? prepared.sessionId;
535
- const parentToolCallId = internals?.parentToolCallId;
536
- const ident = () => parentToolCallId !== undefined ? { eventId: uuidv7(), parentToolCallId, sourceTaskId: runSourceTaskId } : { eventId: uuidv7() };
537
- notificationIdent = ident;
538
- queue.push({ type: "wiring_manifest", manifest: prepared.wiringManifest, ...ident() });
539
- prepared.toolRosterDeltas.subscribe((delta) => queue.push({ type: "tool_roster_delta", delta, ...ident() }));
540
- const delegationLifecycleNotifier = createSafeNotifier({
541
- onError: (f) => console.warn(`[sema-core] ${f.site}: delegation-lifecycle observer threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
425
+ }
426
+ runSeatLanes(input, lane) {
427
+ const { spec, queue, onReady, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, entryTracer } = input;
428
+ const { prepared, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, modelCatalog, notificationLane } = lane;
429
+ const { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats, undrainedUserAtEnd } = runIdentityWiring({
430
+ spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef,
431
+ notificationLane, pendingSessionNotifications: this.pendingSessionNotifications, runner: this.depsSeat,
542
432
  });
543
- const emitDelegationLifecycle = (event) => {
544
- if (!prepared.hookIdentity.isDelegatedChild)
545
- return;
546
- if (this.deps.onDelegationLifecycle === undefined)
547
- return;
548
- deliverDelegationLifecycle(this.deps.onDelegationLifecycle, event, delegationLifecycleNotifier, "runtask.onDelegationLifecycle");
549
- };
550
- emitDelegationLifecycle({ phase: "spawn", identity: prepared.hookIdentity });
551
- if (taskIdRef !== undefined && prepared.hookIdentity.isDelegatedChild && this.deps.onDelegationLifecycle !== undefined) {
552
- taskIdRef.delegationTerminalOwed = prepared.hookIdentity;
553
- }
554
- manualCompactRef.emitMooted = (reason) => {
555
- queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "manual", reason, ...ident() });
556
- };
557
- prepared.harness.onUndrainedEngineNotes = (payloads) => {
558
- if (notificationSessionId === undefined)
559
- return;
560
- for (const p of payloads)
561
- this.pendingSessionNotifications.pend(notificationSessionId, p);
562
- };
563
- prepared.harness.engineInjectionsHeld = () => prepared.batchHaltRef.current !== undefined;
564
- let undrainedUserAtEnd;
565
- prepared.harness.onUndrainedUserInputs = (counts) => {
566
- if (prepared.pausedRef.current !== undefined) {
567
- undrainedUserAtEnd = counts;
568
- return;
569
- }
570
- for (const notice of undrainedUserInputNotices(counts, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
571
- deliverEngineNotice(this.deps.onNotice, notice);
572
- }
573
- };
574
- prepared.harness.onEngineNoteConsumed = (p) => {
575
- const peer = p?.peer;
576
- if (peer !== undefined && Array.isArray(peer.hopChain))
577
- peerInboundChainRef.current = [...peer.hopChain];
578
- };
579
- if (notifyRef)
580
- notifyRef.inject = injectTaskNotification;
581
- if (captureOptOutRef && prepared.memoryEngineSession?.captureOptOut !== undefined) {
582
- captureOptOutRef.flip = prepared.memoryEngineSession.captureOptOut.flip;
583
- }
584
- try {
585
- internals?.onNotifyInjectorReady?.(injectTaskNotification);
586
- }
587
- catch {
588
- }
589
- {
590
- const pendingIdle = this.pendingSessionNotifications.drain(prepared.sessionId);
591
- if (pendingIdle !== undefined) {
592
- for (const payload of discloseDroppedPending(pendingIdle)) {
593
- const parkedPriority = pendingIdle.priorities?.get(payload);
594
- deliveredAtTurnOpen.add(taskNotificationDedupKey(payload));
595
- queue.push({ type: "task_notification", notification: payload, ...(parkedPriority !== undefined ? { priority: parkedPriority } : {}), ...ident() });
596
- void prepared.harness.nextTurn(renderTaskNotificationXml(payload), { provenance: "engine-note", enginePayload: payload }).catch(() => {
597
- this.pendingSessionNotifications.pend(prepared.sessionId, payload, parkedPriority);
598
- });
599
- }
600
- }
601
- }
602
- const loopLatch = { ended: false, userInterrupted: false, userHalted: false };
603
- onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId, runId: prepared.runId, hookTimeoutMs: prepared.hookTimeoutMs, hookIdentity: prepared.hookIdentity });
604
- const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
605
- prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
606
- if (resume &&
607
- resume.cp.suspendedAt !== undefined &&
608
- (resume.cp.gate.kind === "human" ||
609
- resume.cp.gate.kind === "irreversible_ask" ||
610
- resume.cp.gate.kind === "needs_review" ||
611
- resume.cp.gate.kind === "plan_review")) {
612
- const waitMs = Math.max(0, prepared.now() - resume.cp.suspendedAt);
613
- const decision = resume.outcome.gate === "wake" ? undefined : resume.outcome.decision;
614
- prepared.humanReviewRef.count += 1;
615
- prepared.humanReviewRef.totalWaitMs += waitMs;
616
- const gateToolName = "toolName" in resume.cp.gate ? resume.cp.gate.toolName : undefined;
617
- prepared.humanReviewRef.gates.push({ kind: resume.cp.gate.kind, waitMs, ...(decision !== undefined ? { decision } : {}), ...(gateToolName !== undefined ? { toolName: gateToolName } : {}) });
618
- }
619
- if (resume !== undefined && resume.outcome.gate === "plan_review" && resume.outcome.decision === "reject") {
620
- prepared.planModeRef.active = true;
621
- }
622
433
  const rs = createRunState();
623
- rs.telemetry.cacheFamily = cacheFamilyOf(prepared.model);
624
- rs.telemetry.pricing = this.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
625
- rs.telemetry.pricingConfigured = isModelPriced(prepared.model, this.deps.pricing);
626
- if (spec.limits?.degrade) {
627
- try {
628
- rs.degrade.degradeToModel = resolveModel(spec.limits.degrade.to, modelCatalog);
629
- }
630
- catch (e) {
631
- this.deps.onError?.(e, { phase: "degraded", sessionId: prepared.sessionId });
632
- }
633
- }
634
- const famOverride = prepared.model.params?.promptCacheFamily;
635
- if (famOverride && !["input-includes-cached", "input-excludes-cached", "openai", "anthropic"].includes(famOverride)) {
636
- this.deps.onError?.(new Error(`prompt-cache: unrecognized model.params.promptCacheFamily "${famOverride}" — ignored (fell back to model.api inference). Use "input-includes-cached" | "input-excludes-cached" (aliases "openai" | "anthropic").`), { phase: "prompt-cache", sessionId: prepared.sessionId });
637
- }
638
- rs.limits.turnsExceeded = false;
639
- rs.degrade.outputErrorStreak = 0;
640
- rs.degrade.outputInvalid = false;
641
- rs.telemetry.cacheBreakReported = false;
642
- rs.limits.outputRetryCap = resolveOutputRetries(spec.outputRetries);
643
- rs.limits.effectiveMaxTurns = resolveMaxTurns(spec.limits);
644
- rs.telemetry.tracer = entryTracer !== undefined ? entryTracer.tracer : (spec.tracer ?? this.deps.tracer);
645
- rs.telemetry.taskId = runSourceTaskId;
646
- rs.telemetry.runId = prepared.runId;
647
- if (taskIdRef) {
648
- taskIdRef.current = rs.telemetry.taskId;
649
- taskIdRef.sessionId = prepared.sessionId;
650
- }
651
- rs.telemetry.taskStart = Date.now();
652
- rs.telemetry.taskStartMonotonic = performance.now();
653
- const discloseNoteTaskRunFailure = (err) => {
654
- try {
655
- this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId: prepared.sessionId });
656
- }
657
- catch {
658
- }
659
- };
660
- try {
661
- void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId, rs.telemetry.runId)).catch(discloseNoteTaskRunFailure);
662
- }
663
- catch (err) {
664
- discloseNoteTaskRunFailure(err);
665
- }
666
- const noteUnevaluablePriceTable = (p) => {
667
- if (prepared.usageGovernance?.governsCost !== true)
668
- return;
669
- if (malformedPricingField(p) !== undefined)
670
- rs.telemetry.unpricedSpend = true;
671
- };
672
- rs.degrade.recordDegraded = (info, toModel) => {
673
- if (rs.degrade.degraded !== undefined)
674
- return;
675
- let m = toModel;
676
- if (!m) {
677
- try {
678
- m = resolveModel(info.to, modelCatalog);
679
- }
680
- catch {
681
- m = undefined;
682
- }
683
- }
684
- if (m) {
685
- rs.telemetry.pricing = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
686
- rs.telemetry.pricingConfigured = isModelPriced(m, this.deps.pricing);
687
- noteUnevaluablePriceTable(rs.telemetry.pricing);
688
- rs.telemetry.cacheFamily = cacheFamilyOf(m);
689
- }
690
- else {
691
- if (this.deps.pricing?.[info.to]) {
692
- rs.telemetry.pricing = this.deps.pricing[info.to];
693
- rs.telemetry.pricingConfigured = true;
694
- noteUnevaluablePriceTable(rs.telemetry.pricing);
695
- }
696
- else {
697
- rs.telemetry.pricingConfigured = false;
698
- }
699
- rs.telemetry.cacheFamily = "input-excludes-cached";
700
- }
701
- rs.degrade.degraded = info;
702
- emitTrace(rs.telemetry.tracer, () => ({ kind: "task.degraded", version: 1, taskId: rs.telemetry.taskId, from: info.from, to: info.to, reason: info.reason, atTurn: info.atTurn, ts: Date.now() }));
703
- try {
704
- this.deps.onError?.(new Error(`degraded to a cheaper model: ${info.from} → ${info.to} (${info.reason}) at turn ${info.atTurn}`), { phase: "degraded", sessionId: prepared.sessionId });
705
- }
706
- catch {
707
- }
708
- };
709
- const sliceWindowMicroUsd = spec.limits?.maxCostUsd !== undefined ? Math.round(spec.limits.maxCostUsd * 1e6) : undefined;
710
- rs.budget.remainingMicroUsd = prepared.resourceLedger
711
- ? remainingBudgetMicroUsd(prepared.resourceLedger)
712
- : spec.resourceSuspend?.totalBudgetUsd !== undefined
713
- ? Math.round(spec.resourceSuspend.totalBudgetUsd * 1e6)
714
- : undefined;
715
- rs.budget.maxCostMicroUsd =
716
- sliceWindowMicroUsd !== undefined && rs.budget.remainingMicroUsd !== undefined
717
- ? Math.min(sliceWindowMicroUsd, rs.budget.remainingMicroUsd)
718
- : (sliceWindowMicroUsd ?? rs.budget.remainingMicroUsd);
719
- rs.budget.remainingTokens = prepared.resourceLedger
720
- ? remainingTokens(prepared.resourceLedger)
721
- : spec.resourceSuspend?.totalTokens;
722
- rs.budget.maxTokensWindow =
723
- spec.limits?.maxTokens !== undefined && rs.budget.remainingTokens !== undefined
724
- ? Math.min(spec.limits.maxTokens, rs.budget.remainingTokens)
725
- : (spec.limits?.maxTokens ?? rs.budget.remainingTokens);
726
- rs.budget.overBudget = () => {
727
- if (rs.budget.maxTokensWindow !== undefined && stats.tokens > rs.budget.maxTokensWindow)
728
- return "tokens";
729
- if (rs.budget.maxCostMicroUsd !== undefined && stats.costMicroUsd > rs.budget.maxCostMicroUsd)
730
- return "cost";
731
- return undefined;
732
- };
733
- rs.budget.streamCancel =
734
- (spec.limits?.budgetStreamCancel ?? rs.budget.maxCostMicroUsd !== undefined) && prepared.suspendForResource === undefined;
735
- rs.budget.callOutputChars = 0;
736
- rs.budget.lastStreamBudgetCheck = 0;
737
- rs.budget.projectedOverBudget = () => {
738
- const estOut = Math.ceil(rs.budget.callOutputChars / 4);
739
- if (rs.budget.maxTokensWindow !== undefined && stats.tokens + estOut > rs.budget.maxTokensWindow)
740
- return "tokens";
741
- if (rs.budget.maxCostMicroUsd !== undefined) {
742
- const estOutMicro = computeCostMicroUsd({ totalInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: estOut }, rs.telemetry.pricing);
743
- if (stats.costMicroUsd + estOutMicro > rs.budget.maxCostMicroUsd)
744
- return "cost";
745
- }
746
- return undefined;
747
- };
748
- rs.turn.turnUsageMissing = false;
749
- rs.counters.repetitionCuts = 0;
750
- rs.counters.repetitionSpared = 0;
751
- rs.counters.repetitionEvents = [];
752
- rs.counters.REPETITION_EVENTS_CAP = 20;
753
- rs.counters.preemptIgnoredReported = false;
754
- rs.counters.wroteThisRun = false;
755
- rs.counters.finalVerifyInjections = 0;
756
- rs.counters.groundingSignalPreR9 = false;
757
- rs.counters.groundingSignalPostR9 = false;
758
- rs.attach.attachmentsCfg = spec.attachments;
759
- rs.attach.agentListingOn = rs.attach.attachmentsCfg?.agentListing !== false && eventDefaultOn("agent_listing");
760
- rs.attach.skillsListingOn = rs.attach.attachmentsCfg?.skillsListing !== false && eventDefaultOn("skills_listing");
761
- const listingsLive = (rs.attach.agentListingOn && prepared.agentListing !== undefined) || (rs.attach.skillsListingOn && prepared.skillsListing !== undefined);
762
- const backgroundTasksLive = (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true;
763
- rs.attach.attachState = rs.attach.attachmentsCfg !== undefined || listingsLive || backgroundTasksLive ? createAttachmentState() : undefined;
764
- rs.attach.dateState = prepared.dateChange !== undefined ? { announcedDate: prepared.dateChange.legDate } : undefined;
765
- rs.attach.instrProbe = this.deps.probeInstructionSources;
766
- rs.attach.instrState =
767
- rs.attach.instrProbe !== undefined && prepared.instructionSources !== undefined && prepared.instructionSources.length > 0
768
- ? { lastAnnouncedHash: new Map(prepared.instructionSources.map((s) => [s.path, s.contentHash])) }
769
- : undefined;
770
- rs.attach.sizeGuidelineState =
771
- prepared.workflowSizeGuideline !== undefined
772
- ? { announcedGuideline: prepared.workflowSizeGuideline.legGuideline, current: prepared.workflowSizeGuideline.current }
773
- : undefined;
774
- rs.counters.cadenceTurns = 0;
775
- rs.turn.lastTurnHadToolCalls = false;
776
- if (rs.attach.attachState !== undefined && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
777
- try {
778
- const branch = await prepared.session.getBranch();
779
- for (let i = branch.length - 1; i >= 0; i--) {
780
- const e = branch[i];
781
- if (e.type === "compaction") {
782
- rs.attach.attachState.postCompactPending = true;
783
- break;
784
- }
785
- if (e.type === "message" && e.message.role === "assistant")
786
- break;
787
- }
788
- }
789
- catch {
790
- }
791
- }
792
- if (rs.attach.attachState !== undefined &&
793
- ((rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) ||
794
- (rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true))) {
795
- const cpListings = resume?.cp.state.announcedListings;
796
- let entryListings;
797
- try {
798
- entryListings = await prepared.session.getAnnouncedListing();
799
- }
800
- catch {
801
- entryListings = undefined;
802
- }
803
- const textOf = (m) => {
804
- const c = m.content;
805
- if (typeof c === "string")
806
- return c;
807
- if (Array.isArray(c)) {
808
- return c
809
- .map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : ""))
810
- .join("\n");
811
- }
812
- return "";
813
- };
814
- let branchTexts = null;
815
- const branchLoad = async () => {
816
- if (branchTexts === null) {
817
- const branch = await prepared.session.getBranch();
818
- branchTexts = branch.flatMap((e) => {
819
- if (e.type !== "message")
820
- return [];
821
- const m = e.message;
822
- if (m.role !== "user")
823
- return [];
824
- const full = textOf(e.message);
825
- if (m.engineMinted === true)
826
- return [stripGitStatusUnits(full, prepared.reminderMark)];
827
- if (Array.isArray(m.engineSegments) && m.engineSegments.length > 0) {
828
- return m.engineSegments.map((s) => stripGitStatusUnits(full.slice(Math.max(0, s.start), Math.max(0, s.end)), prepared.reminderMark));
829
- }
830
- if (typeof m.enginePrefixChars === "number" && m.enginePrefixChars > 0)
831
- return [stripGitStatusUnits(full.slice(0, m.enginePrefixChars), prepared.reminderMark)];
832
- return [];
833
- });
834
- }
835
- return branchTexts;
836
- };
837
- const branchReplay = async (headers) => {
838
- try {
839
- return replayAnnouncedListing(await branchLoad(), headers);
840
- }
841
- catch {
842
- return undefined;
843
- }
844
- };
845
- if (rs.attach.agentListingOn && prepared.agentListing?.seedAnnounced === true) {
846
- const listing = prepared.agentListing;
847
- if (cpListings?.agents === undefined && entryListings?.agents !== undefined) {
848
- const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
849
- rs.attach.attachState.announcedAgentTypes = new Map(entryListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
850
- prepared.announcedListingsRef.agents = [...entryListings.agents];
851
- if (entryListings.models !== undefined) {
852
- rs.attach.attachState.announcedModels = [...entryListings.models];
853
- prepared.announcedListingsRef.models = [...entryListings.models];
854
- }
855
- }
856
- else if (cpListings?.agents !== undefined) {
857
- const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
858
- rs.attach.attachState.announcedAgentTypes = new Map(cpListings.agents.map((n) => [n, descOf.get(n) ?? ""]));
859
- prepared.announcedListingsRef.agents = [...cpListings.agents];
860
- if (cpListings.models !== undefined) {
861
- rs.attach.attachState.announcedModels = [...cpListings.models];
862
- prepared.announcedListingsRef.models = [...cpListings.models];
863
- }
864
- }
865
- else {
866
- const replayed = await branchReplay({
867
- initial: agentListingInitialHeader(listing.toolName),
868
- delta: agentListingDeltaHeader(listing.toolName),
869
- removed: AGENT_LISTING_REMOVED_HEADER,
870
- });
871
- if (replayed !== undefined) {
872
- const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
873
- rs.attach.attachState.announcedAgentTypes = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
874
- prepared.announcedListingsRef.agents = [...replayed];
875
- const replayedModels = replayAnnouncedModels(await branchLoad());
876
- if (replayedModels !== undefined) {
877
- rs.attach.attachState.announcedModels = [...replayedModels];
878
- prepared.announcedListingsRef.models = [...replayedModels];
879
- }
880
- }
881
- }
882
- }
883
- if (rs.attach.skillsListingOn && prepared.skillsListing?.seedAnnounced === true) {
884
- const listing = prepared.skillsListing;
885
- if (cpListings?.skills === undefined && entryListings?.skills !== undefined) {
886
- const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
887
- rs.attach.attachState.announcedSkills = new Map(entryListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
888
- prepared.announcedListingsRef.skills = [...entryListings.skills];
889
- }
890
- else if (cpListings?.skills !== undefined) {
891
- const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
892
- rs.attach.attachState.announcedSkills = new Map(cpListings.skills.map((n) => [n, descOf.get(n) ?? ""]));
893
- prepared.announcedListingsRef.skills = [...cpListings.skills];
894
- }
895
- else {
896
- const replayed = await branchReplay({
897
- initial: SKILLS_LISTING_PROBE_HEADER,
898
- delta: SKILLS_LISTING_DELTA_HEADER,
899
- removed: SKILLS_LISTING_REMOVED_HEADER,
900
- });
901
- if (replayed !== undefined) {
902
- const descOf = new Map(listing.entries.map((e) => [e.name, e.description]));
903
- rs.attach.attachState.announcedSkills = new Map([...replayed].map((n) => [n, descOf.get(n) ?? ""]));
904
- prepared.announcedListingsRef.skills = [...replayed];
905
- }
906
- }
907
- }
908
- }
909
- if (spec.finalVerification === true && resume !== undefined) {
910
- try {
911
- const branch = await prepared.session.getBranch();
912
- for (const e of branch) {
913
- if (e.type !== "message")
914
- continue;
915
- const m = e.message;
916
- if (m.role === "user") {
917
- const text = typeof m.content === "string"
918
- ? m.content
919
- : m.content.map((b) => (b !== null && typeof b === "object" && b.type === "text" ? String(b.text ?? "") : "")).join("\n");
920
- const fvMarked = m.engineMinted === true && text.includes(`<system-reminder mark="${prepared.reminderMark}">[final verification]`);
921
- if ((fvMarked || text.includes("<system-reminder>[final verification]")) && rs.counters.finalVerifyInjections < 2)
922
- rs.counters.finalVerifyInjections += 1;
923
- }
924
- else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
925
- rs.counters.wroteThisRun = true;
926
- }
927
- }
928
- }
929
- catch {
930
- }
931
- }
932
- rs.attach.attachmentsInjected = 0;
933
- const todoToolMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => t.name === "TodoWrite");
934
- const taskToolsMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => t.name === "TaskCreate");
935
- const writeFamilyByName = new Map();
936
- if (rs.attach.attachState !== undefined) {
937
- for (const t of prepared.tools) {
938
- const family = writeFamilyOfCanonical(t.name);
939
- if (family !== undefined)
940
- for (const n of [t.name, ...(t.aliases ?? [])])
941
- writeFamilyByName.set(n, family);
942
- }
943
- }
944
- const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(name);
945
- const toolStartAt = new Map();
946
- const startedToolCallIds = new Set();
947
- emitTrace(rs.telemetry.tracer, () => ({ kind: "task.start", version: 1, taskId: rs.telemetry.taskId, runId: rs.telemetry.runId, model: prepared.model.id, engineVersion: engineVersion(), ts: rs.telemetry.taskStart }));
948
- emitTrace(rs.telemetry.tracer, () => ({
949
- kind: "prompt.assembled",
950
- version: 1,
951
- taskId: rs.telemetry.taskId,
952
- constitution: prepared.promptManifest.constitution,
953
- blocks: prepared.promptManifest.blocks,
954
- ...(prepared.promptManifest.sections ? { sections: prepared.promptManifest.sections } : {}),
955
- ...(prepared.promptManifest.tools ? { tools: prepared.promptManifest.tools } : {}),
956
- ...(prepared.promptManifest.snapshot ? { snapshot: prepared.promptManifest.snapshot } : {}),
957
- ...(prepared.promptManifest.lowering ? { lowering: prepared.promptManifest.lowering } : {}),
958
- ...(prepared.promptManifest.toolDisclosure ? { toolDisclosure: prepared.promptManifest.toolDisclosure } : {}),
959
- totalChars: prepared.promptManifest.blocks.reduce((n, b) => n + b.chars, 0),
960
- ts: rs.telemetry.taskStart,
961
- }));
962
- emitTrace(rs.telemetry.tracer, () => {
963
- const reasons = declarationReasons(spec.configOverrides);
964
- return {
965
- kind: "config.assembled",
966
- version: 1,
967
- taskId: rs.telemetry.taskId,
968
- catalogVersion: CONFIG_CATALOG_VERSION,
969
- fields: resolveEffectiveConfig(spec, { modelMaxTokens: prepared.model.maxTokens }),
970
- ...(Object.keys(reasons).length > 0 ? { overrideReasons: reasons } : {}),
971
- ts: rs.telemetry.taskStart,
972
- };
434
+ const { noteUnevaluablePriceTable } = runTelemetryAndBudgetSeats({ spec, prepared, resume, stats, taskIdRef, entryTracer, modelCatalog, runSourceTaskId, rs, runner: this.depsSeat, sessions: this.sessions });
435
+ return runAttachmentSeats({
436
+ spec, prepared, resume, rs, runner: this.depsSeat,
437
+ next: () => this.runAssembliesAndLegs(input, lane, { rs, stats, ident, parentToolCallId, emitDelegationLifecycle, loopLatch, undrainedUserAtEnd, noteUnevaluablePriceTable }),
973
438
  });
974
- if (prepared.turnSnapshot !== undefined) {
975
- prepared.turnSnapshot.onChange = (snap) => emitTrace(rs.telemetry.tracer, () => ({
976
- kind: "prompt.snapshot_changed",
977
- version: 1,
978
- taskId: rs.telemetry.taskId,
979
- snapshot: { cacheIdentity: snap.cacheIdentity, elements: { ...snap.elements } },
980
- ts: Date.now(),
981
- }));
982
- }
983
- let reasoningResolution = prepared.thinking && prepared.thinking !== "off" ? resolveReasoning(prepared.thinking, prepared.model) : undefined;
984
- const publishReasoningResolution = (r) => {
985
- reasoningResolution = r;
986
- if (taskIdRef)
987
- taskIdRef.effectiveReasoning = r;
988
- emitTrace(rs.telemetry.tracer, () => ({
989
- kind: "reasoning.resolved",
990
- version: 1,
991
- taskId: rs.telemetry.taskId,
992
- model: prepared.model.id,
993
- requested: r.requested,
994
- effective: r.effective,
995
- graded: r.graded,
996
- clamped: r.clamped,
997
- format: r.format,
998
- endpoint: r.endpoint,
999
- ...(r.dropped === true ? { dropped: true } : {}),
1000
- ts: Date.now(),
1001
- }));
1002
- };
1003
- if (reasoningResolution !== undefined)
1004
- publishReasoningResolution(reasoningResolution);
1005
- let reasoningFactsConsumed = false;
1006
- const observeReasoningWireFacts = (facts) => {
1007
- if (reasoningFactsConsumed)
1008
- return;
1009
- reasoningFactsConsumed = true;
1010
- if (prepared.thinking === undefined || prepared.thinking === "off")
1011
- return;
1012
- const next = resolveReasoning(prepared.thinking, prepared.model, facts);
1013
- const current = reasoningResolution;
1014
- if (current !== undefined &&
1015
- current.effective === next.effective &&
1016
- current.graded === next.graded &&
1017
- current.clamped === next.clamped &&
1018
- current.format === next.format &&
1019
- current.endpoint === next.endpoint &&
1020
- current.dropped === next.dropped) {
1021
- return;
1022
- }
1023
- publishReasoningResolution(next);
1024
- };
1025
- const effectiveTimeoutMs = spec.limits?.maxWalltimeMs;
1026
- const walltimeMonotonicDeadline = effectiveTimeoutMs !== undefined ? rs.telemetry.taskStartMonotonic + effectiveTimeoutMs : undefined;
1027
- rs.counters.walltimeSyncBackstopFired = false;
1028
- const timeout = startTimeout(prepared.harness, prepared.abortController, walltimeMonotonicDeadline !== undefined ? walltimeMonotonicDeadline - performance.now() : undefined, prepared.suspendForResource !== undefined);
1029
- const pushContent = (e) => {
1030
- queue.push(e);
1031
- if (parentToolCallId !== undefined && internals?.onForwardEvent) {
1032
- try {
1033
- internals.onForwardEvent(e);
1034
- }
1035
- catch {
1036
- }
1037
- }
1038
- };
1039
- const ownCommittedTailRef = { current: undefined };
1040
- const emitCommitted = (entryId, role, toolCallId) => {
1041
- ownCommittedTailRef.current = entryId;
1042
- if (role === "toolResult" && toolCallId !== undefined)
1043
- startedToolCallIds.delete(toolCallId);
1044
- queue.push({ type: "message_committed", entryId, role, ...(toolCallId !== undefined ? { toolCallId } : {}), ...ident() });
1045
- };
1046
- const subagentName = parentToolCallId !== undefined && internals?.agentName !== undefined ? inlineUntrusted(internals.agentName.slice(0, 320), 80) : undefined;
1047
- const statusSinkNotifier = createSafeNotifier({
1048
- onError: (f) => console.warn(`[sema-core] ${f.site}: run-internals status sink threw (contained; further failures counted, not re-disclosed): ${f.error.message}`),
1049
- });
1050
- const statusEmit = (s) => {
1051
- const frame = {
1052
- type: "status",
1053
- phase: s.phase,
1054
- ...(s.detail !== undefined ? { detail: s.detail } : {}),
1055
- ...(s.retryInSec !== undefined ? { retryInSec: s.retryInSec } : {}),
1056
- ...(s.retryInMs !== undefined ? { retryInMs: s.retryInMs } : {}),
1057
- ...(s.retryAtMs !== undefined ? { retryAtMs: s.retryAtMs } : {}),
1058
- ...(s.elapsedMs !== undefined ? { elapsedMs: s.elapsedMs } : {}),
1059
- ...(s.timeoutMs !== undefined ? { timeoutMs: s.timeoutMs } : {}),
1060
- ...(s.attempt !== undefined ? { attempt: s.attempt } : {}),
1061
- ...(s.maxRetries !== undefined ? { maxRetries: s.maxRetries } : {}),
1062
- ...(s.errClass !== undefined ? { errClass: s.errClass } : {}),
1063
- ...(s.errorStatus !== undefined ? { errorStatus: s.errorStatus } : {}),
1064
- ...ident(),
1065
- };
1066
- Object.freeze(frame);
1067
- const accepted = queue.push(frame);
1068
- if (accepted && internals?.onStatusEvent !== undefined) {
1069
- statusSinkNotifier.notify(() => observeThenableRejection(internals.onStatusEvent?.(frame), statusSinkNotifier, "runtask.onStatusEvent"), "runtask.onStatusEvent");
1070
- }
1071
- };
1072
- const telemetryEmit = (t) => {
1073
- emitTrace(rs.telemetry.tracer, () => t.kind === "failover"
1074
- ? {
1075
- kind: "brain.failover",
1076
- version: 1,
1077
- taskId: rs.telemetry.taskId,
1078
- servedIndex: t.servedIndex,
1079
- total: t.total,
1080
- ...(t.errorCode !== undefined ? { errorCode: t.errorCode } : {}),
1081
- ts: Date.now(),
1082
- }
1083
- : t.kind === "breaker"
1084
- ? { kind: "breaker.transition", version: 1, taskId: rs.telemetry.taskId, key: t.key, phase: t.phase, failures: t.failures, ts: Date.now() }
1085
- : t.kind === "retry"
1086
- ? {
1087
- kind: "brain.retry",
1088
- version: 1,
1089
- taskId: rs.telemetry.taskId,
1090
- attempt: t.attempt,
1091
- phase: t.phase,
1092
- ...(t.errClass !== undefined ? { errClass: t.errClass } : {}),
1093
- ...(t.nextDelayMs !== undefined ? { nextDelayMs: t.nextDelayMs } : {}),
1094
- ts: Date.now(),
1095
- }
1096
- : { kind: "vision.placeholder", version: 1, taskId: rs.telemetry.taskId, count: t.count, ts: Date.now() });
1097
- };
1098
- const withBrainSinks = (fn) => runWithStatusSink(statusEmit, () => runWithBrainTelemetry(telemetryEmit, () => runWithReasoningWireFacts(observeReasoningWireFacts, fn)));
439
+ }
440
+ runAssembliesAndLegs(input, lane, seats) {
441
+ const { spec, queue, setResult, onSuggestions, onSuspend, manualCompactRef, taskIdRef, resume, internals, entryActor } = input;
442
+ const { prepared, notificationLane, unsubscribeTaskNotifications } = lane;
443
+ const { rs, stats, ident, parentToolCallId, emitDelegationLifecycle, loopLatch, undrainedUserAtEnd, noteUnevaluablePriceTable } = seats;
444
+ const { todoToolMounted, taskToolsMounted, writeFamilyOf, toolStartAt, startedToolCallIds } = runToolMountFacts({ spec, prepared, rs });
445
+ const { observeReasoningWireFacts, reasoningResolution } = runReasoningSeat({ prepared, rs, taskIdRef });
446
+ const { effectiveTimeoutMs, walltimeMonotonicDeadline, timeout, pushContent, ownCommittedTailRef, emitCommitted } = runClockAndContent({ spec, prepared, queue, internals, rs, ident, parentToolCallId, startedToolCallIds });
447
+ const { subagentName, withBrainSinks } = runBrainSinks({ queue, internals, rs, ident, parentToolCallId, observeReasoningWireFacts });
1099
448
  const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
1100
449
  for (const orphan of prepared.wakeRecovered) {
1101
450
  queue.push({ type: "tool_end", toolCallId: orphan.toolCallId, toolName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), isError: true, ...reconciledToolEndBody(orphan, prepared.structuredProjector), ...ident() });
@@ -1113,412 +462,16 @@ export class Runner {
1113
462
  runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
1114
463
  },
1115
464
  });
1116
- const recordCompactionUsage = (m, msg) => {
1117
- const u = msg?.usage;
1118
- if (!u)
1119
- return;
1120
- const fam = cacheFamilyOf(m);
1121
- const price = this.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
1122
- const priced = isModelPriced(m, this.deps.pricing);
1123
- if (!priced)
1124
- rs.telemetry.unpricedSpend = true;
1125
- noteUnevaluablePriceTable(price);
1126
- const { totalInputTokens, uncachedInputTokens, costMicroUsd } = usageCostMicroUsd(fam, u, price);
1127
- stats.tokens += u.totalTokens || 0;
1128
- stats.promptTokens += uncachedInputTokens;
1129
- stats.totalInputTokens += totalInputTokens;
1130
- stats.cachedTokens += u.cacheRead || 0;
1131
- stats.cacheWriteTokens += u.cacheWrite || 0;
1132
- stats.outputTokens += u.output || 0;
1133
- stats.costMicroUsd += costMicroUsd;
1134
- stats.compactionMicroUsd = (stats.compactionMicroUsd ?? 0) + costMicroUsd;
1135
- emitTrace(rs.telemetry.tracer, () => ({
1136
- kind: "brain.call", version: 1, taskId: rs.telemetry.taskId, model: m.id, provider: m.provider,
1137
- promptTokens: uncachedInputTokens, totalInputTokens, completionTokens: u.output || 0,
1138
- cacheRead: u.cacheRead || 0, cacheWrite: u.cacheWrite || 0,
1139
- latencyMs: 0, ...(priced ? { costMicroUsd } : {}), ...(typeof msg?.stopReason === "string" ? { stopReason: msg.stopReason } : {}), ts: Date.now(),
1140
- }));
1141
- };
1142
- const compactionBrain = {
1143
- stream: this.deps.brain.stream,
1144
- complete: async (m, c, o) => {
1145
- const msg = await runWithStatusSink(() => { }, async () => await runWithReasoningWireFacts(() => { }, async () => this.deps.brain.complete
1146
- ? await this.deps.brain.complete(m, c, o)
1147
- : await (await Promise.resolve(this.deps.brain.stream(m, c, o))).result()));
1148
- recordCompactionUsage(m, msg);
1149
- return msg;
1150
- },
1151
- };
1152
- const withinTaskCompaction = (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true);
1153
- const compactionBreaker = { failures: 0 };
1154
- if (spec.compaction?.enabled ?? true) {
1155
- const prefixWindow = resolveTriggerWindow(prepared.model).window;
1156
- if (Number.isFinite(prefixWindow) && prefixWindow > 0) {
1157
- const prefixSettings = sanitizeCompactionSettings({ ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction }, prefixWindow);
1158
- const prefixCompactAt = prefixWindow - prefixSettings.reserveTokens;
1159
- if (prepared.promptOverheadTokens >= prefixCompactAt) {
1160
- this.deps.onError?.(new Error(`compaction cannot help: the fixed request prefix (system prompt + tool schemas, ≈${prepared.promptOverheadTokens} tokens) ` +
1161
- `already meets or exceeds the compaction threshold (${prefixCompactAt} of a ${prefixWindow}-token window). ` +
1162
- `Compaction only shrinks conversation history, so this run will re-trigger or overflow regardless — ` +
1163
- `shrink the system prompt/tool surface or use a larger-window model.`), { phase: "config", sessionId: prepared.sessionId });
1164
- }
1165
- }
1166
- }
1167
- const windowSafetyOptions = (mainModel) => ({
1168
- ...(rs.budget.maxCostMicroUsd !== undefined
1169
- ? {
1170
- fallbackBudget: {
1171
- spentMicroUsd: () => stats.costMicroUsd,
1172
- capMicroUsd: rs.budget.maxCostMicroUsd,
1173
- mainInputPer1M: (this.deps.pricing?.[mainModel.id] ?? modelCostToPricing(mainModel.cost)).inputPer1M,
1174
- },
1175
- }
1176
- : {}),
1177
- onWindowSafety: (info) => {
1178
- if (info.kind === "fallback") {
1179
- emitTrace(rs.telemetry.tracer, () => ({
1180
- kind: "compaction.model_fallback",
1181
- version: 1,
1182
- taskId: rs.telemetry.taskId,
1183
- contentTokens: info.contentTokens,
1184
- headroomTokens: info.headroomTokens ?? 0,
1185
- truncationRatio: info.truncationRatio,
1186
- ...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
1187
- ts: Date.now(),
1188
- }));
1189
- }
1190
- else {
1191
- emitTrace(rs.telemetry.tracer, () => ({
1192
- kind: "compaction.clamp_disclosure",
1193
- version: 1,
1194
- taskId: rs.telemetry.taskId,
1195
- truncationRatio: info.truncationRatio,
1196
- reason: info.reason ?? "tolerance",
1197
- ...(info.estCostMicroUsd !== undefined ? { estCostMicroUsd: info.estCostMicroUsd } : {}),
1198
- ts: Date.now(),
1199
- }));
1200
- }
465
+ const { compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions } = runCompactionMachinery({ spec, prepared, rs, stats, noteUnevaluablePriceTable, runner: this.depsSeat });
466
+ runStopAndFinalVerify({ spec, prepared, queue, rs, stats, ident, walltimeMonotonicDeadline, runner: this.depsSeat });
467
+ runRecoveryLanes({
468
+ spec, prepared, queue, rs, ident, compactionBrain, compactionBreaker, windowSafetyOptions, runner: this.depsSeat,
469
+ compactionSeats: {
470
+ seamCCompactionOptions: (p) => this.seamCCompactionOptions(p),
471
+ compactionHookOptions: (s, sid, trig, identity, seatBound) => this.compactionHookOptions(s, sid, trig, identity, seatBound),
472
+ recordCompactionReuse: (p, c) => this.recordCompactionReuse(p, c),
1201
473
  },
1202
474
  });
1203
- const stopHook = (spec.hooks ?? this.deps.hooks)?.stop;
1204
- const finalVerificationOn = spec.finalVerification === true;
1205
- const finalVerifyBudgetFill = () => {
1206
- let worst = 0;
1207
- if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow > 0)
1208
- worst = Math.max(worst, stats.tokens / rs.budget.maxTokensWindow);
1209
- if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd > 0)
1210
- worst = Math.max(worst, stats.costMicroUsd / rs.budget.maxCostMicroUsd);
1211
- if (walltimeMonotonicDeadline !== undefined && prepared.suspendForResource === undefined) {
1212
- const windowMs = walltimeMonotonicDeadline - rs.telemetry.taskStartMonotonic;
1213
- if (windowMs > 0)
1214
- worst = Math.max(worst, (performance.now() - rs.telemetry.taskStartMonotonic) / windowMs);
1215
- }
1216
- return worst;
1217
- };
1218
- const emitFinalVerifyEcho = (body) => {
1219
- queue.push({ type: "steering_injected", source: "final_verification", preview: body.slice(0, 220), ...ident() });
1220
- };
1221
- if (stopHook || finalVerificationOn) {
1222
- let consecutiveBlocks = 0;
1223
- prepared.harness.setStopGate(async () => {
1224
- if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined || prepared.batchHaltRef.current !== undefined)
1225
- return [];
1226
- if (finalVerificationOn &&
1227
- (rs.counters.finalVerifyInjections === 0 || (rs.counters.finalVerifyInjections === 1 && rs.counters.groundingSignalPreR9 && !rs.counters.groundingSignalPostR9)) &&
1228
- rs.counters.wroteThisRun &&
1229
- prepared.outputRef.set !== true &&
1230
- !(rs.limits.effectiveMaxTurns !== undefined && rs.limits.effectiveMaxTurns > 0 && stats.turns >= rs.limits.effectiveMaxTurns - 1) &&
1231
- finalVerifyBudgetFill() < 0.9) {
1232
- rs.counters.finalVerifyInjections += 1;
1233
- if (rs.counters.finalVerifyInjections === 2) {
1234
- const reentryBody = openSystemReminder(prepared.reminderMark) +
1235
- "[final verification] Your tool calls in this run worked with raw bytes, structural parsing, " +
1236
- "or checksum/digest computation — the deliverable very likely embeds verifiable structure (structural fields, an " +
1237
- "embedded checksum-family value, reference data it must match, or a replayable deterministic path). You MUST " +
1238
- "execute the grounding check that structure supports — recompute the embedded value and compare it against the " +
1239
- "declared one, re-parse the structure from the raw bytes and reconcile it with your output, compare against the " +
1240
- "reference data, or replay the deterministic path — and REPORT the check's concrete result before finishing. " +
1241
- "A closing statement without a reported check result is not verification. If you already ran such a check, state " +
1242
- "its concrete result now; if the check mismatches, fix the deliverable first. This is the final reminder from " +
1243
- "this verification gate — it will not intervene again.</system-reminder>";
1244
- emitFinalVerifyEcho(reentryBody);
1245
- return [
1246
- {
1247
- role: "user",
1248
- engineMinted: true,
1249
- content: reentryBody,
1250
- timestamp: Date.now(),
1251
- },
1252
- ];
1253
- }
1254
- const nudgeBody = openSystemReminder(prepared.reminderMark) +
1255
- "[final verification] Before finishing: re-verify the FINAL deliverable through its REAL entry point, " +
1256
- "exactly as the acceptance criteria would exercise it — execute the binary/function/endpoint directly and read the ACTUAL " +
1257
- "output and exit code. Do NOT rely on earlier self-tests, shell redirections, or assumptions (a program that prints to " +
1258
- "stdout is not a program that writes the required file). If anything mismatches the task's requirements, fix it before " +
1259
- "finishing. " +
1260
- "Treat verification writes as state-harmless: when the deliverable itself is a persisted final " +
1261
- "state (for example a committed or pushed file, a deployed artifact, or a required output file), " +
1262
- "do not change that state merely to test it. This constrains HOW you verify — it is never a " +
1263
- "license to skip the real acceptance path or to check a substitute of your own making: expected " +
1264
- "values must come from the task's requirements, never from content you generated. If the real " +
1265
- "acceptance path requires a write, use disposable inputs or an isolated target, end in the exact " +
1266
- "required final state, and verify that final state before finishing. " +
1267
- "If the work relied on a third-party API, library, or model, check the usage contract the object itself declares " +
1268
- "(docstrings, metadata, configuration — e.g. prompt conventions shipped with a model) and confirm your calls follow " +
1269
- "it rather than a default symmetric usage. Verify not only that the deliverable EXISTS but that the METHOD that " +
1270
- "produced it matches the task's requirements. " +
1271
- "Choose the verification SURFACE deliberately: check against the reference data, oracle, or evaluation tooling the " +
1272
- "task itself provides — re-running your own implementation and getting the same answer is self-consistency, not " +
1273
- "correctness — and cross-check through an independent second path where the task or environment offers one " +
1274
- "(checksums, runtime artifacts). Verify the PERSISTED artifact — re-read what is actually on disk or committed, " +
1275
- "not in-memory state — against every hard constraint from the original task text (numeric bounds, allowed-value " +
1276
- "lists, naming semantics, required files), reconciling whole-set completeness: nothing missing, nothing duplicated. " +
1277
- "If the deliverable embeds verifiable structure — structural fields, an embedded checksum-family value, " +
1278
- "reference data it must match, or a replayable deterministic path — you MUST execute the grounding check " +
1279
- "that structure supports and REPORT its concrete result in your closing summary: for such a deliverable, " +
1280
- "no reported check result means the work is not finished. " +
1281
- "If the task produced neither an executable deliverable nor any verifiable structure or acceptance " +
1282
- "oracle to check against, briefly confirm completion and stop. " +
1283
- "Residue YOUR OWN testing created (scratch files, running processes, generated outputs the task does not ask for) " +
1284
- "is not protected state — if the task's required final state is a clean target, removing your own residue is part " +
1285
- "of delivering it. " +
1286
- "Verification must never LAUNDER uncertainty: if part of your conclusion was uncertain before this check, keep " +
1287
- "reporting it as uncertain unless the check you actually ran resolved it — a re-stated conclusion is not new " +
1288
- "evidence.</system-reminder>";
1289
- emitFinalVerifyEcho(nudgeBody);
1290
- return [
1291
- {
1292
- role: "user",
1293
- engineMinted: true,
1294
- content: nudgeBody,
1295
- timestamp: Date.now(),
1296
- },
1297
- ];
1298
- }
1299
- if (!stopHook)
1300
- return [];
1301
- let result;
1302
- try {
1303
- const stopSeat = await runHookSeat("stop", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => stopHook({
1304
- stopHookActive: consecutiveBlocks > 0,
1305
- consecutiveBlocks,
1306
- getBranch: () => prepared.session.getBranch(),
1307
- identity: prepared.hookIdentity,
1308
- signal: sig,
1309
- }));
1310
- if (stopSeat.expired) {
1311
- if (stopSeat.cause === "timeout") {
1312
- this.deps.onError?.(hookSeatExpiredError("stop", prepared.hookTimeoutMs, stopSeat.cause, "the run was allowed to END (the seat's own no-opinion answer); no pushback and no additional context were injected"), { phase: "hook", sessionId: prepared.sessionId });
1313
- }
1314
- consecutiveBlocks = 0;
1315
- return [];
1316
- }
1317
- result = stopSeat.value;
1318
- }
1319
- catch (err) {
1320
- this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
1321
- return [];
1322
- }
1323
- const messages = [];
1324
- if (result?.additionalContext) {
1325
- messages.push({
1326
- role: "user",
1327
- engineMinted: true,
1328
- content: formatHookFeedback(`Stop hook additional context: ${result.additionalContext}`, prepared.reminderMark),
1329
- timestamp: Date.now(),
1330
- });
1331
- }
1332
- if (messages.length === 0 && !result?.block) {
1333
- consecutiveBlocks = 0;
1334
- return messages;
1335
- }
1336
- consecutiveBlocks++;
1337
- const cap = STOP_HOOK_BLOCK_CAP;
1338
- if (consecutiveBlocks > cap) {
1339
- this.deps.onError?.(new Error(`a Stop hook kept the turn from ending ${consecutiveBlocks} consecutive times — overriding and ending the run. ` +
1340
- `Both a block and an additionalContext-only push-back count (CC 2.1.220). ` +
1341
- `Check ctx.stopHookActive in the hook and return success while it's true.`), { phase: "hook", sessionId: prepared.sessionId });
1342
- return [];
1343
- }
1344
- if (result?.block) {
1345
- messages.push({
1346
- role: "user",
1347
- engineMinted: true,
1348
- content: formatHookFeedback(`Stop hook stopped continuation: ${result.block}`, prepared.reminderMark),
1349
- timestamp: Date.now(),
1350
- });
1351
- }
1352
- return messages;
1353
- });
1354
- }
1355
- const runForcedCompactionPass = async (lane, turnSignal) => {
1356
- if (!(spec.compaction?.enabled ?? true))
1357
- return false;
1358
- if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES)
1359
- return false;
1360
- const passSignal = turnSignal !== undefined ? AbortSignal.any([prepared.abortController.signal, turnSignal]) : prepared.abortController.signal;
1361
- try {
1362
- const comp = await maybeCompact({
1363
- session: prepared.session,
1364
- epochDeclaredSections: prepared.epochDeclaredSections,
1365
- ...centerAdoptionOption(prepared),
1366
- model: prepared.harness.getModel(),
1367
- compactionModel: prepared.compModel,
1368
- ...forkContextOption(prepared, true),
1369
- brain: compactionBrain,
1370
- getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
1371
- thinking: prepared.thinking,
1372
- settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
1373
- customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
1374
- signal: passSignal,
1375
- minTokens: 0,
1376
- force: true,
1377
- overheadTokens: prepared.promptOverheadTokens,
1378
- ...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
1379
- onInputTruncated: emitInputTruncated(rs.telemetry.tracer, rs.telemetry.taskId),
1380
- workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
1381
- ...contextInstructionFilesOption(prepared),
1382
- ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
1383
- ...this.seamCCompactionOptions(prepared),
1384
- ...gitRestateOption(prepared),
1385
- ...windowSafetyOptions(prepared.harness.getModel()),
1386
- ...this.compactionHookOptions(spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: passSignal }),
1387
- });
1388
- if (comp.compacted) {
1389
- compactionBreaker.failures = 0;
1390
- this.recordCompactionReuse(prepared, comp);
1391
- if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
1392
- const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
1393
- rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
1394
- }
1395
- queue.push({
1396
- type: "compacted",
1397
- trigger: "forced",
1398
- tokensBefore: comp.tokensBefore ?? 0,
1399
- ...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
1400
- ...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
1401
- ...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
1402
- ...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
1403
- ...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
1404
- ...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
1405
- ...(comp.modelFallback ? { modelFallback: true } : {}),
1406
- ...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
1407
- ...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
1408
- ...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
1409
- ...ident(),
1410
- });
1411
- if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
1412
- const pd = comp.phaseDurations;
1413
- const pdDur = comp.durationMs;
1414
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
1415
- }
1416
- prepared.cacheBreakDetector?.notifyCompaction();
1417
- if (rs.attach.attachState !== undefined) {
1418
- rs.attach.attachState.postCompactPending = true;
1419
- rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
1420
- }
1421
- }
1422
- else {
1423
- if (comp.noop) {
1424
- compactionBreaker.failures = 0;
1425
- }
1426
- else {
1427
- compactionBreaker.failures += 1;
1428
- }
1429
- const declineReason = lane === "rejection" ? "prompt-too-long recovery pass did not land" : "guard-chain forced compaction pass did not land";
1430
- if (!comp.noop) {
1431
- this.deps.onError?.(new Error(declineReason), { phase: "compaction", sessionId: prepared.sessionId });
1432
- }
1433
- queue.push({
1434
- type: "compaction_outcome",
1435
- outcome: comp.noop ? "noop" : "failed",
1436
- trigger: "forced",
1437
- reason: declineReason,
1438
- ...ident(),
1439
- });
1440
- }
1441
- return comp.compacted === true;
1442
- }
1443
- catch (err) {
1444
- if (turnSignal?.aborted === true && !prepared.abortController.signal.aborted) {
1445
- const interruptReason = lane === "rejection"
1446
- ? "prompt-too-long recovery pass cut short by a turn interrupt"
1447
- : "guard-chain forced compaction cut short by a turn interrupt";
1448
- queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: interruptReason, ...ident() });
1449
- return false;
1450
- }
1451
- compactionBreaker.failures += 1;
1452
- this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "compaction", sessionId: prepared.sessionId });
1453
- const msg = String(err instanceof Error ? err.message : err);
1454
- queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
1455
- return false;
1456
- }
1457
- };
1458
- prepared.harness.setLoopRecovery({
1459
- truncatedOutput: {},
1460
- malformedToolUse: {},
1461
- thinkingOnly: {},
1462
- degenerateOutput: { detect: (m) => isDegenerateCutMessage(m) },
1463
- promptTooLong: {
1464
- recover: async (attempt, turnSignal) => {
1465
- if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
1466
- return false;
1467
- if (turnSignal?.aborted === true)
1468
- return false;
1469
- if (prepared.microCompact.clearOnRejection && attempt === 1) {
1470
- const proj = prepared.microCompact.projectionRef.current;
1471
- const compactionAvailable = (spec.compaction?.enabled ?? true) && compactionBreaker.failures < MAX_CONSECUTIVE_COMPACTION_FAILURES;
1472
- const declineArm = compactionAvailable ? "forced_compaction" : "none";
1473
- const plan = proj === undefined
1474
- ? { declined: "no_candidates" }
1475
- : planRejectionClears(proj.messages, {
1476
- keyOf: proj.keyOf,
1477
- ...(prepared.microCompact.offloadPersist ? { offload: { persist: prepared.microCompact.offloadPersist } } : {}),
1478
- });
1479
- if ("declined" in plan) {
1480
- emitTrace(rs.telemetry.tracer, () => ({
1481
- kind: "context.mc_null",
1482
- version: 1,
1483
- taskId: rs.telemetry.taskId,
1484
- reason: plan.declined,
1485
- nextArm: declineArm,
1486
- ts: Date.now(),
1487
- }));
1488
- }
1489
- else {
1490
- for (const e of plan.cleared) {
1491
- prepared.microCompact.ledger.entries.set(e.key, { marker: e.marker, groupCount: e.groupCount, fp: e.fp });
1492
- }
1493
- emitTrace(rs.telemetry.tracer, () => ({
1494
- kind: "context.mc_clear",
1495
- version: 1,
1496
- taskId: rs.telemetry.taskId,
1497
- clearedCount: plan.cleared.length,
1498
- tokensSavedEstimate: plan.tokensSavedEstimate,
1499
- trigger: "refusal",
1500
- ts: Date.now(),
1501
- }));
1502
- return true;
1503
- }
1504
- }
1505
- return runForcedCompactionPass("rejection", turnSignal);
1506
- },
1507
- },
1508
- });
1509
- prepared.microCompact.inTurnCompactionRef.current = async (turnSignal, anchoredEstimate) => {
1510
- if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
1511
- return false;
1512
- if (turnSignal?.aborted === true)
1513
- return false;
1514
- if (anchoredEstimate !== undefined && rs.counters.compactionFloor > 0 && anchoredEstimate < rs.counters.compactionFloor) {
1515
- const floor = rs.counters.compactionFloor;
1516
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.suppressed", version: 1, taskId: rs.telemetry.taskId, estTokens: anchoredEstimate, floor, ts: Date.now() }));
1517
- queue.push({ type: "compaction_outcome", outcome: "suppressed", trigger: "forced", ...ident() });
1518
- return false;
1519
- }
1520
- return runForcedCompactionPass("guard", turnSignal);
1521
- };
1522
475
  const rapidRefill = createRapidRefillState();
1523
476
  const drainManualCompact = (outcome) => {
1524
477
  manualCompactRef.requested = false;
@@ -1540,955 +493,25 @@ export class Runner {
1540
493
  },
1541
494
  },
1542
495
  });
1543
- prepared.gitStatusRef.reassert = async () => {
1544
- const ref = prepared.gitStatusRef;
1545
- const frame = ref.frame;
1546
- if (frame === undefined)
1547
- return;
1548
- delete ref.mirrorOwed;
1549
- const use = ref.overBudgetShrunk && frame.shrunk !== undefined
1550
- ? { kind: "degraded", body: frame.shrunk.body, hash: frame.shrunk.hash }
1551
- : { kind: frame.kind, body: frame.body, hash: frame.hash };
1552
- const wrapped = wrapGitFrame(use.body, prepared.reminderMark);
1553
- try {
1554
- const entryId = await prepared.session.appendMessage({
1555
- role: "user",
1556
- content: [{ type: "text", text: wrapped }],
1557
- timestamp: Date.now(),
1558
- engineMinted: true,
1559
- });
1560
- ref.announced = { kind: use.kind, hash: use.hash, entryId };
1561
- ref.protectedText = wrapped;
1562
- if (use.kind === "full" && frame.shrunk !== undefined) {
1563
- ref.wrappedShrink = { find: wrapped, replace: wrapGitFrame(frame.shrunk.body, prepared.reminderMark) };
1564
- }
1565
- else {
1566
- delete ref.wrappedShrink;
1567
- }
1568
- queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[use.kind], ...ident() });
1569
- rs.attach.attachmentsInjected += 1;
1570
- try {
1571
- await prepared.session.appendGitAnnouncement?.({ kind: use.kind, hash: use.hash, entryId });
1572
- }
1573
- catch (mirrorErr) {
1574
- try {
1575
- this.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; next leg re-announces): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
1576
- }
1577
- catch {
1578
- }
1579
- }
1580
- }
1581
- catch (err) {
1582
- ref.announced = { kind: use.kind, hash: use.hash, pending: true };
1583
- try {
1584
- this.deps.onError?.(new Error(`git status frame re-assert append failed (pending; retried at the next boundary): ${err instanceof Error ? err.message : String(err)}`), { phase: "degraded", sessionId: prepared.sessionId });
1585
- }
1586
- catch {
1587
- }
1588
- }
1589
- };
1590
- const flushGitMirror = async () => {
1591
- const owed = prepared.gitStatusRef.mirrorOwed;
1592
- if (owed === undefined)
1593
- return;
1594
- if (prepared.gitStatusRef.overBudgetShrunk && owed.kind === "full") {
1595
- delete prepared.gitStatusRef.mirrorOwed;
1596
- return;
1597
- }
1598
- delete prepared.gitStatusRef.mirrorOwed;
1599
- try {
1600
- await prepared.session.appendGitAnnouncement?.(owed);
1601
- }
1602
- catch (mirrorErr) {
1603
- prepared.gitStatusRef.mirrorOwed = owed;
1604
- try {
1605
- this.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; retried at the next serialization point): ${mirrorErr instanceof Error ? mirrorErr.message : String(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
1606
- }
1607
- catch {
1608
- }
1609
- }
1610
- };
1611
- const unsubGitRetry = prepared.harness.on("turn_boundary", async () => {
1612
- await flushGitMirror();
1613
- if (prepared.gitStatusRef.announced?.pending === true)
1614
- await prepared.gitStatusRef.reassert?.();
1615
- return undefined;
1616
- });
1617
- const unsubBoundary = prepared.harness.on("turn_boundary", onTurnBoundary);
1618
- const unsub = prepared.harness.subscribe((event) => {
1619
- switch (event.type) {
1620
- case "message_update":
1621
- onMessageUpdate(event);
1622
- break;
1623
- case "message_end":
1624
- onMessageEnd(event);
1625
- break;
1626
- case "tool_execution_start":
1627
- onToolStart(event);
1628
- break;
1629
- case "tool_execution_end":
1630
- onToolEnd(event);
1631
- break;
1632
- case "turn_end":
1633
- onTurnEnd();
1634
- break;
1635
- default:
1636
- break;
1637
- }
1638
- });
1639
- let final;
1640
- let threw;
1641
- let abortedLive = false;
1642
- let userInterruptedLive = false;
1643
- let strandedHumanAnswers = [];
1644
- try {
1645
- if (prepared.abortController.signal.aborted) {
1646
- const e = new Error("run aborted");
1647
- e.name = "AbortError";
1648
- throw e;
1649
- }
1650
- if (resume) {
1651
- const walltimeExhaustedResume = effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0;
1652
- const resumeWindowRetryAfterMs = !walltimeExhaustedResume && prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
1653
- if (resumeWindowRetryAfterMs !== undefined) {
1654
- const owesNothing = resume.outcome.gate === "resource_limit" &&
1655
- readPendingSteerQueue(resume.cp.state).length === 0 &&
1656
- (resume.cp.state.runningBackgroundTasks?.length ?? 0) === 0;
1657
- const committed = owesNothing &&
1658
- prepared.suspendForPlatformLimit !== undefined &&
1659
- (await prepared.suspendForPlatformLimit("usage_window", { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) }, { resumeAfterMs: resumeWindowRetryAfterMs }));
1660
- if (committed) {
1661
- rs.limits.turnsExceeded = false;
1662
- rs.limits.budgetHit = undefined;
1663
- }
1664
- else {
1665
- const terminal = platformLimitTerminal("usage_window", resumeWindowRetryAfterMs, "resume");
1666
- rs.limits.platformTerminal = terminal;
1667
- try {
1668
- this.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
1669
- }
1670
- catch {
1671
- }
1672
- }
1673
- }
1674
- if (resumeWindowRetryAfterMs === undefined && !walltimeExhaustedResume && resume.outcome.gate !== "wake") {
1675
- await this.applyResumeDecision(prepared, resume, (e) => pushContent({ ...e, ...ident() }), emitCommitted, (toolName, details) => {
1676
- if (rs.attach.attachState === undefined)
1677
- return;
1678
- const family = writeFamilyOf(toolName);
1679
- if (family !== undefined)
1680
- stampWriteAnchor(rs.attach.attachState, family, rs.counters.cadenceTurns);
1681
- if (details !== undefined)
1682
- reduceToolEnd(rs.attach.attachState, details);
1683
- }, (toolCallId) => {
1684
- startedToolCallIds.add(toolCallId);
1685
- if (resume !== undefined && resume.cp.pendingAction.kind === "tool_approval" && toolCallId === resume.cp.pendingAction.toolCallId) {
1686
- resume.pendingActionStarted = true;
1687
- }
1688
- });
1689
- if (resume.outcome.gate === "policy_ask")
1690
- resume.decisionDelivered = true;
1691
- announceWorkspaceMove();
1692
- }
1693
- if (resumeWindowRetryAfterMs !== undefined) {
1694
- }
1695
- else if (walltimeExhaustedResume) {
1696
- timeout.fired = true;
1697
- timeout.clear();
1698
- void prepared.harness.abort();
1699
- prepared.abortController.abort();
1700
- }
1701
- else if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow <= 0) {
1702
- rs.limits.budgetHit = "exceeded";
1703
- rs.limits.budgetAxis = "tokens";
1704
- }
1705
- else if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd <= 0) {
1706
- rs.limits.budgetHit = "exceeded";
1707
- rs.limits.budgetAxis = "cost";
1708
- }
1709
- else {
1710
- let continuation = resumeContinuation(resume, prepared.reminderMark);
1711
- const bgSnapshot = resume.cp.state.runningBackgroundTasks;
1712
- if (bgSnapshot !== undefined && bgSnapshot.length > 0) {
1713
- const alive = new Set(prepared.listBackgroundTasks().map((t) => t.id));
1714
- const orphans = bgSnapshot.filter((t) => !alive.has(t.id));
1715
- if (orphans.length > 0) {
1716
- continuation += "\n\n" + formatHookFeedback(renderOrphanedBackgroundTasks(orphans), prepared.reminderMark);
1717
- }
1718
- }
1719
- let gitResumeDelivered;
1720
- {
1721
- const gitBody = await resolveGitLegDelivery(prepared, resume.cp.state.gitAnnouncement, (err) => {
1722
- try {
1723
- this.deps.onError?.(new Error(`git announcement ladder unreadable (conservative re-announce): ${err.message}`), { phase: "degraded", sessionId: prepared.sessionId });
1724
- }
1725
- catch {
1726
- }
1727
- });
1728
- const gitFrame = prepared.gitStatusRef.frame;
1729
- if (gitBody !== undefined && gitFrame !== undefined) {
1730
- const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
1731
- continuation += "\n\n" + wrappedGit;
1732
- gitResumeDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, wrapped: wrappedGit };
1733
- if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
1734
- prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
1735
- }
1736
- }
1737
- }
1738
- const engineSegments = continuation.length > 0 ? [{ start: 0, end: continuation.length }] : [];
1739
- const resumeFrames = [
1740
- ...readPendingSteerQueue(resume.cp.state).map((entry) => ({ entry, source: "steer" })),
1741
- ...(resume.wakeMessage !== undefined ? [{ entry: resume.wakeMessage, source: "wake" }] : []),
1742
- ];
1743
- for (const { entry: steer, source } of resumeFrames) {
1744
- if (source === "wake" && resume.wakeMessageHookContext !== undefined) {
1745
- const hookStart = continuation.length;
1746
- continuation += "\n\n" + formatHookFeedback(resume.wakeMessageHookContext, prepared.reminderMark);
1747
- engineSegments.push({ start: hookStart, end: continuation.length });
1748
- }
1749
- if (source === "steer") {
1750
- const parkedScreen = (spec.hooks ?? this.deps.hooks)?.userPromptSubmit;
1751
- if (parkedScreen !== undefined) {
1752
- let outcome;
1753
- try {
1754
- const seat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => parkedScreen(steer.text, {
1755
- identity: prepared.hookIdentity,
1756
- signal: sig,
1757
- source: "parked_redelivery",
1758
- ...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
1759
- ...(steer.actor !== undefined ? { actor: snapshotActorAssertion(steer.actor) } : {}),
1760
- }));
1761
- outcome = seat.expired
1762
- ? { blocked: seat.cause === "timeout" ? `the screen did not answer within its ${prepared.hookTimeoutMs}ms bound (fail-closed)` : "the run was cancelled while the screen was still deciding (fail-closed)" }
1763
- : seat.value?.block !== undefined && seat.value.block !== ""
1764
- ? { blocked: inlineUntrusted(seat.value.block) }
1765
- : { ...(seat.value?.additionalContext !== undefined && seat.value.additionalContext !== "" ? { context: seat.value.additionalContext } : {}) };
1766
- }
1767
- catch (hookErr) {
1768
- const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
1769
- try {
1770
- this.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
1771
- }
1772
- catch {
1773
- }
1774
- outcome = { blocked: `the screen crashed (${inlineUntrusted(err.message)}; fail-closed)` };
1775
- }
1776
- if ("blocked" in outcome) {
1777
- deliverEngineNotice(this.deps.onNotice, {
1778
- code: "steering.parked_input_blocked",
1779
- message: "a parked steering input was withheld by the deployment's userPromptSubmit screen when this resume redelivered it: " +
1780
- `${outcome.blocked}. The parked row was consumed with the checkpoint; the instruction never reached the model — ` +
1781
- "re-issue it if it still applies.",
1782
- detail: {
1783
- ...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
1784
- sessionId: prepared.sessionId,
1785
- runId: prepared.runId,
1786
- ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
1787
- },
1788
- });
1789
- queue.push({
1790
- ...buildHumanInputEvent({
1791
- carrier: source,
1792
- source,
1793
- delivery: "blocked",
1794
- sessionSeq: nextHumanInputSeq(prepared.harness),
1795
- ...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
1796
- ...(steer.actor !== undefined ? { actor: steer.actor } : {}),
1797
- ...(steer.actor?.issuer !== undefined ? { issuer: steer.actor.issuer } : {}),
1798
- ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
1799
- }),
1800
- ...ident(),
1801
- });
1802
- continue;
1803
- }
1804
- if ("context" in outcome && outcome.context !== undefined) {
1805
- const hookStart = continuation.length;
1806
- continuation += "\n\n" + formatHookFeedback(outcome.context, prepared.reminderMark);
1807
- engineSegments.push({ start: hookStart, end: continuation.length });
1808
- }
1809
- }
1810
- }
1811
- const start = continuation.length;
1812
- const projected = projectHumanInput({ text: steer.text, actor: steer.actor, source });
1813
- continuation +=
1814
- "\n\n" +
1815
- (steer.trusted ? formatHookFeedback(projected, prepared.reminderMark) : delimitUntrusted("supervisor steering message", projected));
1816
- if (steer.trusted)
1817
- engineSegments.push({ start, end: continuation.length });
1818
- queue.push({
1819
- ...buildHumanInputEvent({
1820
- carrier: source,
1821
- source,
1822
- delivery: "applied",
1823
- sessionSeq: nextHumanInputSeq(prepared.harness),
1824
- inputId: steer.inputId,
1825
- ...(steer.actor !== undefined ? { actor: steer.actor } : {}),
1826
- ...(steer.actor?.issuer !== undefined ? { issuer: steer.actor.issuer } : {}),
1827
- ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
1828
- }),
1829
- ...ident(),
1830
- });
1831
- }
1832
- if (resume !== undefined)
1833
- resume.decisionDelivered = true;
1834
- if (gitResumeDelivered !== undefined) {
1835
- const delivered = gitResumeDelivered;
1836
- prepared.gitStatusRef.protectedText = delivered.wrapped;
1837
- prepared.gitStatusRef.pendingReceipt = {
1838
- text: continuation,
1839
- commit: (entryId) => {
1840
- prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
1841
- queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
1842
- rs.attach.attachmentsInjected += 1;
1843
- prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
1844
- },
1845
- };
1846
- }
1847
- final = await withBrainSinks(() => prepared.harness.prompt(continuation, { engineSegments }));
1848
- await flushGitMirror();
1849
- if (prepared.gitStatusRef.announced?.pending === true)
1850
- await prepared.gitStatusRef.reassert?.();
1851
- }
1852
- }
1853
- else {
1854
- const objectiveActor = entryActor;
1855
- const projectedObjective = projectHumanInput({ text: spec.objective, actor: objectiveActor, source: "objective" });
1856
- let effectiveObjective = projectedObjective;
1857
- let promptBlocked = false;
1858
- const userPromptSubmit = (spec.hooks ?? this.deps.hooks)?.userPromptSubmit;
1859
- if (userPromptSubmit) {
1860
- try {
1861
- const promptSeat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => userPromptSubmit(spec.objective, { identity: prepared.hookIdentity, signal: sig, source: "objective", ...(objectiveActor !== undefined ? { actor: snapshotActorAssertion(objectiveActor) } : {}) }));
1862
- if (promptSeat.expired) {
1863
- if (promptSeat.cause === "timeout") {
1864
- try {
1865
- this.deps.onError?.(hookSeatExpiredError("userPromptSubmit", prepared.hookTimeoutMs, promptSeat.cause, "the prompt was NOT submitted (fail-closed) and the task ends blocked"), { phase: "hook", sessionId: prepared.sessionId });
1866
- }
1867
- catch {
1868
- }
1869
- }
1870
- prepared.blockedRef.reason = formatHookFeedback(promptSeat.cause === "timeout"
1871
- ? `the deployment's userPromptSubmit hook did not answer within its ${prepared.hookTimeoutMs}ms bound while screening this prompt; ` +
1872
- `the prompt was NOT submitted (fail-closed)`
1873
- : `the task was cancelled while the deployment's userPromptSubmit hook was still screening this prompt; ` +
1874
- `the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
1875
- promptBlocked = true;
1876
- }
1877
- const decision = promptSeat.expired ? undefined : promptSeat.value;
1878
- if (decision?.block) {
1879
- prepared.blockedRef.reason = formatHookFeedback(decision.block, prepared.reminderMark);
1880
- promptBlocked = true;
1881
- }
1882
- else if (decision?.additionalContext) {
1883
- effectiveObjective = `${formatHookFeedback(decision.additionalContext, prepared.reminderMark)}\n\n${projectedObjective}`;
1884
- }
1885
- }
1886
- catch (hookErr) {
1887
- const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
1888
- try {
1889
- this.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
1890
- }
1891
- catch {
1892
- }
1893
- prepared.blockedRef.reason = formatHookFeedback(`the deployment's userPromptSubmit hook crashed while screening this prompt (${err.message}); ` +
1894
- `the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
1895
- promptBlocked = true;
1896
- }
1897
- }
1898
- let gitLegDelivered;
1899
- if (!promptBlocked) {
1900
- const gitBody = await resolveGitLegDelivery(prepared, undefined, (err) => {
1901
- try {
1902
- this.deps.onError?.(new Error(`git announcement ladder unreadable (conservative re-announce): ${err.message}`), { phase: "degraded", sessionId: prepared.sessionId });
1903
- }
1904
- catch {
1905
- }
1906
- });
1907
- const gitFrame = prepared.gitStatusRef.frame;
1908
- if (gitBody !== undefined && gitFrame !== undefined) {
1909
- const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
1910
- const standalone = spec.images !== undefined && spec.images.length > 0;
1911
- if (!standalone)
1912
- effectiveObjective = `${wrappedGit}\n${effectiveObjective}`;
1913
- gitLegDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, standalone, wrapped: wrappedGit };
1914
- if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
1915
- prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
1916
- }
1917
- }
1918
- }
1919
- const firstFrames = [];
1920
- if (rs.attach.attachState !== undefined) {
1921
- const attach = rs.attach.attachState;
1922
- if (rs.attach.agentListingOn && prepared.agentListing !== undefined) {
1923
- const listing = prepared.agentListing;
1924
- const body = renderAgentListingDelta(attach, listing.entries, listing.toolName, listing.models);
1925
- if (body !== undefined) {
1926
- firstFrames.push({
1927
- source: "agent_listing",
1928
- body,
1929
- commit: () => {
1930
- commitAgentListing(attach, listing.entries, listing.models);
1931
- prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
1932
- if (listing.models !== undefined)
1933
- prepared.announcedListingsRef.models = [...listing.models];
1934
- },
1935
- });
1936
- }
1937
- }
1938
- if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined) {
1939
- const listing = prepared.skillsListing;
1940
- const body = renderSkillsListingDelta(attach, listing.entries);
1941
- if (body !== undefined) {
1942
- firstFrames.push({
1943
- source: "skills_listing",
1944
- body,
1945
- commit: () => {
1946
- commitSkillsListing(attach, listing.entries);
1947
- prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
1948
- },
1949
- });
1950
- }
1951
- }
1952
- if (firstFrames.length > 0) {
1953
- effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body, attachmentEnvelopeTags(f.source)), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
1954
- }
1955
- }
1956
- const gitQueuedChars = gitLegDelivered !== undefined && gitLegDelivered.standalone ? gitLegDelivered.wrapped.length : 0;
1957
- const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
1958
- ? 0
1959
- : computeCostMicroUsd({
1960
- totalInputTokens: Math.ceil((effectiveObjective.length + gitQueuedChars) / 4),
1961
- cacheReadTokens: 0,
1962
- cacheWriteTokens: 0,
1963
- cacheWriteTokensLong: 0,
1964
- outputTokens: prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS,
1965
- }, rs.telemetry.pricing);
1966
- const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? rs.budget.remainingMicroUsd : rs.budget.maxCostMicroUsd;
1967
- const precallTokens = Math.ceil((effectiveObjective.length + gitQueuedChars) / 4) + (prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS);
1968
- const precallCeilingTokens = prepared.suspendForResource !== undefined ? rs.budget.remainingTokens : rs.budget.maxTokensWindow;
1969
- const entryUsageRetryAfterMs = prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
1970
- if (promptBlocked) {
1971
- }
1972
- else if (entryUsageRetryAfterMs !== undefined) {
1973
- const terminal = platformLimitTerminal("usage_window", entryUsageRetryAfterMs, "entry");
1974
- rs.limits.platformTerminal = terminal;
1975
- try {
1976
- this.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
1977
- }
1978
- catch {
1979
- }
1980
- }
1981
- else if (precallCeilingTokens !== undefined && precallTokens > precallCeilingTokens) {
1982
- rs.limits.budgetHit = "precall";
1983
- rs.limits.budgetAxis = "tokens";
1984
- }
1985
- else if (precallCeilingMicroUsd !== undefined && precallMicroUsd > precallCeilingMicroUsd) {
1986
- rs.limits.budgetHit = "precall";
1987
- rs.limits.budgetAxis = "cost";
1988
- }
1989
- else if (effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0) {
1990
- timeout.fired = true;
1991
- timeout.clear();
1992
- void prepared.harness.abort();
1993
- prepared.abortController.abort();
1994
- }
1995
- else {
1996
- const images = spec.images
1997
- ? await Promise.all(spec.images.map((img) => toImageContent(img, this.deps.allowImageUrl)))
1998
- : undefined;
1999
- if (gitLegDelivered !== undefined) {
2000
- const delivered = gitLegDelivered;
2001
- if (delivered.standalone)
2002
- await prepared.harness.nextTurn(delivered.wrapped, { engineMinted: true });
2003
- prepared.gitStatusRef.protectedText = delivered.wrapped;
2004
- prepared.gitStatusRef.pendingReceipt = {
2005
- text: delivered.standalone ? delivered.wrapped : effectiveObjective,
2006
- commit: (entryId) => {
2007
- prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
2008
- queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
2009
- rs.attach.attachmentsInjected += 1;
2010
- prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
2011
- },
2012
- };
2013
- }
2014
- for (const f of firstFrames) {
2015
- f.commit();
2016
- queue.push({ type: "steering_injected", source: f.source, preview: redactThenCut(f.body, 220), ...ident() });
2017
- }
2018
- rs.attach.attachmentsInjected += firstFrames.length;
2019
- if (firstFrames.length > 0 && prepared.announcedSnapshotRecovered)
2020
- await prepared.session.appendAnnouncedListing(prepared.announcedListingsRef).catch(() => undefined);
2021
- const enginePrefixChars = effectiveObjective.length - projectedObjective.length;
2022
- queue.push({
2023
- ...buildHumanInputEvent({
2024
- carrier: "objective",
2025
- source: "objective",
2026
- delivery: "applied",
2027
- sessionSeq: nextHumanInputSeq(prepared.harness),
2028
- ...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
2029
- ...(objectiveActor?.issuer !== undefined ? { issuer: objectiveActor.issuer } : {}),
2030
- ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
2031
- }),
2032
- ...ident(),
2033
- });
2034
- final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, {
2035
- ...(images && images.length > 0 ? { images } : {}),
2036
- ...(enginePrefixChars > 0 ? { enginePrefixChars } : {}),
2037
- ...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
2038
- }));
2039
- await flushGitMirror();
2040
- if (prepared.gitStatusRef.announced?.pending === true)
2041
- await prepared.gitStatusRef.reassert?.();
2042
- }
2043
- }
2044
- loopLatch.ended = true;
2045
- abortedLive = prepared.abortController.signal.aborted;
2046
- userInterruptedLive = loopLatch.userInterrupted;
2047
- }
2048
- catch (err) {
2049
- loopLatch.ended = true;
2050
- if (errorCodeOf(err) === "resume.tool_unavailable" || errorCodeOf(err) === "resume.tool_contract_mismatch") {
2051
- await settleTeardownLeg(() => prepared.mcp.dispose(), "mcp.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2052
- await settleTeardownLeg(() => prepared.a2a?.dispose(), "a2a.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2053
- await settleTeardownLeg(() => (prepared.ownedEnv && hasDestroy(prepared.ownedEnv) ? prepared.ownedEnv.destroy() : undefined), "ownedEnv.destroy (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2054
- throw err;
2055
- }
2056
- threw = err;
2057
- abortedLive = prepared.abortController.signal.aborted;
2058
- userInterruptedLive = loopLatch.userInterrupted;
2059
- }
2060
- finally {
2061
- timeout.clear();
2062
- prepared.lspDiagnostics?.registry.releaseRun(prepared.lspDiagnostics.runIdent);
2063
- prepared.abortController.abort();
2064
- prepared.releaseSignal();
2065
- strandedHumanAnswers = prepared.settleContentAskBindings();
2066
- try {
2067
- prepared.harness.recoverUndrainedEngineNotes();
2068
- }
2069
- catch {
2070
- }
2071
- notificationLaneLive = false;
2072
- unsubscribeTaskNotifications();
2073
- prepared.toolRosterDeltas.unsubscribe();
2074
- unsubGitRetry();
2075
- unsubBoundary();
2076
- unsub();
2077
- }
2078
- if (threw === undefined && prepared.brainCallGuardrailRef.timedOut !== undefined) {
2079
- threw = prepared.brainCallGuardrailRef.timedOut;
2080
- }
2081
- if (threw === undefined && prepared.gateStopRef.terminal !== undefined) {
2082
- threw = prepared.gateStopRef.terminal;
2083
- }
2084
- if (rs.limits.platformTerminal !== undefined) {
2085
- threw = rs.limits.platformTerminal;
2086
- }
2087
- if (prepared.usageGovernance !== undefined) {
2088
- try {
2089
- await awaitChargeWithSlowDisclosure(prepared.usageGovernance.commit(stats.tokens, rs.telemetry.unpricedSpend ? undefined : stats.costMicroUsd, Date.now()), () => this.deps.onError?.(new Error("the usage ledger's FINAL charge has not settled after 10s — still waiting (a charge is never abandoned: walking away would double-charge on a later flush). A wedged ledger store wedges this teardown, now visibly."), { phase: "config", sessionId: prepared.sessionId }));
2090
- }
2091
- catch (flushErr) {
2092
- this.deps.onError?.(flushErr, { phase: "config", sessionId: prepared.sessionId });
2093
- }
2094
- }
2095
- const committedPause = prepared.pausedRef.current;
2096
- const durablyPaused = committedPause !== undefined;
2097
- const interrupted = !durablyPaused &&
2098
- (threw !== undefined || timeout.fired || rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || abortedLive || final?.stopReason === "aborted");
2099
- let orphansClosed = 0;
2100
- let reconcileComplete = false;
2101
- if (interrupted) {
2102
- try {
2103
- const report = await reconcileInterruptedSession(prepared.session, prepared.toolEffects, undefined, startedToolCallIds);
2104
- orphansClosed = report.recovered.length;
2105
- for (const orphan of report.recovered) {
2106
- queue.push({ type: "tool_end", toolCallId: orphan.toolCallId, toolName: orphan.toolName, ...(toolLabels.has(orphan.toolName) ? { label: toolLabels.get(orphan.toolName) } : {}), isError: true, ...reconciledToolEndBody(orphan, prepared.structuredProjector), ...ident() });
2107
- emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
2108
- }
2109
- reconcileComplete = true;
2110
- }
2111
- catch (reconcileErr) {
2112
- this.deps.onError?.(reconcileErr instanceof Error ? reconcileErr : new Error(String(reconcileErr)), {
2113
- phase: "interrupt-reconcile",
2114
- sessionId: prepared.sessionId,
2115
- });
2116
- }
2117
- try {
2118
- await prepared.harness.flushQueuedSessionWrites();
2119
- }
2120
- catch (flushErr) {
2121
- this.deps.onError?.(flushErr instanceof Error ? flushErr : new Error(String(flushErr)), {
2122
- phase: "interrupt-reconcile",
2123
- sessionId: prepared.sessionId,
2124
- });
2125
- }
2126
- }
2127
- if (interrupted && userInterruptedLive && reconcileComplete) {
2128
- try {
2129
- const entryId = await appendInterruptionMarker(prepared.session, { toolUseInFlight: orphansClosed > 0 });
2130
- emitCommitted(entryId, "user");
2131
- }
2132
- catch (markerErr) {
2133
- this.deps.onError?.(markerErr instanceof Error ? markerErr : new Error(String(markerErr)), {
2134
- phase: "interrupt-reconcile",
2135
- sessionId: prepared.sessionId,
2136
- });
2137
- }
2138
- }
2139
- let migratedParked = { steer: 0, followUp: 0 };
2140
- if (committedPause !== undefined && this.deps.checkpointStore !== undefined) {
2141
- const parkToken = committedPause.cause.token;
2142
- const parkScope = committedPause.scope;
2143
- const store = this.deps.checkpointStore;
2144
- const carried = [];
2145
- for (const record of prepared.harness.readParkableUserInputs()) {
2146
- try {
2147
- if (!(await store.setPendingSteer(parkToken, parkScope, record)))
2148
- break;
2149
- carried.push(record);
2150
- }
2151
- catch (parkErr) {
2152
- this.deps.onError?.(parkErr instanceof Error ? parkErr : new Error(String(parkErr)), {
2153
- phase: "config",
2154
- sessionId: prepared.sessionId,
2155
- });
2156
- const parkCode = errorCodeOf(parkErr);
2157
- if (parkCode === "steering.invalid_content" || parkCode === "steering.queue_full")
2158
- continue;
2159
- break;
2160
- }
2161
- }
2162
- migratedParked = prepared.harness.dropParkedUserInputs(carried);
2163
- }
2164
- if (undrainedUserAtEnd !== undefined) {
2165
- const remaining = {
2166
- steer: Math.max(0, undrainedUserAtEnd.steer - migratedParked.steer),
2167
- followUp: Math.max(0, undrainedUserAtEnd.followUp - migratedParked.followUp),
2168
- };
2169
- for (const notice of undrainedUserInputNotices(remaining, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
2170
- deliverEngineNotice(this.deps.onNotice, notice);
2171
- }
2172
- }
2173
- if (rs.attach.attachState?.postCompactPending === true && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
2174
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
2175
- }
2176
- const comp = await this.finish(spec, prepared, {
2177
- telemetry: { tracer: rs.telemetry.tracer, taskId: rs.telemetry.taskId },
2178
- skipCompaction: durablyPaused || compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES,
2179
- minTokens: rs.counters.compactionFloor,
2180
- brain: compactionBrain,
2181
- windowSafety: windowSafetyOptions(prepared.model),
2182
- onCompactionFailed: (reason) => queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "auto", reason, ...ident() }),
2183
- cancelled: abortedLive,
496
+ const { flushGitMirror, unsubGitRetry, unsubBoundary, unsub } = runGitLane({ prepared, queue, rs, ident, onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, onTurnBoundary, runner: this.depsSeat });
497
+ return runLeg({
498
+ spec, queue, prepared, resume, rs, entryActor, effectiveTimeoutMs, timeout, pushContent, ident, emitCommitted, writeFamilyOf, startedToolCallIds,
499
+ announceWorkspaceMove, withBrainSinks, flushGitMirror, loopLatch, notificationLane, unsubscribeTaskNotifications, unsubGitRetry, unsubBoundary, unsub,
500
+ runner: this.depsSeat,
501
+ applyResumeDecision: (p, r, emit, committed, onResolved, onStart) => this.applyResumeDecision(p, r, emit, committed, onResolved, onStart),
502
+ next: ({ final, threw, abortedLive, userInterruptedLive, strandedHumanAnswers }) => runTerminalAdoption({
503
+ spec, queue, prepared, rs, stats, ident, emitCommitted, startedToolCallIds, toolLabels, timeout, compactionBrain, compactionBreaker, windowSafetyOptions,
504
+ undrainedUserAtEnd, final, threw, abortedLive, userInterruptedLive, runner: this.depsSeat,
505
+ finish: (s, p, o) => this.finish(s, p, o),
506
+ next: ({ threw, comp }) => runSettleAndTeardown({
507
+ spec, queue, prepared, resume, internals, rs, stats, ident, parentToolCallId, subagentName, loopLatch, timeout, ownCommittedTailRef,
508
+ emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, strandedHumanAnswers, reasoningResolution, comp,
509
+ setResult, onSuggestions, onSuspend, runner: this.depsSeat, sessions: this.sessions,
510
+ suggestNextPrompts: (s, p, r) => this.suggestNextPrompts(s, p, r),
511
+ teardownOwnedEnv: (p) => this.teardownOwnedEnv(p),
512
+ }),
513
+ }),
2184
514
  });
2185
- try {
2186
- if (comp?.compacted) {
2187
- queue.push({
2188
- type: "compacted",
2189
- trigger: "auto",
2190
- tokensBefore: comp.tokensBefore ?? 0,
2191
- ...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
2192
- ...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
2193
- ...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
2194
- ...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
2195
- ...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
2196
- ...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
2197
- ...(comp.modelFallback ? { modelFallback: true } : {}),
2198
- ...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
2199
- ...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
2200
- ...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
2201
- ...ident(),
2202
- });
2203
- if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
2204
- const pd = comp.phaseDurations;
2205
- const pdDur = comp.durationMs;
2206
- emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
2207
- }
2208
- prepared.cacheBreakDetector?.notifyCompaction();
2209
- }
2210
- if (prepared.nestedStats.tasks > 0) {
2211
- stats.nested = {
2212
- tokens: prepared.nestedStats.tokens,
2213
- turns: prepared.nestedStats.turns,
2214
- tasks: prepared.nestedStats.tasks,
2215
- ...(prepared.nestedStats.anyUnpriced ? {} : { costMicroUsd: prepared.nestedStats.costMicroUsd }),
2216
- };
2217
- }
2218
- if (stats.totalInputTokens > 0) {
2219
- const rawHit = stats.cachedTokens / stats.totalInputTokens;
2220
- if (stats.cachedTokens > stats.totalInputTokens) {
2221
- this.deps.onError?.(new Error(`prompt-cache: usage/api mismatch — cachedTokens (${stats.cachedTokens}) > totalInputTokens (${stats.totalInputTokens}); the cache family is likely wrong. Does model.api match the brain serving it? Override with model.params.promptCacheFamily = "input-includes-cached" | "input-excludes-cached".`), { phase: "prompt-cache", sessionId: prepared.sessionId });
2222
- }
2223
- stats.cacheHitRate = Math.min(1, rawHit);
2224
- if (!rs.telemetry.cacheBreakReported && stats.turns >= 2 && stats.totalInputTokens >= 8000 && stats.cacheHitRate < 0.15) {
2225
- const cacheWritten = stats.cacheWriteTokens + stats.cacheWriteTokensLong;
2226
- const cause = rs.degrade.degraded
2227
- ? `a mid-task model switch (${rs.degrade.degraded.from} → ${rs.degrade.degraded.to}, degradation) reset the prefix cache — this is the likely cause`
2228
- : cacheWritten > 0
2229
- ? `this task reported ${cacheWritten} cache-write tokens across its calls (compaction included) but served almost none back as reads — consistent with a prompt prefix that CHANGES between turns (client-side: volatile content up front, or per-turn tool churn/reorder) and, less often, with server-side eviction. Keep volatile content (memory/timestamps/ids) out of the prefix and the tool list stable in membership AND order`
2230
- : `no call in this task reported any cache-write tokens — and this API family may not report them at all (an openai-shaped usage row carries cached READS only), so the write side is no evidence here; check that the prompt prefix (system prompt + tool list, membership AND order) is byte-stable across turns and that this route caches this model`;
2231
- this.deps.onError?.(new Error(`prompt-cache: low prefix-cache hit rate ${(stats.cacheHitRate * 100).toFixed(0)}% over ${stats.turns} turns (${stats.totalInputTokens} prompt tokens) — ${cause}. See design/09.`), { phase: "prompt-cache", sessionId: prepared.sessionId });
2232
- }
2233
- }
2234
- if (prepared.resourceLedger &&
2235
- (resume?.cp.gate.kind === "resource_limit" || prepared.pausedRef.current?.cause.gate.kind === "resource_limit")) {
2236
- stats.costMicroUsd += prepared.resourceLedger.spentMicroUsd;
2237
- stats.tokens += prepared.resourceLedger.spentTokens;
2238
- stats.turns += prepared.resourceLedger.spentTurns;
2239
- }
2240
- if (prepared.humanReviewRef.count > 0) {
2241
- stats.humanReview = {
2242
- count: prepared.humanReviewRef.count,
2243
- totalWaitMs: prepared.humanReviewRef.totalWaitMs,
2244
- gates: [...prepared.humanReviewRef.gates],
2245
- };
2246
- }
2247
- const reminderDisclosuresActive = Object.keys(prepared.reminderDisclosureCounts).length > 0;
2248
- if (rs.counters.finalVerifyInjections > 0 || rs.attach.attachmentsInjected > 0 || rs.counters.repetitionCuts > 0 || rs.counters.repetitionSpared > 0 || rs.counters.approachNoticesSent > 0 || reminderDisclosuresActive) {
2249
- stats.mechanisms = {
2250
- ...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
2251
- ...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjections: rs.counters.finalVerifyInjections } : {}),
2252
- ...(rs.attach.attachmentsInjected > 0 ? { attachmentsInjected: rs.attach.attachmentsInjected } : {}),
2253
- ...(rs.counters.approachNoticesSent > 0 ? { approachNoticesSent: rs.counters.approachNoticesSent } : {}),
2254
- ...(rs.counters.repetitionCuts > 0 ? { repetitionCuts: rs.counters.repetitionCuts } : {}),
2255
- ...(rs.counters.repetitionSpared > 0 ? { repetitionSpared: rs.counters.repetitionSpared } : {}),
2256
- ...(rs.counters.repetitionEvents.length > 0 ? { repetitionEvents: rs.counters.repetitionEvents } : {}),
2257
- ...(reminderDisclosuresActive ? { reminderDisclosures: { ...prepared.reminderDisclosureCounts } } : {}),
2258
- };
2259
- }
2260
- const result = assembleResult(spec, prepared.sessionId, final, stats, {
2261
- threw,
2262
- runId: prepared.runId,
2263
- model: prepared.model.id,
2264
- unpricedSpend: rs.telemetry.unpricedSpend,
2265
- rewindNotes: prepared.rewindNotes,
2266
- editedFiles: prepared.editedFilesSnapshot(),
2267
- haltedOnUserRejection: prepared.batchHaltRef.current !== undefined,
2268
- userHalted: loopLatch.userHalted,
2269
- strandedHumanAnswers,
2270
- remoteEnvFailures: prepared.remoteEnvFailures,
2271
- effectiveReadFace: prepared.effectiveReadFace,
2272
- effectiveReadDenyPatterns: prepared.effectiveReadDenyPatterns,
2273
- effectiveMemoryScopes: prepared.effectiveMemoryScopes,
2274
- effectiveReasoning: reasoningResolution,
2275
- retryAfterMs: rs.limits.platformTerminal?.retryAfterMs,
2276
- abortedForTimeout: timeout.fired,
2277
- abortedForTurns: rs.limits.turnsExceeded,
2278
- abortedLive,
2279
- budgetHit: rs.limits.budgetHit,
2280
- budgetAxis: rs.limits.budgetAxis,
2281
- blockedReason: prepared.blockedRef.reason,
2282
- conflict: prepared.conflictRef.hit,
2283
- gitCoreOverBudget: prepared.gitStatusRef.terminalCode === "irreducible_core_over_budget",
2284
- outputInvalid: rs.degrade.outputInvalid,
2285
- suspendLoop: prepared.suspendLoopRef.hit,
2286
- ...(prepared.pausedRef.current !== undefined ? { paused: prepared.pausedRef.current.cause } : {}),
2287
- });
2288
- if (resume !== undefined &&
2289
- resume.pendingActionStarted !== true &&
2290
- result.terminal.kind !== "completed" &&
2291
- result.terminal.kind !== "paused" &&
2292
- !(resumeDecisionWasNegative(resume) && resume.decisionDelivered === true) &&
2293
- resume.onEnvRestoreFailed !== undefined) {
2294
- try {
2295
- await resume.onEnvRestoreFailed("env_failed");
2296
- amendTerminal(result, "resume.reopened_unstarted", (m) => `${m ?? "the resume leg did not complete"} — the approved action never started, so the checkpoint was REOPENED (pending): the same approval can be resumed again, do not ask for a fresh one`);
2297
- }
2298
- catch {
2299
- amendTerminal(result, "resume.reopen_failed_unstarted", (m) => `${m ?? "the resume leg did not complete"} — the approved action never started, and reopening the checkpoint FAILED: its state is unprovable from here. Re-read it before deciding; do NOT issue a fresh approval on the assumption the old one is dead`);
2300
- }
2301
- }
2302
- prepared.sealReadStateSeat?.(ownCommittedTailRef.current);
2303
- {
2304
- const wss = prepared.workspaceStateSettle;
2305
- if (wss !== undefined && result.terminal.kind !== "paused") {
2306
- const curCwd = prepared.cwdRef?.current !== undefined && prepared.cwdRef.current !== wss.rootCanonical ? prepared.cwdRef.current : undefined;
2307
- const curWt = prepared.worktreeSessionRef?.current;
2308
- const changed = curCwd !== wss.restoredHandsCwd || curWt?.worktreeDir !== wss.restoredWorktreeDir;
2309
- if (curCwd !== undefined || curWt !== undefined || changed || wss.baselineUnknown === true) {
2310
- await settleTeardownLeg(() => prepared.session.appendWorkspaceState({
2311
- taskRoot: wss.rootCanonical,
2312
- ...(curCwd !== undefined ? { handsCwd: curCwd } : {}),
2313
- ...(curWt !== undefined ? { activeWorktree: { ...curWt } } : {}),
2314
- }), "session.appendWorkspaceState (settle leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2315
- }
2316
- }
2317
- }
2318
- const stopFailureHook = (spec.hooks ?? this.deps.hooks)?.stopFailure;
2319
- if (stopFailureHook !== undefined &&
2320
- result.terminal.kind === "failed" &&
2321
- final?.stopReason === "error" &&
2322
- !isDegenerateCutMessage(final) &&
2323
- threw === undefined &&
2324
- rs.limits.budgetHit === undefined &&
2325
- !rs.degrade.outputInvalid &&
2326
- !prepared.suspendLoopRef.hit &&
2327
- !abortedLive &&
2328
- result.terminal.code !== "conflict") {
2329
- const failedCause = result.terminal;
2330
- try {
2331
- const failureSeat = await runHookSeat("stopFailure", { timeoutMs: prepared.hookTimeoutMs }, (sig) => stopFailureHook({
2332
- identity: prepared.hookIdentity,
2333
- error: failedCause.message ?? "model error",
2334
- ...(failedCause.code !== undefined ? { errorKind: failedCause.code } : {}),
2335
- turns: stats.turns,
2336
- signal: sig,
2337
- }));
2338
- if (failureSeat.expired) {
2339
- try {
2340
- this.deps.onError?.(hookSeatExpiredError("stopFailure", prepared.hookTimeoutMs, failureSeat.cause, "the terminal observation was abandoned; the assembled TaskResult is unchanged"), { phase: "hook", sessionId: prepared.sessionId });
2341
- }
2342
- catch {
2343
- }
2344
- }
2345
- }
2346
- catch (err) {
2347
- try {
2348
- this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
2349
- }
2350
- catch {
2351
- }
2352
- }
2353
- }
2354
- const terminalFace = terminalProjection(result.terminal);
2355
- emitDelegationLifecycle({
2356
- phase: "terminal",
2357
- identity: prepared.hookIdentity,
2358
- status: terminalFace.status,
2359
- turns: stats.turns,
2360
- ...(terminalFace.errorCode !== undefined ? { errorCode: terminalFace.errorCode } : {}),
2361
- });
2362
- if (taskIdRef !== undefined)
2363
- taskIdRef.delegationTerminalOwed = undefined;
2364
- if (rs.degrade.degraded)
2365
- result.degraded = rs.degrade.degraded;
2366
- if (prepared.outputRef.set)
2367
- result.structuredOutput = prepared.outputRef.value;
2368
- const committedToken = prepared.pausedRef.current?.cause.token;
2369
- const committedScope = prepared.pausedRef.current?.scope;
2370
- if (committedToken !== undefined) {
2371
- const store = resolveCheckpointStore(spec, this.deps);
2372
- if (store && committedScope !== undefined) {
2373
- onSuspend({
2374
- env: prepared.ownedEnv,
2375
- token: committedToken,
2376
- scope: committedScope,
2377
- store,
2378
- sessionId: prepared.sessionId,
2379
- });
2380
- }
2381
- }
2382
- if (resume && committedToken === undefined) {
2383
- await settleTeardownLeg(() => this.sessions.unpin?.(resume.cp.sessionId), "sessions.unpin (resumed-terminal leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2384
- }
2385
- if (prepared.memoryEngineSession) {
2386
- const mes = prepared.memoryEngineSession;
2387
- await settleTeardownLeg(() => mes.harvest(), "memoryEngineSession.harvest (settle leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2388
- }
2389
- emitTrace(rs.telemetry.tracer, () => ({
2390
- kind: "task.end",
2391
- version: 1,
2392
- taskId: rs.telemetry.taskId,
2393
- runId: rs.telemetry.runId,
2394
- status: terminalFace.status,
2395
- errorCode: terminalFace.errorCode,
2396
- turns: stats.turns,
2397
- tokens: stats.tokens,
2398
- ...(rs.telemetry.unpricedSpend ? {} : { costMicroUsd: stats.costMicroUsd }),
2399
- durationMs: Date.now() - rs.telemetry.taskStart,
2400
- ...(prepared.outputRef.set ? { hasStructuredOutput: true } : {}),
2401
- ...(stats.mechanisms !== undefined
2402
- ? { mechanisms: (({ repetitionEvents: _events, ...scalars }) => scalars)(stats.mechanisms) }
2403
- : {}),
2404
- ...(timeout.latenessMs !== undefined && timeout.latenessMs > TIMER_LATENESS_REPORT_MS
2405
- ? { timerLatenessMs: timeout.latenessMs }
2406
- : {}),
2407
- ...(rs.counters.walltimeSyncBackstopFired ? { walltimeSyncBackstop: true } : {}),
2408
- ts: Date.now(),
2409
- }));
2410
- setResult(result);
2411
- if (parentToolCallId !== undefined && result.terminal.kind !== "paused") {
2412
- const terminalTick = {
2413
- type: "task_progress",
2414
- taskId: rs.telemetry.taskId,
2415
- ...(internals?.delegationTaskType !== undefined ? { taskType: internals.delegationTaskType } : {}),
2416
- ...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
2417
- ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
2418
- ...(subagentName ? { name: subagentName } : {}),
2419
- model: prepared.model.id,
2420
- usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
2421
- status: result.terminal.kind === "completed" ? "completed" : "failed",
2422
- ...ident(),
2423
- };
2424
- queue.push(terminalTick);
2425
- try {
2426
- internals?.onForwardEvent?.(terminalTick);
2427
- }
2428
- catch {
2429
- }
2430
- }
2431
- if (manualCompactRef.waiters.length > 0) {
2432
- queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "manual", reason: "task_ending", ...ident() });
2433
- drainManualCompact("mooted");
2434
- }
2435
- manualCompactRef.emitMooted = undefined;
2436
- if (forwardsSubagentEvents(spec))
2437
- await drainForwardedFramesBeforeDone();
2438
- queue.push({ type: "done", result });
2439
- queue.close();
2440
- await prepared.fileHistoryBoundary?.settle();
2441
- if (spec.suggestNextPrompts && result.terminal.kind === "completed") {
2442
- onSuggestions(this.suggestNextPrompts(spec, prepared, result));
2443
- }
2444
- }
2445
- finally {
2446
- const ownedEnvDying = prepared.ownedEnv !== undefined &&
2447
- hasDestroy(prepared.ownedEnv) &&
2448
- isIsolated(prepared.ownedEnv) &&
2449
- prepared.pausedRef.current === undefined;
2450
- await settleTeardownLeg(() => defaultTaskRegistry.settleKilledForOwner({ owner: spec.taskId ?? prepared.sessionId, scope: spec.principal ?? "default", sessionId: prepared.sessionId }, {
2451
- source: abortedLive &&
2452
- !timeout.fired &&
2453
- !rs.limits.turnsExceeded &&
2454
- rs.limits.budgetHit === undefined &&
2455
- prepared.pausedRef.current === undefined
2456
- ? "user"
2457
- : "parent",
2458
- skipSessionScoped: true,
2459
- envDying: ownedEnvDying,
2460
- retainProcesses: spec.retainBackgroundProcesses === true,
2461
- }), "taskRegistry.settleKilledForOwner", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2462
- const bgEnv = prepared.ownedEnv ?? this.deps.executionEnv;
2463
- const retainBgBySpec = spec.retainBackgroundProcesses === true && !ownedEnvDying;
2464
- if (bgEnv && hasBackgroundShell(bgEnv) && !retainBgBySpec) {
2465
- try {
2466
- const keepAlive = ownedEnvDying && bgEnv === prepared.ownedEnv
2467
- ? []
2468
- : [
2469
- ...defaultTaskRegistry.sessionResidentShellIds(prepared.sessionId, bgEnv),
2470
- ...defaultTaskRegistry.timeoutResidentShellIds(bgEnv),
2471
- ...defaultTaskRegistry.retainedShellIds(bgEnv),
2472
- ];
2473
- await settleTeardownLeg(() => sweepBackgroundShells(bgEnv, defaultTaskRegistry, keepAlive.length > 0 ? { except: keepAlive } : undefined), "sweepBackgroundShells", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2474
- }
2475
- catch (e) {
2476
- this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId });
2477
- }
2478
- }
2479
- defaultTaskRegistry.clearBackgroundForOwner({
2480
- owner: spec.taskId ?? prepared.sessionId,
2481
- scope: spec.principal ?? "default",
2482
- });
2483
- defaultTaskRegistry.abortBackgroundAgentsForOwner({
2484
- owner: spec.taskId ?? prepared.sessionId,
2485
- scope: spec.principal ?? "default",
2486
- }, { skipSessionScoped: true });
2487
- if (prepared.subagentRetain) {
2488
- await settleTeardownLeg(() => prepared.subagentRetain.disposeAll(), "subagentRetain.disposeAll", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2489
- }
2490
- await settleTeardownLeg(() => this.teardownOwnedEnv(prepared), "teardownOwnedEnv", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
2491
- }
2492
515
  }
2493
516
  async suggestNextPrompts(spec, prepared, result) {
2494
517
  const cfg = typeof spec.suggestNextPrompts === "object" ? spec.suggestNextPrompts : {};