@sema-agent/core 7.12.0 → 7.13.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 (57) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/core/ask-origin.d.ts +55 -0
  3. package/dist/core/ask-origin.js +21 -0
  4. package/dist/core/engine-notice.d.ts +18 -0
  5. package/dist/core/gate-lanes.d.ts +0 -40
  6. package/dist/core/gate-lanes.js +3 -17
  7. package/dist/core/governance-codes.d.ts +1 -1
  8. package/dist/core/governance-codes.js +2 -0
  9. package/dist/core/hooks.d.ts +1 -1
  10. package/dist/core/hooks.js +1 -1
  11. package/dist/core/runner/assemble-result.d.ts +45 -1
  12. package/dist/core/runner/assemble-result.js +4 -1
  13. package/dist/core/runner/compaction-seams.d.ts +42 -0
  14. package/dist/core/runner/compaction-seams.js +80 -0
  15. package/dist/core/runner/contracts.d.ts +44 -6
  16. package/dist/core/runner/denial-limit-arms.js +3 -2
  17. package/dist/core/runner/permission-rule-lanes.d.ts +2 -1
  18. package/dist/core/runner/permission-rule-lanes.js +2 -1
  19. package/dist/core/runner/prepare-policy-chain.js +3 -2
  20. package/dist/core/runner/prepare-protocol-tools.js +5 -0
  21. package/dist/core/runner/prepare-safety-scan.js +11 -16
  22. package/dist/core/runner/prepare-task.d.ts +9 -1
  23. package/dist/core/runner/prepare-task.js +6 -1
  24. package/dist/core/runner/resume-claim.d.ts +2 -2
  25. package/dist/core/runner/resume-preflight.d.ts +2 -2
  26. package/dist/core/runner/run-attachment-seats.d.ts +2 -2
  27. package/dist/core/runner/run-git-lane.d.ts +1 -1
  28. package/dist/core/runner/run-harness-handlers.js +2 -0
  29. package/dist/core/runner/run-identity-wiring.d.ts +7 -0
  30. package/dist/core/runner/run-identity-wiring.js +3 -1
  31. package/dist/core/runner/run-leg.d.ts +7 -5
  32. package/dist/core/runner/run-leg.js +256 -5
  33. package/dist/core/runner/run-notification-lane.d.ts +4 -3
  34. package/dist/core/runner/run-recovery-lanes.d.ts +4 -17
  35. package/dist/core/runner/run-recovery-lanes.js +5 -4
  36. package/dist/core/runner/run-settle-and-teardown.d.ts +14 -10
  37. package/dist/core/runner/run-settle-and-teardown.js +112 -4
  38. package/dist/core/runner/run-stop-and-final-verify.d.ts +2 -2
  39. package/dist/core/runner/run-terminal-adoption.d.ts +16 -16
  40. package/dist/core/runner/run-terminal-adoption.js +87 -7
  41. package/dist/core/runner/run-turn-boundary.js +5 -4
  42. package/dist/core/runner/runtask.d.ts +10 -90
  43. package/dist/core/runner/runtask.js +77 -596
  44. package/dist/core/runner/stream-lifecycle-verbs.js +10 -1
  45. package/dist/core/task-stream.d.ts +10 -1
  46. package/dist/core/tool-face.d.ts +8 -0
  47. package/dist/core/tool-face.js +1 -0
  48. package/dist/core/tool-policy.d.ts +5 -3
  49. package/dist/core/tool-registry.d.ts +11 -3
  50. package/dist/core/tool-registry.js +7 -1
  51. package/dist/core/tool-roster.d.ts +26 -0
  52. package/dist/core/tool-roster.js +38 -7
  53. package/dist/index.d.ts +3 -3
  54. package/dist/index.js +3 -3
  55. package/dist/server/http.js +2 -1
  56. package/package.json +1 -1
  57. package/test/export-surface.snapshot.json +11 -1
@@ -1,44 +1,29 @@
1
1
  import { deliverEngineNotice } from "../types.js";
2
- import { DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
2
+ import { uuidv7 } from "../../internal/harness.js";
3
3
  import { snapshotActorAssertion } from "../../internal/llm.js";
4
- import { CheckpointError, remainingBudgetMicroUsd, realApprovalOrgFact } from "../checkpoint-store.js";
5
- import { settleExecutionRecord } from "./execution-record.js";
6
- import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact } from "../auto-compaction.js";
7
- import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
4
+ import { CheckpointError } from "../checkpoint-store.js";
5
+ import { createRapidRefillState } from "../auto-compaction.js";
8
6
  import { boundInputHashOf } from "../canonical-json.js";
9
- import { modelCostToPricing } from "../pricing.js";
10
7
  import { emitTrace } from "../trace.js";
11
- import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
12
8
  import { emitTaskOutcome } from "../task-outcome.js";
13
- import { adjudicateDerivedRoute, authCarrierFingerprint, fallbackToPrimaryNotice, normalizeBaseUrl, sameRouteIdentity } from "../../brain/route-adjudicator.js";
9
+ import { authCarrierFingerprint, normalizeBaseUrl } from "../../brain/route-adjudicator.js";
14
10
  import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
15
- import { expandTiers, resolveTaskModel } from "../roles.js";
11
+ import { expandTiers } from "../roles.js";
16
12
  import { screenSwappableDeps } from "../swappable-deps.js";
17
13
  import { runSideQuery } from "../side-query.js";
18
- import { generatePromptSuggestions } from "./prompt-suggestions.js";
19
14
  import { PushQueue } from "../push-queue.js";
20
15
  import { TtlSessionStore } from "../session-store.js";
21
16
  import { errorCodeOf } from "./assemble-result.js";
22
- import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
23
17
  import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
24
- import { callFaceSeat, judgeParkedToolIdentity, rosterEntryNamed, toolCallFaceOf } from "../tool-roster.js";
25
- import { normalizePersistedRuleHit } from "../gate-lanes.js";
26
- import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
27
18
  import { SessionReadFileStates } from "./prepare-hands-readface.js";
28
- import { hasDestroy } from "../remote-env.js";
29
- import { formatHookFeedback, hookSeatExpiredError, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
30
- import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "../untrusted-text.js";
31
19
  import { RunnerSharedToolResultStore } from "../tool-result-store.js";
32
20
  import { PAUSE_REGISTRY } from "../pause-registry.js";
33
- import { refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.js";
34
- import { mintGateOutcome } from "./gate-exit.js";
21
+ import { toolPolicyNameSets } from "../tool-policy.js";
35
22
  import { PendingSessionNotifications } from "../task-notification.js";
36
23
  import { ToolDetachHub } from "../tool-detach.js";
37
24
  import { createRunState } from "./initial-run-state.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";
25
+ import { reconciledToolEndBody } from "./tool-end-body.js";
26
+ import { answerFaceForRedeemedCall } from "./decide-continuation.js";
42
27
  import { createTurnBoundary } from "./run-turn-boundary.js";
43
28
  import { createHarnessHandlers } from "./run-harness-handlers.js";
44
29
  import { resumeAdmission } from "./resume-admission.js";
@@ -69,11 +54,6 @@ import { runCompactionMachinery } from "./run-compaction-machinery.js";
69
54
  import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
70
55
  import { runRecoveryLanes } from "./run-recovery-lanes.js";
71
56
  import { runGitLane } from "./run-git-lane.js";
72
- const ORG_DISCLOSURE_MAX_CHARS = 600;
73
- const SUGGESTIONS_DEFAULT_COUNT = 3;
74
- const SUGGESTIONS_MAX_COUNT = 8;
75
- const SUGGESTIONS_TIMEOUT_MS = 30_000;
76
- const SUGGESTIONS_TRANSCRIPT_MESSAGES = 12;
77
57
  export class Runner {
78
58
  deps;
79
59
  sessions;
@@ -415,174 +395,81 @@ export class Runner {
415
395
  };
416
396
  }
417
397
  runLocked(input) {
418
- const { spec, queue, resume, internals, taskIdRef } = input;
398
+ const { spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, entryActor, entryTracer } = input;
419
399
  return runNotificationLane({
420
400
  spec, queue, resume, internals, taskIdRef,
421
401
  runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, pendingSessionNotifications: this.pendingSessionNotifications,
422
402
  prepareTask,
423
- next: (lane) => this.runSeatLanes(input, lane),
424
- });
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,
432
- });
433
- const rs = createRunState();
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 }),
438
- });
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 });
448
- const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
449
- for (const orphan of prepared.wakeRecovered) {
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() });
451
- emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
452
- }
453
- const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
454
- const batchArgs = postToolBatchHook ? new Map() : undefined;
455
- rs.turn.toolBatch = [];
456
- const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove } = createHarnessHandlers({
457
- prepared, stats, rs,
458
- deps: {
459
- spec, queue, internals, ident, parentToolCallId, subagentName,
460
- pushContent, emitCommitted, startedToolCallIds, toolStartAt, writeFamilyOf,
461
- toolLabels, postToolBatchHook, batchArgs,
462
- runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
463
- },
464
- });
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),
473
- },
474
- });
475
- const rapidRefill = createRapidRefillState();
476
- const drainManualCompact = (outcome) => {
477
- manualCompactRef.requested = false;
478
- for (const w of manualCompactRef.waiters.splice(0))
479
- w.resolve(outcome);
480
- };
481
- const onTurnBoundary = createTurnBoundary({
482
- prepared, stats, rs,
483
- deps: {
484
- spec, queue, loopLatch, manualCompactRef, todoToolMounted, taskToolsMounted,
485
- walltimeMonotonicDeadline,
486
- timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction,
487
- compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact,
488
- runnerHooks: {
489
- onError: this.deps.onError,
490
- seamCCompactionOptions: (p) => this.seamCCompactionOptions(p),
491
- compactionHookOptions: (s, sid, trig) => this.compactionHookOptions(s, sid, trig, prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal }),
492
- recordCompactionReuse: (p, c) => this.recordCompactionReuse(p, c),
493
- },
403
+ next: ({ prepared, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, modelCatalog, notificationLane, unsubscribeTaskNotifications }) => {
404
+ const { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats, undrainedUserAtEnd, userInputLostAtEnd } = runIdentityWiring({
405
+ spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef,
406
+ notificationLane, pendingSessionNotifications: this.pendingSessionNotifications, runner: this.depsSeat,
407
+ });
408
+ const rs = createRunState();
409
+ const { noteUnevaluablePriceTable } = runTelemetryAndBudgetSeats({ spec, prepared, resume, stats, taskIdRef, entryTracer, modelCatalog, runSourceTaskId, rs, runner: this.depsSeat, sessions: this.sessions });
410
+ return runAttachmentSeats({
411
+ spec, prepared, resume, rs, runner: this.depsSeat,
412
+ next: () => {
413
+ const { todoToolMounted, taskToolsMounted, writeFamilyOf, toolStartAt, startedToolCallIds } = runToolMountFacts({ spec, prepared, rs });
414
+ const { observeReasoningWireFacts, reasoningResolution } = runReasoningSeat({ prepared, rs, taskIdRef });
415
+ const { effectiveTimeoutMs, walltimeMonotonicDeadline, timeout, pushContent, ownCommittedTailRef, emitCommitted } = runClockAndContent({ spec, prepared, queue, internals, rs, ident, parentToolCallId, startedToolCallIds });
416
+ const { subagentName, withBrainSinks } = runBrainSinks({ queue, internals, rs, ident, parentToolCallId, observeReasoningWireFacts });
417
+ const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
418
+ for (const orphan of prepared.wakeRecovered) {
419
+ 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() });
420
+ emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
421
+ }
422
+ const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
423
+ const batchArgs = postToolBatchHook ? new Map() : undefined;
424
+ rs.turn.toolBatch = [];
425
+ const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove } = createHarnessHandlers({
426
+ prepared, stats, rs,
427
+ deps: {
428
+ spec, queue, internals, ident, parentToolCallId, subagentName,
429
+ pushContent, emitCommitted, startedToolCallIds, toolStartAt, writeFamilyOf,
430
+ toolLabels, postToolBatchHook, batchArgs,
431
+ runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
432
+ },
433
+ });
434
+ const { compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions } = runCompactionMachinery({ spec, prepared, rs, stats, noteUnevaluablePriceTable, runner: this.depsSeat });
435
+ runStopAndFinalVerify({ spec, prepared, queue, rs, stats, ident, walltimeMonotonicDeadline, runner: this.depsSeat });
436
+ runRecoveryLanes({ spec, prepared, queue, rs, ident, compactionBrain, compactionBreaker, windowSafetyOptions, runner: this.depsSeat });
437
+ const rapidRefill = createRapidRefillState();
438
+ const drainManualCompact = (outcome) => {
439
+ manualCompactRef.requested = false;
440
+ for (const w of manualCompactRef.waiters.splice(0))
441
+ w.resolve(outcome);
442
+ };
443
+ const onTurnBoundary = createTurnBoundary({
444
+ prepared, stats, rs,
445
+ deps: {
446
+ spec, queue, loopLatch, manualCompactRef, todoToolMounted, taskToolsMounted,
447
+ walltimeMonotonicDeadline,
448
+ timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction,
449
+ compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact,
450
+ runnerHooks: { onError: this.deps.onError },
451
+ runner: this.depsSeat,
452
+ },
453
+ });
454
+ const { flushGitMirror, unsubGitRetry, unsubBoundary, unsub } = runGitLane({ prepared, queue, rs, ident, onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, onTurnBoundary, runner: this.depsSeat });
455
+ return runLeg({
456
+ spec, queue, prepared, resume, rs, entryActor, effectiveTimeoutMs, timeout, pushContent, ident, emitCommitted, writeFamilyOf, startedToolCallIds,
457
+ announceWorkspaceMove, withBrainSinks, flushGitMirror, loopLatch, notificationLane, unsubscribeTaskNotifications, unsubGitRetry, unsubBoundary, unsub,
458
+ runner: this.depsSeat,
459
+ next: ({ final, threw, abortedLive, userInterruptedLive, strandedHumanAnswers }) => runTerminalAdoption({
460
+ spec, queue, prepared, rs, stats, ident, emitCommitted, startedToolCallIds, toolLabels, timeout, compactionBrain, compactionBreaker, windowSafetyOptions,
461
+ undrainedUserAtEnd, userInputLostAtEnd, final, threw, abortedLive, userInterruptedLive, runner: this.depsSeat, sessions: this.sessions,
462
+ next: ({ threw, comp, answerSettled }) => runSettleAndTeardown({
463
+ spec, queue, prepared, resume, internals, rs, stats, ident, parentToolCallId, subagentName, loopLatch, timeout, ownCommittedTailRef,
464
+ emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp,
465
+ setResult, onSuggestions, onSuspend, runner: this.depsSeat, sessions: this.sessions,
466
+ }),
467
+ }),
468
+ });
469
+ },
470
+ });
494
471
  },
495
472
  });
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
- }),
514
- });
515
- }
516
- async suggestNextPrompts(spec, prepared, result) {
517
- const cfg = typeof spec.suggestNextPrompts === "object" ? spec.suggestNextPrompts : {};
518
- const count = cfg.count && cfg.count > 0 ? Math.min(cfg.count, SUGGESTIONS_MAX_COUNT) : SUGGESTIONS_DEFAULT_COUNT;
519
- const ac = new AbortController();
520
- let timer;
521
- const timeout = new Promise((resolve) => {
522
- timer = setTimeout(() => {
523
- ac.abort();
524
- resolve([]);
525
- }, SUGGESTIONS_TIMEOUT_MS);
526
- });
527
- const work = (async () => {
528
- let model = prepared.model;
529
- let thinking = prepared.thinking;
530
- try {
531
- const resolved = resolveTaskModel({ modelRole: cfg.role ?? "summarize", roles: spec.roles }, this.deps);
532
- model = resolved.model;
533
- thinking = resolved.thinking;
534
- }
535
- catch (roleErr) {
536
- if (cfg.role !== undefined) {
537
- let asked;
538
- try {
539
- asked = String(cfg.role);
540
- }
541
- catch {
542
- asked = `<unrenderable ${typeof cfg.role}>`;
543
- }
544
- try {
545
- this.deps.onError?.(new Error(`suggestNextPrompts.role ${JSON.stringify(asked.length > 80 ? `${asked.slice(0, 80)}…` : asked)} did not resolve to a model — the prompt-suggestion pass ran on the task's own model (${prepared.model.id}) instead. Configure that role in RunnerDeps.roles / TaskSpec.roles, or omit the field to use the "summarize" role.`, { cause: roleErr }), { phase: "suggestions", sessionId: prepared.sessionId });
546
- }
547
- catch {
548
- }
549
- }
550
- }
551
- if (!sameRouteIdentity(model, prepared.model)) {
552
- const verdict = await adjudicateDerivedRoute({ brain: this.deps.brain, model, getApiKeyAndHeaders: spec.getApiKeyAndHeaders });
553
- if (verdict !== undefined && !verdict.ok) {
554
- deliverEngineNotice(this.deps.onNotice, fallbackToPrimaryNotice({ seat: "prompt-suggestions", from: model.id, to: prepared.model.id, verdict, sessionId: prepared.sessionId, runId: prepared.runId }));
555
- model = prepared.model;
556
- thinking = prepared.thinking;
557
- }
558
- }
559
- const pricing = this.deps.pricing?.[model.id] ?? modelCostToPricing(model.cost);
560
- const ctx = await prepared.session.buildContext();
561
- const transcript = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m)).slice(-SUGGESTIONS_TRANSCRIPT_MESSAGES);
562
- const out = await generatePromptSuggestions({ brain: this.deps.brain, model, pricing, thinking, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, signal: ac.signal }, transcript, count);
563
- if (out.tokens > 0 || out.costMicroUsd > 0) {
564
- result.stats.suggestions = { tokens: out.tokens, costMicroUsd: out.costMicroUsd };
565
- if (result.stats.costBreakdown) {
566
- result.stats.costBreakdown.suggestionsMicroUsd = out.costMicroUsd;
567
- }
568
- }
569
- return out.suggestions;
570
- })();
571
- work.catch(() => { });
572
- try {
573
- return await Promise.race([work, timeout]);
574
- }
575
- catch (err) {
576
- try {
577
- this.deps.onError?.(err, { phase: "suggestions", sessionId: prepared.sessionId });
578
- }
579
- catch {
580
- }
581
- return [];
582
- }
583
- finally {
584
- clearTimeout(timer);
585
- }
586
473
  }
587
474
  async runTask(spec, internals) {
588
475
  const stream = this.runTaskStream(spec, undefined, internals);
@@ -683,412 +570,6 @@ export class Runner {
683
570
  }
684
571
  return this.runTaskStream(spec, { cp, outcome: outcomeForStore, onEnvRestoreFailed, recordExecutionOutcome, ...(wakeMessage !== undefined ? { wakeMessage } : {}), ...(wakeHookContext !== undefined ? { wakeMessageHookContext: wakeHookContext } : {}) }, resumeInternals);
685
572
  }
686
- async applyResumeDecision(prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
687
- const { pendingAction } = resume.cp;
688
- if (resume.outcome.gate === "dry_run_review")
689
- return;
690
- if (resume.outcome.gate === "plan_review")
691
- return;
692
- if (pendingAction.kind !== "tool_approval")
693
- return;
694
- if (resume.outcome.gate === "policy_ask" &&
695
- resume.outcome.decision === "allow" &&
696
- !prepared.tools.some((t) => t.name === pendingAction.toolName)) {
697
- const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume — refusing to continue as if it ran; the checkpoint is reopened for a retry with the tool present`);
698
- e.code = "resume.tool_unavailable";
699
- throw e;
700
- }
701
- const liveRoster = prepared.toolRosterDeltas.current;
702
- if (pendingAction.kind === "tool_approval" && liveRoster !== undefined && judgeParkedToolIdentity(pendingAction, rosterEntryNamed(liveRoster, pendingAction.toolName)) === "mismatch") {
703
- const e = new Error(`the approved tool "${pendingAction.toolName}" on this leg is not the tool the approval was minted against (its contract / shape / revision differ) — refusing to execute a replacement wearing the name; the checkpoint is reopened for a retry with the original tool present`);
704
- e.code = "resume.tool_contract_mismatch";
705
- throw e;
706
- }
707
- const completed = new Set(pendingAction.completedCallIds);
708
- const deferredIds = pendingAction.batchToolCallIds.filter((id) => id !== pendingAction.toolCallId && !completed.has(id));
709
- const names = new Map();
710
- const deferredArgs = new Map();
711
- if (deferredIds.length > 0) {
712
- const { messages } = await prepared.session.buildContext();
713
- for (const m of messages) {
714
- if (m.role === "assistant") {
715
- for (const c of m.content) {
716
- if (c.type === "toolCall") {
717
- names.set(c.id, c.name);
718
- deferredArgs.set(c.id, c.arguments);
719
- }
720
- }
721
- }
722
- }
723
- }
724
- for (const id of pendingAction.batchToolCallIds) {
725
- if (id === pendingAction.toolCallId) {
726
- await this.resolvePendingCall(prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart);
727
- }
728
- else if (!completed.has(id)) {
729
- const name = names.get(id) ?? "unknown";
730
- const displayLabel = (() => {
731
- const l = prepared.tools.find((t) => t.name === name)?.label;
732
- return l !== undefined && l !== name ? { label: l } : {};
733
- })();
734
- emit({ type: "tool_start", toolCallId: id, toolName: name, ...displayLabel, args: deferredArgs.get(id) ?? {} });
735
- emit({ type: "tool_end", toolCallId: id, toolName: name, ...displayLabel, isError: true, ...toolEndBodyFrom({ content: formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark) }, true, prepared.structuredProjector) });
736
- const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark), true));
737
- emitCommitted(eid, "toolResult", id);
738
- }
739
- }
740
- }
741
- async resolvePendingCall(prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
742
- const { pendingAction } = resume.cp;
743
- const { outcome } = resume;
744
- if (pendingAction.kind !== "tool_approval" || outcome.gate !== "policy_ask")
745
- return;
746
- const decidedGate = outcome.gateOutcome;
747
- if (decidedGate === undefined) {
748
- throw new CheckpointError("checkpoint.invalid_outcome", "pending action reached the resolver with no settlement record on its decision (gateOutcome) — refusing to execute or settle a decision that carries no record");
749
- }
750
- const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
751
- const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
752
- emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs });
753
- const settleEnd = async (isError, result, gate = decidedGate) => {
754
- emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError, prepared.structuredProjector, gate) });
755
- const word = await resume.recordExecutionOutcome(gate);
756
- settleExecutionRecord(this.deps.onNotice, word, { toolName: pendingAction.toolName, gate, sessionId: prepared.sessionId, runId: prepared.runId, scope: resume.cp.scope, ...(resume.cp.checkpointId !== undefined ? { checkpointId: resume.cp.checkpointId } : {}) });
757
- };
758
- const vetoed = (layer) => mintGateOutcome({
759
- deniedBy: layer,
760
- ...(outcome.gateOutcome?.settlement !== undefined && outcome.gateOutcome.origin !== undefined ? { settled: { settlement: outcome.gateOutcome.settlement, origin: outcome.gateOutcome.origin } } : {}),
761
- });
762
- if (outcome.decision === "deny") {
763
- const defaultDenial = outcome.gateOutcome?.settlement?.kind === "park_sla_expired"
764
- ? `No one answered the approval request for the pending tool call "${pendingAction.toolName}" — the approval window elapsed with no answer, so it was not executed.`
765
- : `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
766
- const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason, REVIEWER_NOTE_MAX_BODY) : defaultDenial;
767
- await settleEnd(true, { content: formatHookFeedback(reason, prepared.reminderMark) });
768
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason, prepared.reminderMark), true));
769
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
770
- return;
771
- }
772
- if (outcome.decision !== "allow") {
773
- throw new CheckpointError("checkpoint.invalid_outcome", `pending action reached the resolver with decision "${String(outcome.decision)}" — only "allow" executes and only "deny" injects a denial; refusing to execute`);
774
- }
775
- const beltBudget = {
776
- resourceRemainingMicroUsd: remainingBudgetMicroUsd(prepared.resourceLedger),
777
- resourceSpentMicroUsd: prepared.resourceLedger?.spentMicroUsd ?? 0,
778
- suspendCount: resume.cp.suspendCount ?? 0,
779
- };
780
- if (outcome.decision === "allow" && outcome.updatedInput !== undefined && prepared.basePolicyForResumeEdit) {
781
- const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check({
782
- toolName: pendingAction.toolName,
783
- args: resolvedArgs,
784
- toolCallId: pendingAction.toolCallId,
785
- budget: beltBudget,
786
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
787
- ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
788
- }, prepared.abortController.signal));
789
- const beltUnsafe = rechecked.action === "deny" ||
790
- (rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs));
791
- if (beltUnsafe) {
792
- const editedDenial = formatHookFeedback(`The approver EDITED this call's input; the edited call is refused by the deployment's tool policy and was not executed${rechecked.action === "deny" && rechecked.message ? `: ${rechecked.message}` : " (the policy rewrote the edit to a value that was not approved)"}.`, prepared.reminderMark);
793
- await settleEnd(true, { content: editedDenial }, vetoed("policy"));
794
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
795
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
796
- return;
797
- }
798
- }
799
- const gateRealApproval = resume.cp.gate.kind === "irreversible_ask" ? resume.cp.gate.realApproval : undefined;
800
- const gateOrgGoverned = realApprovalOrgFact(gateRealApproval) !== undefined;
801
- if (gateOrgGoverned && prepared.permissionRuleOrg === undefined) {
802
- const unwiredDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: its approval was minted under organization governance (${gateRealApproval.origin}), and this worker has no org adjudication wiring — a governed approval may only be redeemed where governance can be enforced. This approval is spent; re-issue the call on an org-wired worker.`, prepared.reminderMark);
803
- await settleEnd(true, { content: unwiredDenial }, vetoed("org"));
804
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, unwiredDenial, true));
805
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
806
- return;
807
- }
808
- if (prepared.permissionRuleOrg !== undefined) {
809
- const orgVerdict = prepared.permissionRuleOrg
810
- .adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId })
811
- .catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
812
- let orgWaitCancelled = false;
813
- const org = await settleOrgVerdictWithin(orgVerdict, { status: "unavailable", disclosures: [`the org adjudication face did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms (or the task ended first)`] }, {
814
- signal: prepared.abortController.signal,
815
- timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS,
816
- onFallback: (cause) => {
817
- orgWaitCancelled = cause === "aborted";
818
- },
819
- });
820
- const blocked = org.status === "unavailable"
821
- ? realApprovalOrgFact(gateRealApproval) === "unavailable"
822
- ? undefined
823
- : orgWaitCancelled
824
- ? "this deployment is org-governed and the task was cancelled before the organization policy snapshot could be adjudicated"
825
- : "this deployment is org-governed and cannot currently adjudicate against an organization policy snapshot"
826
- : org.verdict?.behavior === "deny"
827
- ? `an organization policy rule (${org.verdict.rule}) denies it`
828
- : undefined;
829
- if (blocked !== undefined) {
830
- const orgDisclosures = org.status === "unavailable" && org.disclosures.length > 0
831
- ? ` Governance disclosures: ${inlineUntrusted(org.disclosures.join("; "), ORG_DISCLOSURE_MAX_CHARS)}.`
832
- : "";
833
- const orgDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${blocked}.${orgDisclosures} This approval is spent — the call has to be re-issued and approved again once organization policy permits it.`, prepared.reminderMark);
834
- await settleEnd(true, { content: orgDenial }, vetoed("org"));
835
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, orgDenial, true));
836
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
837
- return;
838
- }
839
- }
840
- if (prepared.denyNarrowingPolicy) {
841
- const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check({
842
- toolName: pendingAction.toolName,
843
- args: resolvedArgs,
844
- toolCallId: pendingAction.toolCallId,
845
- ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
846
- budget: beltBudget,
847
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
848
- }, prepared.abortController.signal));
849
- if (narrowed.action === "deny") {
850
- const narrowedDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`, prepared.reminderMark);
851
- await settleEnd(true, { content: narrowedDenial }, vetoed("policy"));
852
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
853
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
854
- return;
855
- }
856
- }
857
- if (prepared.persistedRuleLane !== undefined) {
858
- const unreadable = { unreadable: true };
859
- const read = await settleOrgVerdictWithin(Promise.resolve()
860
- .then(() => prepared.persistedRuleLane.adjudicate({
861
- toolName: pendingAction.toolName,
862
- args: resolvedArgs,
863
- toolCallId: pendingAction.toolCallId,
864
- ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
865
- budget: beltBudget,
866
- ...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
867
- }))
868
- .then((answer) => normalizePersistedRuleHit(answer)), unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
869
- const tightened = applyPersistedTightening({ action: "allow" }, read);
870
- if (tightened.tightened === "deny" || tightened.tightened === "unreadable") {
871
- const why = tightened.tightened === "deny"
872
- ? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
873
- : "your persisted permission rules could not be read on this leg";
874
- const ruleDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${why}. This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
875
- await settleEnd(true, { content: ruleDenial }, vetoed("persisted_rule"));
876
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, ruleDenial, true));
877
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
878
- return;
879
- }
880
- }
881
- const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
882
- if (!tool) {
883
- const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
884
- e.code = "resume.tool_unavailable";
885
- throw e;
886
- }
887
- const args = resolvedArgs;
888
- onExecuteStart?.(pendingAction.toolCallId);
889
- prepared.suspendProgressRef.executedApproved = true;
890
- let res;
891
- try {
892
- res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
893
- }
894
- catch (err) {
895
- const execError = `Error: ${err instanceof Error ? err.message : String(err)}`;
896
- const marks = (() => {
897
- if (err === null || typeof err !== "object")
898
- return undefined;
899
- const src = err;
900
- let d;
901
- if (src.details !== null && typeof src.details === "object" && !Array.isArray(src.details)) {
902
- d = { ...src.details };
903
- }
904
- if (typeof src.errorKind === "string")
905
- d = { ...(d ?? {}), errorKind: src.errorKind };
906
- return d;
907
- })();
908
- await settleEnd(true, { content: execError, ...(marks !== undefined ? { details: marks } : {}) });
909
- const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, execError, true));
910
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
911
- return;
912
- }
913
- const executedIsError = res.isError === true;
914
- await settleEnd(executedIsError, res);
915
- onResolvedToolSuccess?.(pendingAction.toolName, executedIsError ? undefined : res.details);
916
- const eid = await prepared.session.appendMessage({
917
- role: "toolResult",
918
- toolCallId: pendingAction.toolCallId,
919
- toolName: pendingAction.toolName,
920
- content: res.content,
921
- details: res.details,
922
- isError: executedIsError,
923
- timestamp: Date.now(),
924
- });
925
- emitCommitted(eid, "toolResult", pendingAction.toolCallId);
926
- }
927
- seamCCompactionOptions(prepared) {
928
- if (!this.deps.summaryProvider)
929
- return undefined;
930
- return {
931
- summaryProvider: this.deps.summaryProvider,
932
- onCompaction: this.deps.onCompaction,
933
- maxConsecutiveProviderReuse: this.deps.maxConsecutiveProviderReuse,
934
- consecutiveProviderReuse: prepared.compactionReuseRef.consecutive,
935
- };
936
- }
937
- compactionHookOptions(spec, sessionId, trigger, identity, seatBound) {
938
- const hooks = spec.hooks ?? this.deps.hooks;
939
- const pre = hooks?.preCompact;
940
- const post = hooks?.postCompact;
941
- const seatMs = seatBound?.timeoutMs ?? resolveHookTimeoutMs(hooks?.timeoutMs);
942
- const seatSignal = seatBound?.signal;
943
- const withIdentity = (ctx) => identity !== undefined ? { ...ctx, identity } : ctx;
944
- return {
945
- trigger,
946
- ...(pre
947
- ? {
948
- preCompact: async (rawCtx) => {
949
- const ctx = withIdentity(rawCtx);
950
- const report = (err) => {
951
- try {
952
- this.deps.onError?.(err, { phase: "hook", sessionId });
953
- }
954
- catch {
955
- }
956
- };
957
- try {
958
- const seat = await runHookSeat("preCompact", { timeoutMs: seatMs, ...(seatSignal !== undefined ? { signal: seatSignal } : {}) }, (sig) => pre.call(hooks, { ...ctx, signal: sig }));
959
- if (seat.expired) {
960
- report(hookSeatExpiredError("preCompact", seatMs, seat.cause, `the "${ctx.trigger}" compaction PROCEEDED unblocked and with no additional instructions`));
961
- return undefined;
962
- }
963
- const r = seat.value;
964
- if (r?.block && ctx.trigger === "forced") {
965
- report(new Error(`a preCompact callback blocked a "forced" compaction — ignored (PTL/trim-pressure compaction is not optional): ${r.block}`));
966
- }
967
- return r;
968
- }
969
- catch (err) {
970
- report(err instanceof Error ? err : new Error(String(err)));
971
- return undefined;
972
- }
973
- },
974
- }
975
- : {}),
976
- ...(post
977
- ? {
978
- postCompact: async (rawCtx) => {
979
- try {
980
- const seat = await runHookSeat("postCompact", { timeoutMs: seatMs, ...(seatSignal !== undefined ? { signal: seatSignal } : {}) }, (sig) => post.call(hooks, { ...withIdentity(rawCtx), signal: sig }));
981
- if (seat.expired) {
982
- try {
983
- this.deps.onError?.(hookSeatExpiredError("postCompact", seatMs, seat.cause, "the observation was abandoned; the compaction that already landed is unchanged"), { phase: "hook", sessionId });
984
- }
985
- catch {
986
- }
987
- }
988
- }
989
- catch (err) {
990
- try {
991
- this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
992
- }
993
- catch {
994
- }
995
- }
996
- },
997
- }
998
- : {}),
999
- };
1000
- }
1001
- recordCompactionReuse(prepared, comp) {
1002
- if (!this.deps.summaryProvider || !comp.compacted)
1003
- return;
1004
- prepared.compactionReuseRef.consecutive = comp.reused ? prepared.compactionReuseRef.consecutive + 1 : 0;
1005
- }
1006
- async finish(spec, prepared, opts) {
1007
- let comp;
1008
- if (!opts?.skipCompaction) {
1009
- try {
1010
- const finishComp = await maybeCompact({
1011
- session: prepared.session,
1012
- epochDeclaredSections: prepared.epochDeclaredSections,
1013
- ...centerAdoptionOption(prepared),
1014
- model: prepared.model,
1015
- compactionModel: prepared.compModel,
1016
- ...forkContextOption(prepared, false),
1017
- brain: opts?.brain ?? this.deps.brain,
1018
- getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
1019
- thinking: prepared.thinking,
1020
- settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
1021
- customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
1022
- minTokens: opts?.minTokens,
1023
- overheadTokens: prepared.promptOverheadTokens,
1024
- ...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
1025
- onInputTruncated: emitInputTruncated(opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId),
1026
- ...(opts?.windowSafety ?? {}),
1027
- workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
1028
- ...contextInstructionFilesOption(prepared),
1029
- ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
1030
- ...this.seamCCompactionOptions(prepared),
1031
- ...gitRestateOption(prepared),
1032
- ...this.compactionHookOptions(spec, prepared.sessionId, "auto", prepared.hookIdentity, {
1033
- timeoutMs: prepared.hookTimeoutMs,
1034
- ...(opts?.cancelled === true ? { signal: prepared.abortController.signal } : {}),
1035
- }),
1036
- });
1037
- this.recordCompactionReuse(prepared, finishComp);
1038
- if (finishComp.unevaluableWindow) {
1039
- discloseUnevaluableWindow(finishComp.unevaluableWindow, prepared, opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId, this.deps.onError);
1040
- }
1041
- comp = finishComp;
1042
- }
1043
- catch (err) {
1044
- const failMsg = String(err instanceof Error ? err.message : err);
1045
- const failReason = failMsg.length > 512 ? `${failMsg.slice(0, 512)}…` : failMsg;
1046
- emitTrace(opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, () => ({
1047
- kind: "compaction.failed",
1048
- version: 1,
1049
- taskId: opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId,
1050
- trigger: "auto",
1051
- reason: failReason,
1052
- ts: Date.now(),
1053
- }));
1054
- opts?.onCompactionFailed?.(failReason);
1055
- this.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
1056
- }
1057
- }
1058
- try {
1059
- await this.sessions.touch(prepared.sessionId);
1060
- }
1061
- catch (err) {
1062
- this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
1063
- }
1064
- try {
1065
- await prepared.mcp.dispose();
1066
- }
1067
- catch (err) {
1068
- this.deps.onError?.(err, { phase: "mcp", sessionId: prepared.sessionId });
1069
- }
1070
- if (prepared.a2a !== undefined) {
1071
- try {
1072
- await prepared.a2a.dispose();
1073
- }
1074
- catch (err) {
1075
- this.deps.onError?.(err, { phase: "a2a", sessionId: prepared.sessionId });
1076
- }
1077
- }
1078
- return comp;
1079
- }
1080
- async teardownOwnedEnv(prepared) {
1081
- if (prepared.ownedEnv &&
1082
- hasDestroy(prepared.ownedEnv) &&
1083
- prepared.pausedRef.current === undefined) {
1084
- try {
1085
- await prepared.ownedEnv.destroy();
1086
- }
1087
- catch (err) {
1088
- this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
1089
- }
1090
- }
1091
- }
1092
573
  }
1093
574
  export function runTask(spec, deps) {
1094
575
  return new Runner(deps).runTask(spec);