@sema-agent/core 7.11.2 → 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.
- package/CHANGELOG.md +86 -13
- package/dist/core/ask-origin.d.ts +55 -0
- package/dist/core/ask-origin.js +21 -0
- package/dist/core/auto-mode-arming.d.ts +10 -14
- package/dist/core/auto-mode-arming.js +3 -9
- package/dist/core/auto-mode-defaults.d.ts +0 -2
- package/dist/core/auto-mode-defaults.js +0 -1
- package/dist/core/auto-mode-rebuild.d.ts +6 -13
- package/dist/core/auto-mode-rebuild.js +0 -2
- package/dist/core/auto-mode.d.ts +30 -89
- package/dist/core/auto-mode.js +12 -59
- package/dist/core/checkpoint-store.d.ts +1 -3
- package/dist/core/engine-notice.d.ts +18 -0
- package/dist/core/gate-fold.js +1 -9
- package/dist/core/gate-lanes.d.ts +0 -40
- package/dist/core/gate-lanes.js +18 -26
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +7 -1
- package/dist/core/hooks.js +1 -1
- package/dist/core/runner/assemble-result.d.ts +45 -1
- package/dist/core/runner/assemble-result.js +4 -1
- package/dist/core/runner/compaction-seams.d.ts +42 -0
- package/dist/core/runner/compaction-seams.js +80 -0
- package/dist/core/runner/contracts.d.ts +85 -15
- package/dist/core/runner/denial-limit-arms.d.ts +10 -13
- package/dist/core/runner/denial-limit-arms.js +12 -9
- package/dist/core/runner/gate-exit.js +9 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +2 -1
- package/dist/core/runner/permission-rule-lanes.js +2 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +1 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +0 -5
- package/dist/core/runner/prepare-policy-chain.js +3 -2
- package/dist/core/runner/prepare-protocol-tools.js +5 -0
- package/dist/core/runner/prepare-safety-scan.js +11 -16
- package/dist/core/runner/prepare-suspend-saga.d.ts +0 -2
- package/dist/core/runner/prepare-suspend-saga.js +2 -10
- package/dist/core/runner/prepare-task.d.ts +9 -1
- package/dist/core/runner/prepare-task.js +7 -2
- package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -1
- package/dist/core/runner/prepare-wiring-manifest.js +1 -8
- package/dist/core/runner/resume-claim.d.ts +2 -2
- package/dist/core/runner/resume-preflight.d.ts +2 -2
- package/dist/core/runner/run-attachment-seats.d.ts +6 -4
- package/dist/core/runner/run-attachment-seats.js +2 -2
- package/dist/core/runner/run-git-lane.d.ts +1 -1
- package/dist/core/runner/run-harness-handlers.js +2 -0
- package/dist/core/runner/run-identity-wiring.d.ts +21 -33
- package/dist/core/runner/run-identity-wiring.js +6 -3
- package/dist/core/runner/run-leg.d.ts +108 -0
- package/dist/core/runner/run-leg.js +713 -0
- package/dist/core/runner/run-notification-lane.d.ts +56 -0
- package/dist/core/runner/run-notification-lane.js +128 -0
- package/dist/core/runner/run-reasoning-seat.d.ts +5 -5
- package/dist/core/runner/run-reasoning-seat.js +7 -7
- package/dist/core/runner/run-recovery-lanes.d.ts +4 -17
- package/dist/core/runner/run-recovery-lanes.js +5 -4
- package/dist/core/runner/run-settle-and-teardown.d.ts +113 -0
- package/dist/core/runner/run-settle-and-teardown.js +432 -0
- package/dist/core/runner/run-stop-and-final-verify.d.ts +2 -2
- package/dist/core/runner/run-terminal-adoption.d.ts +99 -0
- package/dist/core/runner/run-terminal-adoption.js +200 -0
- package/dist/core/runner/run-turn-boundary.js +5 -4
- package/dist/core/runner/runtask.d.ts +10 -79
- package/dist/core/runner/runtask.js +83 -1557
- package/dist/core/runner/stream-lifecycle-verbs.js +10 -1
- package/dist/core/runner-deps.d.ts +4 -14
- package/dist/core/store-contracts/workflow-journal-store-contract.d.ts +7 -0
- package/dist/core/store-contracts/workflow-journal-store-contract.js +85 -0
- package/dist/core/task-stream.d.ts +10 -1
- package/dist/core/tool-face.d.ts +8 -0
- package/dist/core/tool-face.js +1 -0
- package/dist/core/tool-policy.d.ts +42 -96
- package/dist/core/tool-policy.js +1 -11
- package/dist/core/tool-registry.d.ts +11 -3
- package/dist/core/tool-registry.js +7 -1
- package/dist/core/tool-roster.d.ts +26 -0
- package/dist/core/tool-roster.js +38 -7
- package/dist/core/trace.d.ts +6 -7
- package/dist/core/wiring-manifest.d.ts +5 -22
- package/dist/core/wiring-manifest.js +3 -11
- package/dist/core/workflow-journal-store.d.ts +35 -4
- package/dist/core/workflow-journal-store.js +19 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +6 -5
- package/dist/orchestration/workflow.js +2 -0
- package/dist/server/http.js +2 -1
- package/dist/stores/file/workflow-journal-store.d.ts +7 -10
- package/dist/stores/file/workflow-journal-store.js +2 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +21 -11
|
@@ -1,63 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { AgentHarness, DEFAULT_COMPACTION_SETTINGS, isSyntheticApiErrorMessage, uuidv7 } from "../../internal/harness.js";
|
|
1
|
+
import { deliverEngineNotice } from "../types.js";
|
|
2
|
+
import { uuidv7 } from "../../internal/harness.js";
|
|
4
3
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
5
|
-
import { CheckpointError
|
|
6
|
-
import {
|
|
7
|
-
import { settleExecutionRecord } from "./execution-record.js";
|
|
8
|
-
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
9
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, createRapidRefillState, maybeCompact } from "../auto-compaction.js";
|
|
10
|
-
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
|
|
4
|
+
import { CheckpointError } from "../checkpoint-store.js";
|
|
5
|
+
import { createRapidRefillState } from "../auto-compaction.js";
|
|
11
6
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
12
|
-
import { computeCostMicroUsd, modelCostToPricing } from "../pricing.js";
|
|
13
7
|
import { emitTrace } from "../trace.js";
|
|
14
|
-
import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
15
8
|
import { emitTaskOutcome } from "../task-outcome.js";
|
|
16
|
-
import {
|
|
17
|
-
import { redactThenCut } from "../../agents/subagent-steps.js";
|
|
18
|
-
import { adjudicateDerivedRoute, authCarrierFingerprint, fallbackToPrimaryNotice, normalizeBaseUrl, sameRouteIdentity } from "../../brain/route-adjudicator.js";
|
|
9
|
+
import { authCarrierFingerprint, normalizeBaseUrl } from "../../brain/route-adjudicator.js";
|
|
19
10
|
import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
|
|
20
|
-
import { expandTiers
|
|
11
|
+
import { expandTiers } from "../roles.js";
|
|
21
12
|
import { screenSwappableDeps } from "../swappable-deps.js";
|
|
22
|
-
import { AutoModeBreakerLedger } from "../auto-mode.js";
|
|
23
13
|
import { runSideQuery } from "../side-query.js";
|
|
24
|
-
import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
25
14
|
import { PushQueue } from "../push-queue.js";
|
|
26
15
|
import { TtlSessionStore } from "../session-store.js";
|
|
27
|
-
import {
|
|
28
|
-
import { assembleResult, errorCodeOf } from "./assemble-result.js";
|
|
29
|
-
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
30
|
-
import { attachmentEnvelopeTags, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingDelta, renderOrphanedBackgroundTasks, renderSkillsListingDelta, stampWriteAnchor } from "./turn-attachments.js";
|
|
31
|
-
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
16
|
+
import { errorCodeOf } from "./assemble-result.js";
|
|
32
17
|
import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
|
|
33
|
-
import { drainForwardedFramesBeforeDone, forwardsSubagentEvents } from "./prepare-run-refs.js";
|
|
34
|
-
import { callFaceSeat, judgeParkedToolIdentity, rosterEntryNamed, toolCallFaceOf } from "../tool-roster.js";
|
|
35
|
-
import { normalizePersistedRuleHit } from "../gate-lanes.js";
|
|
36
|
-
import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
|
|
37
18
|
import { SessionReadFileStates } from "./prepare-hands-readface.js";
|
|
38
|
-
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
39
|
-
import { hasDestroy, isIsolated } from "../remote-env.js";
|
|
40
|
-
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
41
|
-
import { formatHookFeedback, hookSeatExpiredError, resolveHookTimeoutMs, runHookSeat } from "../hooks.js";
|
|
42
|
-
import { buildHumanInputEvent, projectHumanInput } from "../human-input-projection.js";
|
|
43
|
-
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
44
|
-
import { appendInterruptionMarker, reconcileInterruptedSession } from "../session-reconcile.js";
|
|
45
19
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
46
20
|
import { PAUSE_REGISTRY } from "../pause-registry.js";
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import { defaultTaskRegistry } from "../task-registry.js";
|
|
50
|
-
import { isDelegatedAgentTerminal, isTerminalTaskNotification, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
|
|
21
|
+
import { toolPolicyNameSets } from "../tool-policy.js";
|
|
22
|
+
import { PendingSessionNotifications } from "../task-notification.js";
|
|
51
23
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
52
|
-
import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
|
|
53
24
|
import { createRunState } from "./initial-run-state.js";
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import { answerFaceForRedeemedCall, deepJsonEqual, DEFERRED_REISSUE, pendingContentAskCallId, resumeContinuation, resumeDecisionWasNegative } from "./decide-continuation.js";
|
|
57
|
-
import { awaitChargeWithSlowDisclosure, DEFAULT_PRECALL_OUTPUT_TOKENS, discloseUnevaluableWindow, platformLimitTerminal, TIMER_LATENESS_REPORT_MS } from "./clock-and-limits.js";
|
|
58
|
-
import { gitRestateOption, resolveGitLegDelivery, wrapGitFrame } from "./git-leg-delivery.js";
|
|
25
|
+
import { reconciledToolEndBody } from "./tool-end-body.js";
|
|
26
|
+
import { answerFaceForRedeemedCall } from "./decide-continuation.js";
|
|
59
27
|
import { createTurnBoundary } from "./run-turn-boundary.js";
|
|
60
|
-
import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
61
28
|
import { createHarnessHandlers } from "./run-harness-handlers.js";
|
|
62
29
|
import { resumeAdmission } from "./resume-admission.js";
|
|
63
30
|
import { resumeReviewOutcome } from "./resume-review-outcome.js";
|
|
@@ -72,6 +39,10 @@ import { streamReap } from "./stream-reap.js";
|
|
|
72
39
|
import { streamSteerVerb } from "./stream-steer-verb.js";
|
|
73
40
|
import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
|
|
74
41
|
import { streamHaltVerbs } from "./stream-halt-verbs.js";
|
|
42
|
+
import { runNotificationLane } from "./run-notification-lane.js";
|
|
43
|
+
import { runLeg } from "./run-leg.js";
|
|
44
|
+
import { runTerminalAdoption } from "./run-terminal-adoption.js";
|
|
45
|
+
import { runSettleAndTeardown } from "./run-settle-and-teardown.js";
|
|
75
46
|
import { runIdentityWiring } from "./run-identity-wiring.js";
|
|
76
47
|
import { runTelemetryAndBudgetSeats } from "./run-telemetry-and-budget-seats.js";
|
|
77
48
|
import { runAttachmentSeats } from "./run-attachment-seats.js";
|
|
@@ -83,17 +54,11 @@ import { runCompactionMachinery } from "./run-compaction-machinery.js";
|
|
|
83
54
|
import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
|
|
84
55
|
import { runRecoveryLanes } from "./run-recovery-lanes.js";
|
|
85
56
|
import { runGitLane } from "./run-git-lane.js";
|
|
86
|
-
const ORG_DISCLOSURE_MAX_CHARS = 600;
|
|
87
|
-
const SUGGESTIONS_DEFAULT_COUNT = 3;
|
|
88
|
-
const SUGGESTIONS_MAX_COUNT = 8;
|
|
89
|
-
const SUGGESTIONS_TIMEOUT_MS = 30_000;
|
|
90
|
-
const SUGGESTIONS_TRANSCRIPT_MESSAGES = 12;
|
|
91
57
|
export class Runner {
|
|
92
58
|
deps;
|
|
93
59
|
sessions;
|
|
94
60
|
sessionLocks = new Map();
|
|
95
61
|
sessionReadStates = new SessionReadFileStates();
|
|
96
|
-
autoModeBreakerLedger = new AutoModeBreakerLedger();
|
|
97
62
|
pendingSessionNotifications = new PendingSessionNotifications();
|
|
98
63
|
parentConstraintRegistry = new Map();
|
|
99
64
|
suspendedEnvReaps = new Map();
|
|
@@ -429,1115 +394,82 @@ export class Runner {
|
|
|
429
394
|
destroy,
|
|
430
395
|
};
|
|
431
396
|
}
|
|
432
|
-
|
|
397
|
+
runLocked(input) {
|
|
433
398
|
const { spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, entryActor, entryTracer } = input;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
workspaceHandle: resume.cp.state.workspaceHandle,
|
|
443
|
-
executesApprovedAction: resume.cp.pendingAction.kind === "tool_approval" &&
|
|
444
|
-
resume.outcome.gate !== "wake" &&
|
|
445
|
-
resume.outcome.decision === "allow",
|
|
446
|
-
...(resume.outcome.gate !== "wake" && resume.outcome.decision === "allow" && pendingContentAskCallId(resume.cp) !== undefined
|
|
447
|
-
? {
|
|
448
|
-
redeemedContentAskCallId: pendingContentAskCallId(resume.cp),
|
|
449
|
-
redeemedContentAskQuestionsHash: boundInputHashOf(resume.cp.pendingAction.args?.questions),
|
|
450
|
-
}
|
|
451
|
-
: {}),
|
|
452
|
-
}
|
|
453
|
-
: undefined;
|
|
454
|
-
const taskNotificationQueue = new SystemInjectionQueue();
|
|
455
|
-
const detachHub = internals?.detachHub ?? new ToolDetachHub();
|
|
456
|
-
let notificationHarness;
|
|
457
|
-
let notificationIdent = () => ({});
|
|
458
|
-
let notificationLaneLive = true;
|
|
459
|
-
let notificationSessionId;
|
|
460
|
-
let descendantAnchorDisclosed = false;
|
|
461
|
-
const parkTaskNotification = (payload, priority) => {
|
|
462
|
-
if (notificationSessionId !== undefined)
|
|
463
|
-
this.pendingSessionNotifications.pend(notificationSessionId, payload, priority);
|
|
464
|
-
if (!isDelegatedAgentTerminal(payload))
|
|
465
|
-
return;
|
|
466
|
-
const uplink = internals?.parentNotify;
|
|
467
|
-
if (uplink !== undefined) {
|
|
468
|
-
try {
|
|
469
|
-
uplink(payload, { priority });
|
|
470
|
-
}
|
|
471
|
-
catch (e) {
|
|
472
|
-
this.deps.onError?.(e, { phase: "degraded", sessionId: notificationSessionId ?? "", classification: "descendant-terminal-uplink" });
|
|
473
|
-
}
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
const rootAnchor = internals?.rootSessionId;
|
|
477
|
-
if (rootAnchor !== undefined && rootAnchor !== notificationSessionId) {
|
|
478
|
-
this.pendingSessionNotifications.pend(rootAnchor, payload, priority);
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (internals?.parentSessionId !== undefined && !descendantAnchorDisclosed) {
|
|
482
|
-
descendantAnchorDisclosed = true;
|
|
483
|
-
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" });
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
const unsubscribeTaskNotifications = taskNotificationQueue.subscribe((item) => {
|
|
487
|
-
queue.push({ type: "task_notification", notification: item.payload, priority: item.priority, ...notificationIdent() });
|
|
488
|
-
if (notificationHarness && prepared.batchHaltRef.current === undefined) {
|
|
489
|
-
const xml = renderTaskNotificationXml(item.payload);
|
|
490
|
-
const noteOptions = {
|
|
491
|
-
provenance: "engine-note",
|
|
492
|
-
enginePayload: item.payload,
|
|
493
|
-
...(item.payload.task_type !== "external" && isTerminalTaskNotification(item.payload) ? { capPreferred: true } : {}),
|
|
494
|
-
};
|
|
495
|
-
const deliver = item.priority === "later"
|
|
496
|
-
? notificationHarness.followUp(xml, noteOptions)
|
|
497
|
-
: notificationHarness.steer(xml, { ...noteOptions, ...(item.priority === "now" ? { immediate: true } : {}) });
|
|
498
|
-
void deliver.then(() => item.onDisposition?.("queued"), () => {
|
|
499
|
-
parkTaskNotification(item.payload, item.priority);
|
|
500
|
-
item.onDisposition?.("parked");
|
|
399
|
+
return runNotificationLane({
|
|
400
|
+
spec, queue, resume, internals, taskIdRef,
|
|
401
|
+
runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, pendingSessionNotifications: this.pendingSessionNotifications,
|
|
402
|
+
prepareTask,
|
|
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,
|
|
501
407
|
});
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
return Promise.resolve("parked");
|
|
516
|
-
}
|
|
517
|
-
return new Promise((resolve) => {
|
|
518
|
-
const accepted = taskNotificationQueue.enqueue({
|
|
519
|
-
kind: "task_notification",
|
|
520
|
-
priority: opts?.priority ?? "later",
|
|
521
|
-
dedupKey: taskNotificationDedupKey(notification),
|
|
522
|
-
payload: notification,
|
|
523
|
-
onDisposition: resolve,
|
|
524
|
-
});
|
|
525
|
-
if (!accepted)
|
|
526
|
-
resolve("queued");
|
|
527
|
-
});
|
|
528
|
-
};
|
|
529
|
-
const peerSelfRef = internals?.peerSelfRef ?? createPeerSelfRef(internals?.registryScope ?? spec.principal ?? "default");
|
|
530
|
-
const peerInboundChainRef = internals?.peerInboundChainRef ?? createPeerInboundChainRef();
|
|
531
|
-
const modelCatalog = this.deps.models;
|
|
532
|
-
const prepared = await prepareTask(spec, this.deps, this.sessions, prepareResume, {
|
|
533
|
-
...(internals ?? {}),
|
|
534
|
-
peerSelfRef,
|
|
535
|
-
peerInboundChainRef,
|
|
536
|
-
detachHub,
|
|
537
|
-
sessionReadStates: this.sessionReadStates,
|
|
538
|
-
autoModeBreakerLedger: this.autoModeBreakerLedger,
|
|
539
|
-
onTaskNotification: (notification, opts) => {
|
|
540
|
-
try {
|
|
541
|
-
upstreamTaskNotification?.(notification, opts);
|
|
542
|
-
}
|
|
543
|
-
catch {
|
|
544
|
-
}
|
|
545
|
-
injectTaskNotification(notification, opts);
|
|
546
|
-
},
|
|
547
|
-
}, this, taskIdRef);
|
|
548
|
-
let undrainedUserAtEnd;
|
|
549
|
-
const notificationLane = {
|
|
550
|
-
get harness() { return notificationHarness; }, set harness(h) { notificationHarness = h; },
|
|
551
|
-
get sessionId() { return notificationSessionId; }, set sessionId(s) { notificationSessionId = s; },
|
|
552
|
-
get ident() { return notificationIdent; }, set ident(f) { notificationIdent = f; },
|
|
553
|
-
};
|
|
554
|
-
const undrainedUserAtEndCell = { get current() { return undrainedUserAtEnd; }, set current(v) { undrainedUserAtEnd = v; } };
|
|
555
|
-
const { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats } = runIdentityWiring({
|
|
556
|
-
spec, queue, prepared, internals, taskIdRef, onReady, manualCompactRef, notifyRef, captureOptOutRef, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef,
|
|
557
|
-
notificationLane, undrainedUserAtEnd: undrainedUserAtEndCell, pendingSessionNotifications: this.pendingSessionNotifications, runner: this.depsSeat,
|
|
558
|
-
});
|
|
559
|
-
const rs = createRunState();
|
|
560
|
-
const { noteUnevaluablePriceTable } = runTelemetryAndBudgetSeats({ spec, prepared, resume, stats, taskIdRef, entryTracer, modelCatalog, runSourceTaskId, rs, runner: this.depsSeat, sessions: this.sessions });
|
|
561
|
-
await runAttachmentSeats({ spec, prepared, resume, rs, runner: this.depsSeat });
|
|
562
|
-
const { todoToolMounted, taskToolsMounted, writeFamilyOf, toolStartAt, startedToolCallIds } = runToolMountFacts({ spec, prepared, rs });
|
|
563
|
-
let reasoningResolution;
|
|
564
|
-
const reasoningResolutionCell = { get current() { return reasoningResolution; }, set current(v) { reasoningResolution = v; } };
|
|
565
|
-
const { observeReasoningWireFacts } = runReasoningSeat({ prepared, rs, taskIdRef, reasoningResolution: reasoningResolutionCell });
|
|
566
|
-
const { effectiveTimeoutMs, walltimeMonotonicDeadline, timeout, pushContent, ownCommittedTailRef, emitCommitted } = runClockAndContent({ spec, prepared, queue, internals, rs, ident, parentToolCallId, startedToolCallIds });
|
|
567
|
-
const { subagentName, withBrainSinks } = runBrainSinks({ queue, internals, rs, ident, parentToolCallId, observeReasoningWireFacts });
|
|
568
|
-
const toolLabels = new Map(prepared.tools.flatMap((t) => (t.label !== undefined && t.label !== t.name ? [[t.name, t.label]] : [])));
|
|
569
|
-
for (const orphan of prepared.wakeRecovered) {
|
|
570
|
-
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() });
|
|
571
|
-
emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
|
|
572
|
-
}
|
|
573
|
-
const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
|
|
574
|
-
const batchArgs = postToolBatchHook ? new Map() : undefined;
|
|
575
|
-
rs.turn.toolBatch = [];
|
|
576
|
-
const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove } = createHarnessHandlers({
|
|
577
|
-
prepared, stats, rs,
|
|
578
|
-
deps: {
|
|
579
|
-
spec, queue, internals, ident, parentToolCallId, subagentName,
|
|
580
|
-
pushContent, emitCommitted, startedToolCallIds, toolStartAt, writeFamilyOf,
|
|
581
|
-
toolLabels, postToolBatchHook, batchArgs,
|
|
582
|
-
runnerHooks: { onError: (e, info) => this.deps.onError?.(e, info) },
|
|
583
|
-
},
|
|
584
|
-
});
|
|
585
|
-
const { compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions } = runCompactionMachinery({ spec, prepared, rs, stats, noteUnevaluablePriceTable, runner: this.depsSeat });
|
|
586
|
-
runStopAndFinalVerify({ spec, prepared, queue, rs, stats, ident, walltimeMonotonicDeadline, runner: this.depsSeat });
|
|
587
|
-
runRecoveryLanes({
|
|
588
|
-
spec, prepared, queue, rs, ident, compactionBrain, compactionBreaker, windowSafetyOptions, runner: this.depsSeat,
|
|
589
|
-
compactionSeats: {
|
|
590
|
-
seamCCompactionOptions: (p) => this.seamCCompactionOptions(p),
|
|
591
|
-
compactionHookOptions: (s, sid, trig, identity, seatBound) => this.compactionHookOptions(s, sid, trig, identity, seatBound),
|
|
592
|
-
recordCompactionReuse: (p, c) => this.recordCompactionReuse(p, c),
|
|
593
|
-
},
|
|
594
|
-
});
|
|
595
|
-
const rapidRefill = createRapidRefillState();
|
|
596
|
-
const drainManualCompact = (outcome) => {
|
|
597
|
-
manualCompactRef.requested = false;
|
|
598
|
-
for (const w of manualCompactRef.waiters.splice(0))
|
|
599
|
-
w.resolve(outcome);
|
|
600
|
-
};
|
|
601
|
-
const onTurnBoundary = createTurnBoundary({
|
|
602
|
-
prepared, stats, rs,
|
|
603
|
-
deps: {
|
|
604
|
-
spec, queue, loopLatch, manualCompactRef, todoToolMounted, taskToolsMounted,
|
|
605
|
-
walltimeMonotonicDeadline,
|
|
606
|
-
timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction,
|
|
607
|
-
compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact,
|
|
608
|
-
runnerHooks: {
|
|
609
|
-
onError: this.deps.onError,
|
|
610
|
-
seamCCompactionOptions: (p) => this.seamCCompactionOptions(p),
|
|
611
|
-
compactionHookOptions: (s, sid, trig) => this.compactionHookOptions(s, sid, trig, prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal }),
|
|
612
|
-
recordCompactionReuse: (p, c) => this.recordCompactionReuse(p, c),
|
|
613
|
-
},
|
|
614
|
-
},
|
|
615
|
-
});
|
|
616
|
-
const { flushGitMirror, unsubGitRetry, unsubBoundary, unsub } = runGitLane({ prepared, queue, rs, ident, onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, onTurnBoundary, runner: this.depsSeat });
|
|
617
|
-
let final;
|
|
618
|
-
let threw;
|
|
619
|
-
let abortedLive = false;
|
|
620
|
-
let userInterruptedLive = false;
|
|
621
|
-
let strandedHumanAnswers = [];
|
|
622
|
-
try {
|
|
623
|
-
if (prepared.abortController.signal.aborted) {
|
|
624
|
-
const e = new Error("run aborted");
|
|
625
|
-
e.name = "AbortError";
|
|
626
|
-
throw e;
|
|
627
|
-
}
|
|
628
|
-
if (resume) {
|
|
629
|
-
const walltimeExhaustedResume = effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0;
|
|
630
|
-
const resumeWindowRetryAfterMs = !walltimeExhaustedResume && prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
|
|
631
|
-
if (resumeWindowRetryAfterMs !== undefined) {
|
|
632
|
-
const owesNothing = resume.outcome.gate === "resource_limit" &&
|
|
633
|
-
readPendingSteerQueue(resume.cp.state).length === 0 &&
|
|
634
|
-
(resume.cp.state.runningBackgroundTasks?.length ?? 0) === 0;
|
|
635
|
-
const committed = owesNothing &&
|
|
636
|
-
prepared.suspendForPlatformLimit !== undefined &&
|
|
637
|
-
(await prepared.suspendForPlatformLimit("usage_window", { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) }, { resumeAfterMs: resumeWindowRetryAfterMs }));
|
|
638
|
-
if (committed) {
|
|
639
|
-
rs.limits.turnsExceeded = false;
|
|
640
|
-
rs.limits.budgetHit = undefined;
|
|
641
|
-
}
|
|
642
|
-
else {
|
|
643
|
-
const terminal = platformLimitTerminal("usage_window", resumeWindowRetryAfterMs, "resume");
|
|
644
|
-
rs.limits.platformTerminal = terminal;
|
|
645
|
-
try {
|
|
646
|
-
this.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
|
|
647
|
-
}
|
|
648
|
-
catch {
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
if (resumeWindowRetryAfterMs === undefined && !walltimeExhaustedResume && resume.outcome.gate !== "wake") {
|
|
653
|
-
await this.applyResumeDecision(prepared, resume, (e) => pushContent({ ...e, ...ident() }), emitCommitted, (toolName, details) => {
|
|
654
|
-
if (rs.attach.attachState === undefined)
|
|
655
|
-
return;
|
|
656
|
-
const family = writeFamilyOf(toolName);
|
|
657
|
-
if (family !== undefined)
|
|
658
|
-
stampWriteAnchor(rs.attach.attachState, family, rs.counters.cadenceTurns);
|
|
659
|
-
if (details !== undefined)
|
|
660
|
-
reduceToolEnd(rs.attach.attachState, details);
|
|
661
|
-
}, (toolCallId) => {
|
|
662
|
-
startedToolCallIds.add(toolCallId);
|
|
663
|
-
if (resume !== undefined && resume.cp.pendingAction.kind === "tool_approval" && toolCallId === resume.cp.pendingAction.toolCallId) {
|
|
664
|
-
resume.pendingActionStarted = true;
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
if (resume.outcome.gate === "policy_ask")
|
|
668
|
-
resume.decisionDelivered = true;
|
|
669
|
-
announceWorkspaceMove();
|
|
670
|
-
}
|
|
671
|
-
if (resumeWindowRetryAfterMs !== undefined) {
|
|
672
|
-
}
|
|
673
|
-
else if (walltimeExhaustedResume) {
|
|
674
|
-
timeout.fired = true;
|
|
675
|
-
timeout.clear();
|
|
676
|
-
void prepared.harness.abort();
|
|
677
|
-
prepared.abortController.abort();
|
|
678
|
-
}
|
|
679
|
-
else if (rs.budget.maxTokensWindow !== undefined && rs.budget.maxTokensWindow <= 0) {
|
|
680
|
-
rs.limits.budgetHit = "exceeded";
|
|
681
|
-
rs.limits.budgetAxis = "tokens";
|
|
682
|
-
}
|
|
683
|
-
else if (rs.budget.maxCostMicroUsd !== undefined && rs.budget.maxCostMicroUsd <= 0) {
|
|
684
|
-
rs.limits.budgetHit = "exceeded";
|
|
685
|
-
rs.limits.budgetAxis = "cost";
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
let continuation = resumeContinuation(resume, prepared.reminderMark);
|
|
689
|
-
const bgSnapshot = resume.cp.state.runningBackgroundTasks;
|
|
690
|
-
if (bgSnapshot !== undefined && bgSnapshot.length > 0) {
|
|
691
|
-
const alive = new Set(prepared.listBackgroundTasks().map((t) => t.id));
|
|
692
|
-
const orphans = bgSnapshot.filter((t) => !alive.has(t.id));
|
|
693
|
-
if (orphans.length > 0) {
|
|
694
|
-
continuation += "\n\n" + formatHookFeedback(renderOrphanedBackgroundTasks(orphans), prepared.reminderMark);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
let gitResumeDelivered;
|
|
698
|
-
{
|
|
699
|
-
const gitBody = await resolveGitLegDelivery(prepared, resume.cp.state.gitAnnouncement, (err) => {
|
|
700
|
-
try {
|
|
701
|
-
this.deps.onError?.(new Error(`git announcement ladder unreadable (conservative re-announce): ${err.message}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
702
|
-
}
|
|
703
|
-
catch {
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
const gitFrame = prepared.gitStatusRef.frame;
|
|
707
|
-
if (gitBody !== undefined && gitFrame !== undefined) {
|
|
708
|
-
const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
|
|
709
|
-
continuation += "\n\n" + wrappedGit;
|
|
710
|
-
gitResumeDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, wrapped: wrappedGit };
|
|
711
|
-
if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
|
|
712
|
-
prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
const engineSegments = continuation.length > 0 ? [{ start: 0, end: continuation.length }] : [];
|
|
717
|
-
const resumeFrames = [
|
|
718
|
-
...readPendingSteerQueue(resume.cp.state).map((entry) => ({ entry, source: "steer" })),
|
|
719
|
-
...(resume.wakeMessage !== undefined ? [{ entry: resume.wakeMessage, source: "wake" }] : []),
|
|
720
|
-
];
|
|
721
|
-
for (const { entry: steer, source } of resumeFrames) {
|
|
722
|
-
if (source === "wake" && resume.wakeMessageHookContext !== undefined) {
|
|
723
|
-
const hookStart = continuation.length;
|
|
724
|
-
continuation += "\n\n" + formatHookFeedback(resume.wakeMessageHookContext, prepared.reminderMark);
|
|
725
|
-
engineSegments.push({ start: hookStart, end: continuation.length });
|
|
726
|
-
}
|
|
727
|
-
if (source === "steer") {
|
|
728
|
-
const parkedScreen = (spec.hooks ?? this.deps.hooks)?.userPromptSubmit;
|
|
729
|
-
if (parkedScreen !== undefined) {
|
|
730
|
-
let outcome;
|
|
731
|
-
try {
|
|
732
|
-
const seat = await runHookSeat("userPromptSubmit", { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal, abortEnds: true }, (sig) => parkedScreen(steer.text, {
|
|
733
|
-
identity: prepared.hookIdentity,
|
|
734
|
-
signal: sig,
|
|
735
|
-
source: "parked_redelivery",
|
|
736
|
-
...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
|
|
737
|
-
...(steer.actor !== undefined ? { actor: snapshotActorAssertion(steer.actor) } : {}),
|
|
738
|
-
}));
|
|
739
|
-
outcome = seat.expired
|
|
740
|
-
? { 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)" }
|
|
741
|
-
: seat.value?.block !== undefined && seat.value.block !== ""
|
|
742
|
-
? { blocked: inlineUntrusted(seat.value.block) }
|
|
743
|
-
: { ...(seat.value?.additionalContext !== undefined && seat.value.additionalContext !== "" ? { context: seat.value.additionalContext } : {}) };
|
|
744
|
-
}
|
|
745
|
-
catch (hookErr) {
|
|
746
|
-
const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
|
|
747
|
-
try {
|
|
748
|
-
this.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
|
|
749
|
-
}
|
|
750
|
-
catch {
|
|
751
|
-
}
|
|
752
|
-
outcome = { blocked: `the screen crashed (${inlineUntrusted(err.message)}; fail-closed)` };
|
|
753
|
-
}
|
|
754
|
-
if ("blocked" in outcome) {
|
|
755
|
-
deliverEngineNotice(this.deps.onNotice, {
|
|
756
|
-
code: "steering.parked_input_blocked",
|
|
757
|
-
message: "a parked steering input was withheld by the deployment's userPromptSubmit screen when this resume redelivered it: " +
|
|
758
|
-
`${outcome.blocked}. The parked row was consumed with the checkpoint; the instruction never reached the model — ` +
|
|
759
|
-
"re-issue it if it still applies.",
|
|
760
|
-
detail: {
|
|
761
|
-
...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
|
|
762
|
-
sessionId: prepared.sessionId,
|
|
763
|
-
runId: prepared.runId,
|
|
764
|
-
...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
|
|
765
|
-
},
|
|
766
|
-
});
|
|
767
|
-
queue.push({
|
|
768
|
-
...buildHumanInputEvent({
|
|
769
|
-
carrier: source,
|
|
770
|
-
source,
|
|
771
|
-
delivery: "blocked",
|
|
772
|
-
sessionSeq: nextHumanInputSeq(prepared.harness),
|
|
773
|
-
...(steer.inputId !== undefined ? { inputId: steer.inputId } : {}),
|
|
774
|
-
...(steer.actor !== undefined ? { actor: steer.actor } : {}),
|
|
775
|
-
...(steer.actor?.issuer !== undefined ? { issuer: steer.actor.issuer } : {}),
|
|
776
|
-
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
777
|
-
}),
|
|
778
|
-
...ident(),
|
|
779
|
-
});
|
|
780
|
-
continue;
|
|
781
|
-
}
|
|
782
|
-
if ("context" in outcome && outcome.context !== undefined) {
|
|
783
|
-
const hookStart = continuation.length;
|
|
784
|
-
continuation += "\n\n" + formatHookFeedback(outcome.context, prepared.reminderMark);
|
|
785
|
-
engineSegments.push({ start: hookStart, end: continuation.length });
|
|
786
|
-
}
|
|
787
|
-
}
|
|
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);
|
|
788
421
|
}
|
|
789
|
-
const
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
source,
|
|
800
|
-
delivery: "applied",
|
|
801
|
-
sessionSeq: nextHumanInputSeq(prepared.harness),
|
|
802
|
-
inputId: steer.inputId,
|
|
803
|
-
...(steer.actor !== undefined ? { actor: steer.actor } : {}),
|
|
804
|
-
...(steer.actor?.issuer !== undefined ? { issuer: steer.actor.issuer } : {}),
|
|
805
|
-
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
806
|
-
}),
|
|
807
|
-
...ident(),
|
|
808
|
-
});
|
|
809
|
-
}
|
|
810
|
-
if (resume !== undefined)
|
|
811
|
-
resume.decisionDelivered = true;
|
|
812
|
-
if (gitResumeDelivered !== undefined) {
|
|
813
|
-
const delivered = gitResumeDelivered;
|
|
814
|
-
prepared.gitStatusRef.protectedText = delivered.wrapped;
|
|
815
|
-
prepared.gitStatusRef.pendingReceipt = {
|
|
816
|
-
text: continuation,
|
|
817
|
-
commit: (entryId) => {
|
|
818
|
-
prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
819
|
-
queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
|
|
820
|
-
rs.attach.attachmentsInjected += 1;
|
|
821
|
-
prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
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) },
|
|
822
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);
|
|
823
442
|
};
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
const projectedObjective = projectHumanInput({ text: spec.objective, actor: objectiveActor, source: "objective" });
|
|
834
|
-
let effectiveObjective = projectedObjective;
|
|
835
|
-
let promptBlocked = false;
|
|
836
|
-
const userPromptSubmit = (spec.hooks ?? this.deps.hooks)?.userPromptSubmit;
|
|
837
|
-
if (userPromptSubmit) {
|
|
838
|
-
try {
|
|
839
|
-
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) } : {}) }));
|
|
840
|
-
if (promptSeat.expired) {
|
|
841
|
-
if (promptSeat.cause === "timeout") {
|
|
842
|
-
try {
|
|
843
|
-
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 });
|
|
844
|
-
}
|
|
845
|
-
catch {
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
prepared.blockedRef.reason = formatHookFeedback(promptSeat.cause === "timeout"
|
|
849
|
-
? `the deployment's userPromptSubmit hook did not answer within its ${prepared.hookTimeoutMs}ms bound while screening this prompt; ` +
|
|
850
|
-
`the prompt was NOT submitted (fail-closed)`
|
|
851
|
-
: `the task was cancelled while the deployment's userPromptSubmit hook was still screening this prompt; ` +
|
|
852
|
-
`the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
|
|
853
|
-
promptBlocked = true;
|
|
854
|
-
}
|
|
855
|
-
const decision = promptSeat.expired ? undefined : promptSeat.value;
|
|
856
|
-
if (decision?.block) {
|
|
857
|
-
prepared.blockedRef.reason = formatHookFeedback(decision.block, prepared.reminderMark);
|
|
858
|
-
promptBlocked = true;
|
|
859
|
-
}
|
|
860
|
-
else if (decision?.additionalContext) {
|
|
861
|
-
effectiveObjective = `${formatHookFeedback(decision.additionalContext, prepared.reminderMark)}\n\n${projectedObjective}`;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
catch (hookErr) {
|
|
865
|
-
const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
|
|
866
|
-
try {
|
|
867
|
-
this.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
|
|
868
|
-
}
|
|
869
|
-
catch {
|
|
870
|
-
}
|
|
871
|
-
prepared.blockedRef.reason = formatHookFeedback(`the deployment's userPromptSubmit hook crashed while screening this prompt (${err.message}); ` +
|
|
872
|
-
`the prompt was NOT submitted (fail-closed)`, prepared.reminderMark);
|
|
873
|
-
promptBlocked = true;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
let gitLegDelivered;
|
|
877
|
-
if (!promptBlocked) {
|
|
878
|
-
const gitBody = await resolveGitLegDelivery(prepared, undefined, (err) => {
|
|
879
|
-
try {
|
|
880
|
-
this.deps.onError?.(new Error(`git announcement ladder unreadable (conservative re-announce): ${err.message}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
881
|
-
}
|
|
882
|
-
catch {
|
|
883
|
-
}
|
|
884
|
-
});
|
|
885
|
-
const gitFrame = prepared.gitStatusRef.frame;
|
|
886
|
-
if (gitBody !== undefined && gitFrame !== undefined) {
|
|
887
|
-
const wrappedGit = wrapGitFrame(gitBody, prepared.reminderMark);
|
|
888
|
-
const standalone = spec.images !== undefined && spec.images.length > 0;
|
|
889
|
-
if (!standalone)
|
|
890
|
-
effectiveObjective = `${wrappedGit}\n${effectiveObjective}`;
|
|
891
|
-
gitLegDelivered = { kind: gitFrame.kind, hash: gitFrame.hash, standalone, wrapped: wrappedGit };
|
|
892
|
-
if (gitFrame.kind === "full" && gitFrame.shrunk !== undefined) {
|
|
893
|
-
prepared.gitStatusRef.wrappedShrink = { find: wrappedGit, replace: wrapGitFrame(gitFrame.shrunk.body, prepared.reminderMark) };
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
const firstFrames = [];
|
|
898
|
-
if (rs.attach.attachState !== undefined) {
|
|
899
|
-
const attach = rs.attach.attachState;
|
|
900
|
-
if (rs.attach.agentListingOn && prepared.agentListing !== undefined) {
|
|
901
|
-
const listing = prepared.agentListing;
|
|
902
|
-
const body = renderAgentListingDelta(attach, listing.entries, listing.toolName, listing.models);
|
|
903
|
-
if (body !== undefined) {
|
|
904
|
-
firstFrames.push({
|
|
905
|
-
source: "agent_listing",
|
|
906
|
-
body,
|
|
907
|
-
commit: () => {
|
|
908
|
-
commitAgentListing(attach, listing.entries, listing.models);
|
|
909
|
-
prepared.announcedListingsRef.agents = listing.entries.map((e) => e.name);
|
|
910
|
-
if (listing.models !== undefined)
|
|
911
|
-
prepared.announcedListingsRef.models = [...listing.models];
|
|
912
|
-
},
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
if (rs.attach.skillsListingOn && prepared.skillsListing !== undefined) {
|
|
917
|
-
const listing = prepared.skillsListing;
|
|
918
|
-
const body = renderSkillsListingDelta(attach, listing.entries);
|
|
919
|
-
if (body !== undefined) {
|
|
920
|
-
firstFrames.push({
|
|
921
|
-
source: "skills_listing",
|
|
922
|
-
body,
|
|
923
|
-
commit: () => {
|
|
924
|
-
commitSkillsListing(attach, listing.entries);
|
|
925
|
-
prepared.announcedListingsRef.skills = listing.entries.map((e) => e.name);
|
|
926
|
-
},
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
if (firstFrames.length > 0) {
|
|
931
|
-
effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body, attachmentEnvelopeTags(f.source)), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
const gitQueuedChars = gitLegDelivered !== undefined && gitLegDelivered.standalone ? gitLegDelivered.wrapped.length : 0;
|
|
935
|
-
const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
|
|
936
|
-
? 0
|
|
937
|
-
: computeCostMicroUsd({
|
|
938
|
-
totalInputTokens: Math.ceil((effectiveObjective.length + gitQueuedChars) / 4),
|
|
939
|
-
cacheReadTokens: 0,
|
|
940
|
-
cacheWriteTokens: 0,
|
|
941
|
-
cacheWriteTokensLong: 0,
|
|
942
|
-
outputTokens: prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS,
|
|
943
|
-
}, rs.telemetry.pricing);
|
|
944
|
-
const precallCeilingMicroUsd = prepared.suspendForResource !== undefined ? rs.budget.remainingMicroUsd : rs.budget.maxCostMicroUsd;
|
|
945
|
-
const precallTokens = Math.ceil((effectiveObjective.length + gitQueuedChars) / 4) + (prepared.model.maxTokens ?? DEFAULT_PRECALL_OUTPUT_TOKENS);
|
|
946
|
-
const precallCeilingTokens = prepared.suspendForResource !== undefined ? rs.budget.remainingTokens : rs.budget.maxTokensWindow;
|
|
947
|
-
const entryUsageRetryAfterMs = prepared.usageGovernance !== undefined ? await prepared.usageGovernance.check(Date.now()) : undefined;
|
|
948
|
-
if (promptBlocked) {
|
|
949
|
-
}
|
|
950
|
-
else if (entryUsageRetryAfterMs !== undefined) {
|
|
951
|
-
const terminal = platformLimitTerminal("usage_window", entryUsageRetryAfterMs, "entry");
|
|
952
|
-
rs.limits.platformTerminal = terminal;
|
|
953
|
-
try {
|
|
954
|
-
this.deps.onError?.(terminal, { phase: "config", sessionId: prepared.sessionId });
|
|
955
|
-
}
|
|
956
|
-
catch {
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
else if (precallCeilingTokens !== undefined && precallTokens > precallCeilingTokens) {
|
|
960
|
-
rs.limits.budgetHit = "precall";
|
|
961
|
-
rs.limits.budgetAxis = "tokens";
|
|
962
|
-
}
|
|
963
|
-
else if (precallCeilingMicroUsd !== undefined && precallMicroUsd > precallCeilingMicroUsd) {
|
|
964
|
-
rs.limits.budgetHit = "precall";
|
|
965
|
-
rs.limits.budgetAxis = "cost";
|
|
966
|
-
}
|
|
967
|
-
else if (effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0) {
|
|
968
|
-
timeout.fired = true;
|
|
969
|
-
timeout.clear();
|
|
970
|
-
void prepared.harness.abort();
|
|
971
|
-
prepared.abortController.abort();
|
|
972
|
-
}
|
|
973
|
-
else {
|
|
974
|
-
const images = spec.images
|
|
975
|
-
? await Promise.all(spec.images.map((img) => toImageContent(img, this.deps.allowImageUrl)))
|
|
976
|
-
: undefined;
|
|
977
|
-
if (gitLegDelivered !== undefined) {
|
|
978
|
-
const delivered = gitLegDelivered;
|
|
979
|
-
if (delivered.standalone)
|
|
980
|
-
await prepared.harness.nextTurn(delivered.wrapped, { engineMinted: true });
|
|
981
|
-
prepared.gitStatusRef.protectedText = delivered.wrapped;
|
|
982
|
-
prepared.gitStatusRef.pendingReceipt = {
|
|
983
|
-
text: delivered.standalone ? delivered.wrapped : effectiveObjective,
|
|
984
|
-
commit: (entryId) => {
|
|
985
|
-
prepared.gitStatusRef.announced = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
986
|
-
queue.push({ type: "steering_injected", source: "git_status", preview: GIT_STATUS_ECHO_PREVIEW[delivered.kind], ...ident() });
|
|
987
|
-
rs.attach.attachmentsInjected += 1;
|
|
988
|
-
prepared.gitStatusRef.mirrorOwed = { kind: delivered.kind, hash: delivered.hash, entryId };
|
|
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,
|
|
989
452
|
},
|
|
990
|
-
};
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
...(objectiveActor?.issuer !== undefined ? { issuer: objectiveActor.issuer } : {}),
|
|
1008
|
-
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
1009
|
-
}),
|
|
1010
|
-
...ident(),
|
|
1011
|
-
});
|
|
1012
|
-
final = await withBrainSinks(() => prepared.harness.prompt(effectiveObjective, {
|
|
1013
|
-
...(images && images.length > 0 ? { images } : {}),
|
|
1014
|
-
...(enginePrefixChars > 0 ? { enginePrefixChars } : {}),
|
|
1015
|
-
...(objectiveActor !== undefined ? { actor: objectiveActor } : {}),
|
|
1016
|
-
}));
|
|
1017
|
-
await flushGitMirror();
|
|
1018
|
-
if (prepared.gitStatusRef.announced?.pending === true)
|
|
1019
|
-
await prepared.gitStatusRef.reassert?.();
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
loopLatch.ended = true;
|
|
1023
|
-
abortedLive = prepared.abortController.signal.aborted;
|
|
1024
|
-
userInterruptedLive = loopLatch.userInterrupted;
|
|
1025
|
-
}
|
|
1026
|
-
catch (err) {
|
|
1027
|
-
loopLatch.ended = true;
|
|
1028
|
-
if (errorCodeOf(err) === "resume.tool_unavailable" || errorCodeOf(err) === "resume.tool_contract_mismatch") {
|
|
1029
|
-
await settleTeardownLeg(() => prepared.mcp.dispose(), "mcp.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1030
|
-
await settleTeardownLeg(() => prepared.a2a?.dispose(), "a2a.dispose (tool_unavailable leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1031
|
-
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 }));
|
|
1032
|
-
throw err;
|
|
1033
|
-
}
|
|
1034
|
-
threw = err;
|
|
1035
|
-
abortedLive = prepared.abortController.signal.aborted;
|
|
1036
|
-
userInterruptedLive = loopLatch.userInterrupted;
|
|
1037
|
-
}
|
|
1038
|
-
finally {
|
|
1039
|
-
timeout.clear();
|
|
1040
|
-
prepared.lspDiagnostics?.registry.releaseRun(prepared.lspDiagnostics.runIdent);
|
|
1041
|
-
prepared.abortController.abort();
|
|
1042
|
-
prepared.releaseSignal();
|
|
1043
|
-
strandedHumanAnswers = prepared.settleContentAskBindings();
|
|
1044
|
-
try {
|
|
1045
|
-
prepared.harness.recoverUndrainedEngineNotes();
|
|
1046
|
-
}
|
|
1047
|
-
catch {
|
|
1048
|
-
}
|
|
1049
|
-
notificationLaneLive = false;
|
|
1050
|
-
unsubscribeTaskNotifications();
|
|
1051
|
-
prepared.toolRosterDeltas.unsubscribe();
|
|
1052
|
-
unsubGitRetry();
|
|
1053
|
-
unsubBoundary();
|
|
1054
|
-
unsub();
|
|
1055
|
-
}
|
|
1056
|
-
if (threw === undefined && prepared.brainCallGuardrailRef.timedOut !== undefined) {
|
|
1057
|
-
threw = prepared.brainCallGuardrailRef.timedOut;
|
|
1058
|
-
}
|
|
1059
|
-
if (threw === undefined && prepared.gateStopRef.terminal !== undefined) {
|
|
1060
|
-
threw = prepared.gateStopRef.terminal;
|
|
1061
|
-
}
|
|
1062
|
-
if (rs.limits.platformTerminal !== undefined) {
|
|
1063
|
-
threw = rs.limits.platformTerminal;
|
|
1064
|
-
}
|
|
1065
|
-
if (prepared.usageGovernance !== undefined) {
|
|
1066
|
-
try {
|
|
1067
|
-
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 }));
|
|
1068
|
-
}
|
|
1069
|
-
catch (flushErr) {
|
|
1070
|
-
this.deps.onError?.(flushErr, { phase: "config", sessionId: prepared.sessionId });
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
const committedPause = prepared.pausedRef.current;
|
|
1074
|
-
const durablyPaused = committedPause !== undefined;
|
|
1075
|
-
const interrupted = !durablyPaused &&
|
|
1076
|
-
(threw !== undefined || timeout.fired || rs.limits.turnsExceeded || rs.limits.budgetHit !== undefined || abortedLive || final?.stopReason === "aborted");
|
|
1077
|
-
let orphansClosed = 0;
|
|
1078
|
-
let reconcileComplete = false;
|
|
1079
|
-
if (interrupted) {
|
|
1080
|
-
try {
|
|
1081
|
-
const report = await reconcileInterruptedSession(prepared.session, prepared.toolEffects, undefined, startedToolCallIds);
|
|
1082
|
-
orphansClosed = report.recovered.length;
|
|
1083
|
-
for (const orphan of report.recovered) {
|
|
1084
|
-
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() });
|
|
1085
|
-
emitCommitted(orphan.entryId, "toolResult", orphan.toolCallId);
|
|
1086
|
-
}
|
|
1087
|
-
reconcileComplete = true;
|
|
1088
|
-
}
|
|
1089
|
-
catch (reconcileErr) {
|
|
1090
|
-
this.deps.onError?.(reconcileErr instanceof Error ? reconcileErr : new Error(String(reconcileErr)), {
|
|
1091
|
-
phase: "interrupt-reconcile",
|
|
1092
|
-
sessionId: prepared.sessionId,
|
|
1093
|
-
});
|
|
1094
|
-
}
|
|
1095
|
-
try {
|
|
1096
|
-
await prepared.harness.flushQueuedSessionWrites();
|
|
1097
|
-
}
|
|
1098
|
-
catch (flushErr) {
|
|
1099
|
-
this.deps.onError?.(flushErr instanceof Error ? flushErr : new Error(String(flushErr)), {
|
|
1100
|
-
phase: "interrupt-reconcile",
|
|
1101
|
-
sessionId: prepared.sessionId,
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
if (interrupted && userInterruptedLive && reconcileComplete) {
|
|
1106
|
-
try {
|
|
1107
|
-
const entryId = await appendInterruptionMarker(prepared.session, { toolUseInFlight: orphansClosed > 0 });
|
|
1108
|
-
emitCommitted(entryId, "user");
|
|
1109
|
-
}
|
|
1110
|
-
catch (markerErr) {
|
|
1111
|
-
this.deps.onError?.(markerErr instanceof Error ? markerErr : new Error(String(markerErr)), {
|
|
1112
|
-
phase: "interrupt-reconcile",
|
|
1113
|
-
sessionId: prepared.sessionId,
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
let migratedParked = { steer: 0, followUp: 0 };
|
|
1118
|
-
if (committedPause !== undefined && this.deps.checkpointStore !== undefined) {
|
|
1119
|
-
const parkToken = committedPause.cause.token;
|
|
1120
|
-
const parkScope = committedPause.scope;
|
|
1121
|
-
const store = this.deps.checkpointStore;
|
|
1122
|
-
const carried = [];
|
|
1123
|
-
for (const record of prepared.harness.readParkableUserInputs()) {
|
|
1124
|
-
try {
|
|
1125
|
-
if (!(await store.setPendingSteer(parkToken, parkScope, record)))
|
|
1126
|
-
break;
|
|
1127
|
-
carried.push(record);
|
|
1128
|
-
}
|
|
1129
|
-
catch (parkErr) {
|
|
1130
|
-
this.deps.onError?.(parkErr instanceof Error ? parkErr : new Error(String(parkErr)), {
|
|
1131
|
-
phase: "config",
|
|
1132
|
-
sessionId: prepared.sessionId,
|
|
1133
|
-
});
|
|
1134
|
-
const parkCode = errorCodeOf(parkErr);
|
|
1135
|
-
if (parkCode === "steering.invalid_content" || parkCode === "steering.queue_full")
|
|
1136
|
-
continue;
|
|
1137
|
-
break;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
migratedParked = prepared.harness.dropParkedUserInputs(carried);
|
|
1141
|
-
}
|
|
1142
|
-
if (undrainedUserAtEnd !== undefined) {
|
|
1143
|
-
const remaining = {
|
|
1144
|
-
steer: Math.max(0, undrainedUserAtEnd.steer - migratedParked.steer),
|
|
1145
|
-
followUp: Math.max(0, undrainedUserAtEnd.followUp - migratedParked.followUp),
|
|
1146
|
-
};
|
|
1147
|
-
for (const notice of undrainedUserInputNotices(remaining, spec.taskId ?? prepared.sessionId, prepared.sessionId, prepared.runId)) {
|
|
1148
|
-
deliverEngineNotice(this.deps.onNotice, notice);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
if (rs.attach.attachState?.postCompactPending === true && (rs.attach.attachmentsCfg?.backgroundTasks ?? eventDefaultOn("background_tasks")) === true) {
|
|
1152
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.announce_dropped", version: 1, taskId: rs.telemetry.taskId, ts: Date.now() }));
|
|
1153
|
-
}
|
|
1154
|
-
const comp = await this.finish(spec, prepared, {
|
|
1155
|
-
telemetry: { tracer: rs.telemetry.tracer, taskId: rs.telemetry.taskId },
|
|
1156
|
-
skipCompaction: durablyPaused || compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES,
|
|
1157
|
-
minTokens: rs.counters.compactionFloor,
|
|
1158
|
-
brain: compactionBrain,
|
|
1159
|
-
windowSafety: windowSafetyOptions(prepared.model),
|
|
1160
|
-
onCompactionFailed: (reason) => queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "auto", reason, ...ident() }),
|
|
1161
|
-
cancelled: abortedLive,
|
|
1162
|
-
});
|
|
1163
|
-
try {
|
|
1164
|
-
if (comp?.compacted) {
|
|
1165
|
-
queue.push({
|
|
1166
|
-
type: "compacted",
|
|
1167
|
-
trigger: "auto",
|
|
1168
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
1169
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
1170
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
1171
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
1172
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
1173
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
1174
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
1175
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
1176
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
1177
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
1178
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
1179
|
-
...ident(),
|
|
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
|
+
},
|
|
1180
470
|
});
|
|
1181
|
-
|
|
1182
|
-
const pd = comp.phaseDurations;
|
|
1183
|
-
const pdDur = comp.durationMs;
|
|
1184
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
|
|
1185
|
-
}
|
|
1186
|
-
prepared.cacheBreakDetector?.notifyCompaction();
|
|
1187
|
-
}
|
|
1188
|
-
if (prepared.nestedStats.tasks > 0) {
|
|
1189
|
-
stats.nested = {
|
|
1190
|
-
tokens: prepared.nestedStats.tokens,
|
|
1191
|
-
turns: prepared.nestedStats.turns,
|
|
1192
|
-
tasks: prepared.nestedStats.tasks,
|
|
1193
|
-
...(prepared.nestedStats.anyUnpriced ? {} : { costMicroUsd: prepared.nestedStats.costMicroUsd }),
|
|
1194
|
-
};
|
|
1195
|
-
}
|
|
1196
|
-
if (stats.totalInputTokens > 0) {
|
|
1197
|
-
const rawHit = stats.cachedTokens / stats.totalInputTokens;
|
|
1198
|
-
if (stats.cachedTokens > stats.totalInputTokens) {
|
|
1199
|
-
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 });
|
|
1200
|
-
}
|
|
1201
|
-
stats.cacheHitRate = Math.min(1, rawHit);
|
|
1202
|
-
if (!rs.telemetry.cacheBreakReported && stats.turns >= 2 && stats.totalInputTokens >= 8000 && stats.cacheHitRate < 0.15) {
|
|
1203
|
-
const cacheWritten = stats.cacheWriteTokens + stats.cacheWriteTokensLong;
|
|
1204
|
-
const cause = rs.degrade.degraded
|
|
1205
|
-
? `a mid-task model switch (${rs.degrade.degraded.from} → ${rs.degrade.degraded.to}, degradation) reset the prefix cache — this is the likely cause`
|
|
1206
|
-
: cacheWritten > 0
|
|
1207
|
-
? `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`
|
|
1208
|
-
: `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`;
|
|
1209
|
-
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 });
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
if (prepared.resourceLedger &&
|
|
1213
|
-
(resume?.cp.gate.kind === "resource_limit" || prepared.pausedRef.current?.cause.gate.kind === "resource_limit")) {
|
|
1214
|
-
stats.costMicroUsd += prepared.resourceLedger.spentMicroUsd;
|
|
1215
|
-
stats.tokens += prepared.resourceLedger.spentTokens;
|
|
1216
|
-
stats.turns += prepared.resourceLedger.spentTurns;
|
|
1217
|
-
}
|
|
1218
|
-
if (prepared.humanReviewRef.count > 0) {
|
|
1219
|
-
stats.humanReview = {
|
|
1220
|
-
count: prepared.humanReviewRef.count,
|
|
1221
|
-
totalWaitMs: prepared.humanReviewRef.totalWaitMs,
|
|
1222
|
-
gates: [...prepared.humanReviewRef.gates],
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
const reminderDisclosuresActive = Object.keys(prepared.reminderDisclosureCounts).length > 0;
|
|
1226
|
-
if (rs.counters.finalVerifyInjections > 0 || rs.attach.attachmentsInjected > 0 || rs.counters.repetitionCuts > 0 || rs.counters.repetitionSpared > 0 || rs.counters.approachNoticesSent > 0 || reminderDisclosuresActive) {
|
|
1227
|
-
stats.mechanisms = {
|
|
1228
|
-
...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjected: true } : {}),
|
|
1229
|
-
...(rs.counters.finalVerifyInjections > 0 ? { finalVerifyInjections: rs.counters.finalVerifyInjections } : {}),
|
|
1230
|
-
...(rs.attach.attachmentsInjected > 0 ? { attachmentsInjected: rs.attach.attachmentsInjected } : {}),
|
|
1231
|
-
...(rs.counters.approachNoticesSent > 0 ? { approachNoticesSent: rs.counters.approachNoticesSent } : {}),
|
|
1232
|
-
...(rs.counters.repetitionCuts > 0 ? { repetitionCuts: rs.counters.repetitionCuts } : {}),
|
|
1233
|
-
...(rs.counters.repetitionSpared > 0 ? { repetitionSpared: rs.counters.repetitionSpared } : {}),
|
|
1234
|
-
...(rs.counters.repetitionEvents.length > 0 ? { repetitionEvents: rs.counters.repetitionEvents } : {}),
|
|
1235
|
-
...(reminderDisclosuresActive ? { reminderDisclosures: { ...prepared.reminderDisclosureCounts } } : {}),
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
|
-
const result = assembleResult(spec, prepared.sessionId, final, stats, {
|
|
1239
|
-
threw,
|
|
1240
|
-
runId: prepared.runId,
|
|
1241
|
-
model: prepared.model.id,
|
|
1242
|
-
unpricedSpend: rs.telemetry.unpricedSpend,
|
|
1243
|
-
rewindNotes: prepared.rewindNotes,
|
|
1244
|
-
editedFiles: prepared.editedFilesSnapshot(),
|
|
1245
|
-
haltedOnUserRejection: prepared.batchHaltRef.current !== undefined,
|
|
1246
|
-
userHalted: loopLatch.userHalted,
|
|
1247
|
-
strandedHumanAnswers,
|
|
1248
|
-
remoteEnvFailures: prepared.remoteEnvFailures,
|
|
1249
|
-
effectiveReadFace: prepared.effectiveReadFace,
|
|
1250
|
-
effectiveReadDenyPatterns: prepared.effectiveReadDenyPatterns,
|
|
1251
|
-
effectiveMemoryScopes: prepared.effectiveMemoryScopes,
|
|
1252
|
-
effectiveReasoning: reasoningResolution,
|
|
1253
|
-
retryAfterMs: rs.limits.platformTerminal?.retryAfterMs,
|
|
1254
|
-
abortedForTimeout: timeout.fired,
|
|
1255
|
-
abortedForTurns: rs.limits.turnsExceeded,
|
|
1256
|
-
abortedLive,
|
|
1257
|
-
budgetHit: rs.limits.budgetHit,
|
|
1258
|
-
budgetAxis: rs.limits.budgetAxis,
|
|
1259
|
-
blockedReason: prepared.blockedRef.reason,
|
|
1260
|
-
conflict: prepared.conflictRef.hit,
|
|
1261
|
-
gitCoreOverBudget: prepared.gitStatusRef.terminalCode === "irreducible_core_over_budget",
|
|
1262
|
-
outputInvalid: rs.degrade.outputInvalid,
|
|
1263
|
-
suspendLoop: prepared.suspendLoopRef.hit,
|
|
1264
|
-
...(prepared.pausedRef.current !== undefined ? { paused: prepared.pausedRef.current.cause } : {}),
|
|
1265
|
-
});
|
|
1266
|
-
if (resume !== undefined &&
|
|
1267
|
-
resume.pendingActionStarted !== true &&
|
|
1268
|
-
result.terminal.kind !== "completed" &&
|
|
1269
|
-
result.terminal.kind !== "paused" &&
|
|
1270
|
-
!(resumeDecisionWasNegative(resume) && resume.decisionDelivered === true) &&
|
|
1271
|
-
resume.onEnvRestoreFailed !== undefined) {
|
|
1272
|
-
try {
|
|
1273
|
-
await resume.onEnvRestoreFailed("env_failed");
|
|
1274
|
-
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`);
|
|
1275
|
-
}
|
|
1276
|
-
catch {
|
|
1277
|
-
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`);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
prepared.sealReadStateSeat?.(ownCommittedTailRef.current);
|
|
1281
|
-
{
|
|
1282
|
-
const wss = prepared.workspaceStateSettle;
|
|
1283
|
-
if (wss !== undefined && result.terminal.kind !== "paused") {
|
|
1284
|
-
const curCwd = prepared.cwdRef?.current !== undefined && prepared.cwdRef.current !== wss.rootCanonical ? prepared.cwdRef.current : undefined;
|
|
1285
|
-
const curWt = prepared.worktreeSessionRef?.current;
|
|
1286
|
-
const changed = curCwd !== wss.restoredHandsCwd || curWt?.worktreeDir !== wss.restoredWorktreeDir;
|
|
1287
|
-
if (curCwd !== undefined || curWt !== undefined || changed || wss.baselineUnknown === true) {
|
|
1288
|
-
await settleTeardownLeg(() => prepared.session.appendWorkspaceState({
|
|
1289
|
-
taskRoot: wss.rootCanonical,
|
|
1290
|
-
...(curCwd !== undefined ? { handsCwd: curCwd } : {}),
|
|
1291
|
-
...(curWt !== undefined ? { activeWorktree: { ...curWt } } : {}),
|
|
1292
|
-
}), "session.appendWorkspaceState (settle leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
const stopFailureHook = (spec.hooks ?? this.deps.hooks)?.stopFailure;
|
|
1297
|
-
if (stopFailureHook !== undefined &&
|
|
1298
|
-
result.terminal.kind === "failed" &&
|
|
1299
|
-
final?.stopReason === "error" &&
|
|
1300
|
-
!isDegenerateCutMessage(final) &&
|
|
1301
|
-
threw === undefined &&
|
|
1302
|
-
rs.limits.budgetHit === undefined &&
|
|
1303
|
-
!rs.degrade.outputInvalid &&
|
|
1304
|
-
!prepared.suspendLoopRef.hit &&
|
|
1305
|
-
!abortedLive &&
|
|
1306
|
-
result.terminal.code !== "conflict") {
|
|
1307
|
-
const failedCause = result.terminal;
|
|
1308
|
-
try {
|
|
1309
|
-
const failureSeat = await runHookSeat("stopFailure", { timeoutMs: prepared.hookTimeoutMs }, (sig) => stopFailureHook({
|
|
1310
|
-
identity: prepared.hookIdentity,
|
|
1311
|
-
error: failedCause.message ?? "model error",
|
|
1312
|
-
...(failedCause.code !== undefined ? { errorKind: failedCause.code } : {}),
|
|
1313
|
-
turns: stats.turns,
|
|
1314
|
-
signal: sig,
|
|
1315
|
-
}));
|
|
1316
|
-
if (failureSeat.expired) {
|
|
1317
|
-
try {
|
|
1318
|
-
this.deps.onError?.(hookSeatExpiredError("stopFailure", prepared.hookTimeoutMs, failureSeat.cause, "the terminal observation was abandoned; the assembled TaskResult is unchanged"), { phase: "hook", sessionId: prepared.sessionId });
|
|
1319
|
-
}
|
|
1320
|
-
catch {
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
catch (err) {
|
|
1325
|
-
try {
|
|
1326
|
-
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId: prepared.sessionId });
|
|
1327
|
-
}
|
|
1328
|
-
catch {
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
const terminalFace = terminalProjection(result.terminal);
|
|
1333
|
-
emitDelegationLifecycle({
|
|
1334
|
-
phase: "terminal",
|
|
1335
|
-
identity: prepared.hookIdentity,
|
|
1336
|
-
status: terminalFace.status,
|
|
1337
|
-
turns: stats.turns,
|
|
1338
|
-
...(terminalFace.errorCode !== undefined ? { errorCode: terminalFace.errorCode } : {}),
|
|
1339
|
-
});
|
|
1340
|
-
if (taskIdRef !== undefined)
|
|
1341
|
-
taskIdRef.delegationTerminalOwed = undefined;
|
|
1342
|
-
if (rs.degrade.degraded)
|
|
1343
|
-
result.degraded = rs.degrade.degraded;
|
|
1344
|
-
if (prepared.outputRef.set)
|
|
1345
|
-
result.structuredOutput = prepared.outputRef.value;
|
|
1346
|
-
const committedToken = prepared.pausedRef.current?.cause.token;
|
|
1347
|
-
const committedScope = prepared.pausedRef.current?.scope;
|
|
1348
|
-
if (committedToken !== undefined) {
|
|
1349
|
-
const store = resolveCheckpointStore(spec, this.deps);
|
|
1350
|
-
if (store && committedScope !== undefined) {
|
|
1351
|
-
onSuspend({
|
|
1352
|
-
env: prepared.ownedEnv,
|
|
1353
|
-
token: committedToken,
|
|
1354
|
-
scope: committedScope,
|
|
1355
|
-
store,
|
|
1356
|
-
sessionId: prepared.sessionId,
|
|
1357
|
-
});
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
if (resume && committedToken === undefined) {
|
|
1361
|
-
await settleTeardownLeg(() => this.sessions.unpin?.(resume.cp.sessionId), "sessions.unpin (resumed-terminal leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1362
|
-
}
|
|
1363
|
-
if (prepared.memoryEngineSession) {
|
|
1364
|
-
const mes = prepared.memoryEngineSession;
|
|
1365
|
-
await settleTeardownLeg(() => mes.harvest(), "memoryEngineSession.harvest (settle leg)", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1366
|
-
}
|
|
1367
|
-
emitTrace(rs.telemetry.tracer, () => ({
|
|
1368
|
-
kind: "task.end",
|
|
1369
|
-
version: 1,
|
|
1370
|
-
taskId: rs.telemetry.taskId,
|
|
1371
|
-
runId: rs.telemetry.runId,
|
|
1372
|
-
status: terminalFace.status,
|
|
1373
|
-
errorCode: terminalFace.errorCode,
|
|
1374
|
-
turns: stats.turns,
|
|
1375
|
-
tokens: stats.tokens,
|
|
1376
|
-
...(rs.telemetry.unpricedSpend ? {} : { costMicroUsd: stats.costMicroUsd }),
|
|
1377
|
-
durationMs: Date.now() - rs.telemetry.taskStart,
|
|
1378
|
-
...(prepared.outputRef.set ? { hasStructuredOutput: true } : {}),
|
|
1379
|
-
...(stats.mechanisms !== undefined
|
|
1380
|
-
? { mechanisms: (({ repetitionEvents: _events, ...scalars }) => scalars)(stats.mechanisms) }
|
|
1381
|
-
: {}),
|
|
1382
|
-
...(timeout.latenessMs !== undefined && timeout.latenessMs > TIMER_LATENESS_REPORT_MS
|
|
1383
|
-
? { timerLatenessMs: timeout.latenessMs }
|
|
1384
|
-
: {}),
|
|
1385
|
-
...(rs.counters.walltimeSyncBackstopFired ? { walltimeSyncBackstop: true } : {}),
|
|
1386
|
-
ts: Date.now(),
|
|
1387
|
-
}));
|
|
1388
|
-
setResult(result);
|
|
1389
|
-
if (parentToolCallId !== undefined && result.terminal.kind !== "paused") {
|
|
1390
|
-
const terminalTick = {
|
|
1391
|
-
type: "task_progress",
|
|
1392
|
-
taskId: rs.telemetry.taskId,
|
|
1393
|
-
...(internals?.delegationTaskType !== undefined ? { taskType: internals.delegationTaskType } : {}),
|
|
1394
|
-
...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
|
|
1395
|
-
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
1396
|
-
...(subagentName ? { name: subagentName } : {}),
|
|
1397
|
-
model: prepared.model.id,
|
|
1398
|
-
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
|
|
1399
|
-
status: result.terminal.kind === "completed" ? "completed" : "failed",
|
|
1400
|
-
...ident(),
|
|
1401
|
-
};
|
|
1402
|
-
queue.push(terminalTick);
|
|
1403
|
-
try {
|
|
1404
|
-
internals?.onForwardEvent?.(terminalTick);
|
|
1405
|
-
}
|
|
1406
|
-
catch {
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
if (manualCompactRef.waiters.length > 0) {
|
|
1410
|
-
queue.push({ type: "compaction_outcome", outcome: "mooted", trigger: "manual", reason: "task_ending", ...ident() });
|
|
1411
|
-
drainManualCompact("mooted");
|
|
1412
|
-
}
|
|
1413
|
-
manualCompactRef.emitMooted = undefined;
|
|
1414
|
-
if (forwardsSubagentEvents(spec))
|
|
1415
|
-
await drainForwardedFramesBeforeDone();
|
|
1416
|
-
queue.push({ type: "done", result });
|
|
1417
|
-
queue.close();
|
|
1418
|
-
await prepared.fileHistoryBoundary?.settle();
|
|
1419
|
-
if (spec.suggestNextPrompts && result.terminal.kind === "completed") {
|
|
1420
|
-
onSuggestions(this.suggestNextPrompts(spec, prepared, result));
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
finally {
|
|
1424
|
-
const ownedEnvDying = prepared.ownedEnv !== undefined &&
|
|
1425
|
-
hasDestroy(prepared.ownedEnv) &&
|
|
1426
|
-
isIsolated(prepared.ownedEnv) &&
|
|
1427
|
-
prepared.pausedRef.current === undefined;
|
|
1428
|
-
await settleTeardownLeg(() => defaultTaskRegistry.settleKilledForOwner({ owner: spec.taskId ?? prepared.sessionId, scope: spec.principal ?? "default", sessionId: prepared.sessionId }, {
|
|
1429
|
-
source: abortedLive &&
|
|
1430
|
-
!timeout.fired &&
|
|
1431
|
-
!rs.limits.turnsExceeded &&
|
|
1432
|
-
rs.limits.budgetHit === undefined &&
|
|
1433
|
-
prepared.pausedRef.current === undefined
|
|
1434
|
-
? "user"
|
|
1435
|
-
: "parent",
|
|
1436
|
-
skipSessionScoped: true,
|
|
1437
|
-
envDying: ownedEnvDying,
|
|
1438
|
-
retainProcesses: spec.retainBackgroundProcesses === true,
|
|
1439
|
-
}), "taskRegistry.settleKilledForOwner", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1440
|
-
const bgEnv = prepared.ownedEnv ?? this.deps.executionEnv;
|
|
1441
|
-
const retainBgBySpec = spec.retainBackgroundProcesses === true && !ownedEnvDying;
|
|
1442
|
-
if (bgEnv && hasBackgroundShell(bgEnv) && !retainBgBySpec) {
|
|
1443
|
-
try {
|
|
1444
|
-
const keepAlive = ownedEnvDying && bgEnv === prepared.ownedEnv
|
|
1445
|
-
? []
|
|
1446
|
-
: [
|
|
1447
|
-
...defaultTaskRegistry.sessionResidentShellIds(prepared.sessionId, bgEnv),
|
|
1448
|
-
...defaultTaskRegistry.timeoutResidentShellIds(bgEnv),
|
|
1449
|
-
...defaultTaskRegistry.retainedShellIds(bgEnv),
|
|
1450
|
-
];
|
|
1451
|
-
await settleTeardownLeg(() => sweepBackgroundShells(bgEnv, defaultTaskRegistry, keepAlive.length > 0 ? { except: keepAlive } : undefined), "sweepBackgroundShells", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1452
|
-
}
|
|
1453
|
-
catch (e) {
|
|
1454
|
-
this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId });
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
defaultTaskRegistry.clearBackgroundForOwner({
|
|
1458
|
-
owner: spec.taskId ?? prepared.sessionId,
|
|
1459
|
-
scope: spec.principal ?? "default",
|
|
1460
|
-
});
|
|
1461
|
-
defaultTaskRegistry.abortBackgroundAgentsForOwner({
|
|
1462
|
-
owner: spec.taskId ?? prepared.sessionId,
|
|
1463
|
-
scope: spec.principal ?? "default",
|
|
1464
|
-
}, { skipSessionScoped: true });
|
|
1465
|
-
if (prepared.subagentRetain) {
|
|
1466
|
-
await settleTeardownLeg(() => prepared.subagentRetain.disposeAll(), "subagentRetain.disposeAll", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1467
|
-
}
|
|
1468
|
-
await settleTeardownLeg(() => this.teardownOwnedEnv(prepared), "teardownOwnedEnv", (e) => this.deps.onError?.(e, { phase: "config", sessionId: prepared.sessionId }));
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
async suggestNextPrompts(spec, prepared, result) {
|
|
1472
|
-
const cfg = typeof spec.suggestNextPrompts === "object" ? spec.suggestNextPrompts : {};
|
|
1473
|
-
const count = cfg.count && cfg.count > 0 ? Math.min(cfg.count, SUGGESTIONS_MAX_COUNT) : SUGGESTIONS_DEFAULT_COUNT;
|
|
1474
|
-
const ac = new AbortController();
|
|
1475
|
-
let timer;
|
|
1476
|
-
const timeout = new Promise((resolve) => {
|
|
1477
|
-
timer = setTimeout(() => {
|
|
1478
|
-
ac.abort();
|
|
1479
|
-
resolve([]);
|
|
1480
|
-
}, SUGGESTIONS_TIMEOUT_MS);
|
|
471
|
+
},
|
|
1481
472
|
});
|
|
1482
|
-
const work = (async () => {
|
|
1483
|
-
let model = prepared.model;
|
|
1484
|
-
let thinking = prepared.thinking;
|
|
1485
|
-
try {
|
|
1486
|
-
const resolved = resolveTaskModel({ modelRole: cfg.role ?? "summarize", roles: spec.roles }, this.deps);
|
|
1487
|
-
model = resolved.model;
|
|
1488
|
-
thinking = resolved.thinking;
|
|
1489
|
-
}
|
|
1490
|
-
catch (roleErr) {
|
|
1491
|
-
if (cfg.role !== undefined) {
|
|
1492
|
-
let asked;
|
|
1493
|
-
try {
|
|
1494
|
-
asked = String(cfg.role);
|
|
1495
|
-
}
|
|
1496
|
-
catch {
|
|
1497
|
-
asked = `<unrenderable ${typeof cfg.role}>`;
|
|
1498
|
-
}
|
|
1499
|
-
try {
|
|
1500
|
-
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 });
|
|
1501
|
-
}
|
|
1502
|
-
catch {
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
if (!sameRouteIdentity(model, prepared.model)) {
|
|
1507
|
-
const verdict = await adjudicateDerivedRoute({ brain: this.deps.brain, model, getApiKeyAndHeaders: spec.getApiKeyAndHeaders });
|
|
1508
|
-
if (verdict !== undefined && !verdict.ok) {
|
|
1509
|
-
deliverEngineNotice(this.deps.onNotice, fallbackToPrimaryNotice({ seat: "prompt-suggestions", from: model.id, to: prepared.model.id, verdict, sessionId: prepared.sessionId, runId: prepared.runId }));
|
|
1510
|
-
model = prepared.model;
|
|
1511
|
-
thinking = prepared.thinking;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
const pricing = this.deps.pricing?.[model.id] ?? modelCostToPricing(model.cost);
|
|
1515
|
-
const ctx = await prepared.session.buildContext();
|
|
1516
|
-
const transcript = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m)).slice(-SUGGESTIONS_TRANSCRIPT_MESSAGES);
|
|
1517
|
-
const out = await generatePromptSuggestions({ brain: this.deps.brain, model, pricing, thinking, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, signal: ac.signal }, transcript, count);
|
|
1518
|
-
if (out.tokens > 0 || out.costMicroUsd > 0) {
|
|
1519
|
-
result.stats.suggestions = { tokens: out.tokens, costMicroUsd: out.costMicroUsd };
|
|
1520
|
-
if (result.stats.costBreakdown) {
|
|
1521
|
-
result.stats.costBreakdown.suggestionsMicroUsd = out.costMicroUsd;
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
return out.suggestions;
|
|
1525
|
-
})();
|
|
1526
|
-
work.catch(() => { });
|
|
1527
|
-
try {
|
|
1528
|
-
return await Promise.race([work, timeout]);
|
|
1529
|
-
}
|
|
1530
|
-
catch (err) {
|
|
1531
|
-
try {
|
|
1532
|
-
this.deps.onError?.(err, { phase: "suggestions", sessionId: prepared.sessionId });
|
|
1533
|
-
}
|
|
1534
|
-
catch {
|
|
1535
|
-
}
|
|
1536
|
-
return [];
|
|
1537
|
-
}
|
|
1538
|
-
finally {
|
|
1539
|
-
clearTimeout(timer);
|
|
1540
|
-
}
|
|
1541
473
|
}
|
|
1542
474
|
async runTask(spec, internals) {
|
|
1543
475
|
const stream = this.runTaskStream(spec, undefined, internals);
|
|
@@ -1638,412 +570,6 @@ export class Runner {
|
|
|
1638
570
|
}
|
|
1639
571
|
return this.runTaskStream(spec, { cp, outcome: outcomeForStore, onEnvRestoreFailed, recordExecutionOutcome, ...(wakeMessage !== undefined ? { wakeMessage } : {}), ...(wakeHookContext !== undefined ? { wakeMessageHookContext: wakeHookContext } : {}) }, resumeInternals);
|
|
1640
572
|
}
|
|
1641
|
-
async applyResumeDecision(prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
|
|
1642
|
-
const { pendingAction } = resume.cp;
|
|
1643
|
-
if (resume.outcome.gate === "dry_run_review")
|
|
1644
|
-
return;
|
|
1645
|
-
if (resume.outcome.gate === "plan_review")
|
|
1646
|
-
return;
|
|
1647
|
-
if (pendingAction.kind !== "tool_approval")
|
|
1648
|
-
return;
|
|
1649
|
-
if (resume.outcome.gate === "policy_ask" &&
|
|
1650
|
-
resume.outcome.decision === "allow" &&
|
|
1651
|
-
!prepared.tools.some((t) => t.name === pendingAction.toolName)) {
|
|
1652
|
-
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`);
|
|
1653
|
-
e.code = "resume.tool_unavailable";
|
|
1654
|
-
throw e;
|
|
1655
|
-
}
|
|
1656
|
-
const liveRoster = prepared.toolRosterDeltas.current;
|
|
1657
|
-
if (pendingAction.kind === "tool_approval" && liveRoster !== undefined && judgeParkedToolIdentity(pendingAction, rosterEntryNamed(liveRoster, pendingAction.toolName)) === "mismatch") {
|
|
1658
|
-
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`);
|
|
1659
|
-
e.code = "resume.tool_contract_mismatch";
|
|
1660
|
-
throw e;
|
|
1661
|
-
}
|
|
1662
|
-
const completed = new Set(pendingAction.completedCallIds);
|
|
1663
|
-
const deferredIds = pendingAction.batchToolCallIds.filter((id) => id !== pendingAction.toolCallId && !completed.has(id));
|
|
1664
|
-
const names = new Map();
|
|
1665
|
-
const deferredArgs = new Map();
|
|
1666
|
-
if (deferredIds.length > 0) {
|
|
1667
|
-
const { messages } = await prepared.session.buildContext();
|
|
1668
|
-
for (const m of messages) {
|
|
1669
|
-
if (m.role === "assistant") {
|
|
1670
|
-
for (const c of m.content) {
|
|
1671
|
-
if (c.type === "toolCall") {
|
|
1672
|
-
names.set(c.id, c.name);
|
|
1673
|
-
deferredArgs.set(c.id, c.arguments);
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
for (const id of pendingAction.batchToolCallIds) {
|
|
1680
|
-
if (id === pendingAction.toolCallId) {
|
|
1681
|
-
await this.resolvePendingCall(prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart);
|
|
1682
|
-
}
|
|
1683
|
-
else if (!completed.has(id)) {
|
|
1684
|
-
const name = names.get(id) ?? "unknown";
|
|
1685
|
-
const displayLabel = (() => {
|
|
1686
|
-
const l = prepared.tools.find((t) => t.name === name)?.label;
|
|
1687
|
-
return l !== undefined && l !== name ? { label: l } : {};
|
|
1688
|
-
})();
|
|
1689
|
-
emit({ type: "tool_start", toolCallId: id, toolName: name, ...displayLabel, args: deferredArgs.get(id) ?? {} });
|
|
1690
|
-
emit({ type: "tool_end", toolCallId: id, toolName: name, ...displayLabel, isError: true, ...toolEndBodyFrom({ content: formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark) }, true, prepared.structuredProjector) });
|
|
1691
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(id, name, formatHookFeedback(DEFERRED_REISSUE, prepared.reminderMark), true));
|
|
1692
|
-
emitCommitted(eid, "toolResult", id);
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
async resolvePendingCall(prepared, resume, emit, emitCommitted, onResolvedToolSuccess, onExecuteStart) {
|
|
1697
|
-
const { pendingAction } = resume.cp;
|
|
1698
|
-
const { outcome } = resume;
|
|
1699
|
-
if (pendingAction.kind !== "tool_approval" || outcome.gate !== "policy_ask")
|
|
1700
|
-
return;
|
|
1701
|
-
const decidedGate = outcome.gateOutcome;
|
|
1702
|
-
if (decidedGate === undefined) {
|
|
1703
|
-
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");
|
|
1704
|
-
}
|
|
1705
|
-
const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
|
|
1706
|
-
const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
|
|
1707
|
-
emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs });
|
|
1708
|
-
const settleEnd = async (isError, result, gate = decidedGate) => {
|
|
1709
|
-
emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError, prepared.structuredProjector, gate) });
|
|
1710
|
-
const word = await resume.recordExecutionOutcome(gate);
|
|
1711
|
-
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 } : {}) });
|
|
1712
|
-
};
|
|
1713
|
-
const vetoed = (layer) => mintGateOutcome({
|
|
1714
|
-
deniedBy: layer,
|
|
1715
|
-
...(outcome.gateOutcome?.settlement !== undefined && outcome.gateOutcome.origin !== undefined ? { settled: { settlement: outcome.gateOutcome.settlement, origin: outcome.gateOutcome.origin } } : {}),
|
|
1716
|
-
});
|
|
1717
|
-
if (outcome.decision === "deny") {
|
|
1718
|
-
const defaultDenial = outcome.gateOutcome?.settlement?.kind === "park_sla_expired"
|
|
1719
|
-
? `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.`
|
|
1720
|
-
: `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
|
|
1721
|
-
const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason, REVIEWER_NOTE_MAX_BODY) : defaultDenial;
|
|
1722
|
-
await settleEnd(true, { content: formatHookFeedback(reason, prepared.reminderMark) });
|
|
1723
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason, prepared.reminderMark), true));
|
|
1724
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1725
|
-
return;
|
|
1726
|
-
}
|
|
1727
|
-
if (outcome.decision !== "allow") {
|
|
1728
|
-
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`);
|
|
1729
|
-
}
|
|
1730
|
-
const beltBudget = {
|
|
1731
|
-
resourceRemainingMicroUsd: remainingBudgetMicroUsd(prepared.resourceLedger),
|
|
1732
|
-
resourceSpentMicroUsd: prepared.resourceLedger?.spentMicroUsd ?? 0,
|
|
1733
|
-
suspendCount: resume.cp.suspendCount ?? 0,
|
|
1734
|
-
};
|
|
1735
|
-
if (outcome.decision === "allow" && outcome.updatedInput !== undefined && prepared.basePolicyForResumeEdit) {
|
|
1736
|
-
const rechecked = refuseOutOfContractDecision(await prepared.basePolicyForResumeEdit.check({
|
|
1737
|
-
toolName: pendingAction.toolName,
|
|
1738
|
-
args: resolvedArgs,
|
|
1739
|
-
toolCallId: pendingAction.toolCallId,
|
|
1740
|
-
budget: beltBudget,
|
|
1741
|
-
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
1742
|
-
...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
|
|
1743
|
-
}, prepared.abortController.signal));
|
|
1744
|
-
const beltUnsafe = rechecked.action === "deny" ||
|
|
1745
|
-
(rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs));
|
|
1746
|
-
if (beltUnsafe) {
|
|
1747
|
-
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);
|
|
1748
|
-
await settleEnd(true, { content: editedDenial }, vetoed("policy"));
|
|
1749
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, editedDenial, true));
|
|
1750
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1751
|
-
return;
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
const gateRealApproval = resume.cp.gate.kind === "irreversible_ask" ? resume.cp.gate.realApproval : undefined;
|
|
1755
|
-
const gateOrgGoverned = realApprovalOrgFact(gateRealApproval) !== undefined;
|
|
1756
|
-
if (gateOrgGoverned && prepared.permissionRuleOrg === undefined) {
|
|
1757
|
-
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);
|
|
1758
|
-
await settleEnd(true, { content: unwiredDenial }, vetoed("org"));
|
|
1759
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, unwiredDenial, true));
|
|
1760
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1761
|
-
return;
|
|
1762
|
-
}
|
|
1763
|
-
if (prepared.permissionRuleOrg !== undefined) {
|
|
1764
|
-
const orgVerdict = prepared.permissionRuleOrg
|
|
1765
|
-
.adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId })
|
|
1766
|
-
.catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
|
|
1767
|
-
let orgWaitCancelled = false;
|
|
1768
|
-
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)`] }, {
|
|
1769
|
-
signal: prepared.abortController.signal,
|
|
1770
|
-
timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS,
|
|
1771
|
-
onFallback: (cause) => {
|
|
1772
|
-
orgWaitCancelled = cause === "aborted";
|
|
1773
|
-
},
|
|
1774
|
-
});
|
|
1775
|
-
const blocked = org.status === "unavailable"
|
|
1776
|
-
? realApprovalOrgFact(gateRealApproval) === "unavailable"
|
|
1777
|
-
? undefined
|
|
1778
|
-
: orgWaitCancelled
|
|
1779
|
-
? "this deployment is org-governed and the task was cancelled before the organization policy snapshot could be adjudicated"
|
|
1780
|
-
: "this deployment is org-governed and cannot currently adjudicate against an organization policy snapshot"
|
|
1781
|
-
: org.verdict?.behavior === "deny"
|
|
1782
|
-
? `an organization policy rule (${org.verdict.rule}) denies it`
|
|
1783
|
-
: undefined;
|
|
1784
|
-
if (blocked !== undefined) {
|
|
1785
|
-
const orgDisclosures = org.status === "unavailable" && org.disclosures.length > 0
|
|
1786
|
-
? ` Governance disclosures: ${inlineUntrusted(org.disclosures.join("; "), ORG_DISCLOSURE_MAX_CHARS)}.`
|
|
1787
|
-
: "";
|
|
1788
|
-
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);
|
|
1789
|
-
await settleEnd(true, { content: orgDenial }, vetoed("org"));
|
|
1790
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, orgDenial, true));
|
|
1791
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1792
|
-
return;
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
if (prepared.denyNarrowingPolicy) {
|
|
1796
|
-
const narrowed = refuseOutOfContractDecision(await prepared.denyNarrowingPolicy.check({
|
|
1797
|
-
toolName: pendingAction.toolName,
|
|
1798
|
-
args: resolvedArgs,
|
|
1799
|
-
toolCallId: pendingAction.toolCallId,
|
|
1800
|
-
...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
|
|
1801
|
-
budget: beltBudget,
|
|
1802
|
-
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
1803
|
-
}, prepared.abortController.signal));
|
|
1804
|
-
if (narrowed.action === "deny") {
|
|
1805
|
-
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);
|
|
1806
|
-
await settleEnd(true, { content: narrowedDenial }, vetoed("policy"));
|
|
1807
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, narrowedDenial, true));
|
|
1808
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1809
|
-
return;
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
if (prepared.persistedRuleLane !== undefined) {
|
|
1813
|
-
const unreadable = { unreadable: true };
|
|
1814
|
-
const read = await settleOrgVerdictWithin(Promise.resolve()
|
|
1815
|
-
.then(() => prepared.persistedRuleLane.adjudicate({
|
|
1816
|
-
toolName: pendingAction.toolName,
|
|
1817
|
-
args: resolvedArgs,
|
|
1818
|
-
toolCallId: pendingAction.toolCallId,
|
|
1819
|
-
...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
|
|
1820
|
-
budget: beltBudget,
|
|
1821
|
-
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
1822
|
-
}))
|
|
1823
|
-
.then((answer) => normalizePersistedRuleHit(answer)), unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
|
|
1824
|
-
const tightened = applyPersistedTightening({ action: "allow" }, read);
|
|
1825
|
-
if (tightened.tightened === "deny" || tightened.tightened === "unreadable") {
|
|
1826
|
-
const why = tightened.tightened === "deny"
|
|
1827
|
-
? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
|
|
1828
|
-
: "your persisted permission rules could not be read on this leg";
|
|
1829
|
-
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);
|
|
1830
|
-
await settleEnd(true, { content: ruleDenial }, vetoed("persisted_rule"));
|
|
1831
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, ruleDenial, true));
|
|
1832
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1833
|
-
return;
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
|
|
1837
|
-
if (!tool) {
|
|
1838
|
-
const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
|
|
1839
|
-
e.code = "resume.tool_unavailable";
|
|
1840
|
-
throw e;
|
|
1841
|
-
}
|
|
1842
|
-
const args = resolvedArgs;
|
|
1843
|
-
onExecuteStart?.(pendingAction.toolCallId);
|
|
1844
|
-
prepared.suspendProgressRef.executedApproved = true;
|
|
1845
|
-
let res;
|
|
1846
|
-
try {
|
|
1847
|
-
res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
|
|
1848
|
-
}
|
|
1849
|
-
catch (err) {
|
|
1850
|
-
const execError = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
1851
|
-
const marks = (() => {
|
|
1852
|
-
if (err === null || typeof err !== "object")
|
|
1853
|
-
return undefined;
|
|
1854
|
-
const src = err;
|
|
1855
|
-
let d;
|
|
1856
|
-
if (src.details !== null && typeof src.details === "object" && !Array.isArray(src.details)) {
|
|
1857
|
-
d = { ...src.details };
|
|
1858
|
-
}
|
|
1859
|
-
if (typeof src.errorKind === "string")
|
|
1860
|
-
d = { ...(d ?? {}), errorKind: src.errorKind };
|
|
1861
|
-
return d;
|
|
1862
|
-
})();
|
|
1863
|
-
await settleEnd(true, { content: execError, ...(marks !== undefined ? { details: marks } : {}) });
|
|
1864
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, execError, true));
|
|
1865
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1866
|
-
return;
|
|
1867
|
-
}
|
|
1868
|
-
const executedIsError = res.isError === true;
|
|
1869
|
-
await settleEnd(executedIsError, res);
|
|
1870
|
-
onResolvedToolSuccess?.(pendingAction.toolName, executedIsError ? undefined : res.details);
|
|
1871
|
-
const eid = await prepared.session.appendMessage({
|
|
1872
|
-
role: "toolResult",
|
|
1873
|
-
toolCallId: pendingAction.toolCallId,
|
|
1874
|
-
toolName: pendingAction.toolName,
|
|
1875
|
-
content: res.content,
|
|
1876
|
-
details: res.details,
|
|
1877
|
-
isError: executedIsError,
|
|
1878
|
-
timestamp: Date.now(),
|
|
1879
|
-
});
|
|
1880
|
-
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
1881
|
-
}
|
|
1882
|
-
seamCCompactionOptions(prepared) {
|
|
1883
|
-
if (!this.deps.summaryProvider)
|
|
1884
|
-
return undefined;
|
|
1885
|
-
return {
|
|
1886
|
-
summaryProvider: this.deps.summaryProvider,
|
|
1887
|
-
onCompaction: this.deps.onCompaction,
|
|
1888
|
-
maxConsecutiveProviderReuse: this.deps.maxConsecutiveProviderReuse,
|
|
1889
|
-
consecutiveProviderReuse: prepared.compactionReuseRef.consecutive,
|
|
1890
|
-
};
|
|
1891
|
-
}
|
|
1892
|
-
compactionHookOptions(spec, sessionId, trigger, identity, seatBound) {
|
|
1893
|
-
const hooks = spec.hooks ?? this.deps.hooks;
|
|
1894
|
-
const pre = hooks?.preCompact;
|
|
1895
|
-
const post = hooks?.postCompact;
|
|
1896
|
-
const seatMs = seatBound?.timeoutMs ?? resolveHookTimeoutMs(hooks?.timeoutMs);
|
|
1897
|
-
const seatSignal = seatBound?.signal;
|
|
1898
|
-
const withIdentity = (ctx) => identity !== undefined ? { ...ctx, identity } : ctx;
|
|
1899
|
-
return {
|
|
1900
|
-
trigger,
|
|
1901
|
-
...(pre
|
|
1902
|
-
? {
|
|
1903
|
-
preCompact: async (rawCtx) => {
|
|
1904
|
-
const ctx = withIdentity(rawCtx);
|
|
1905
|
-
const report = (err) => {
|
|
1906
|
-
try {
|
|
1907
|
-
this.deps.onError?.(err, { phase: "hook", sessionId });
|
|
1908
|
-
}
|
|
1909
|
-
catch {
|
|
1910
|
-
}
|
|
1911
|
-
};
|
|
1912
|
-
try {
|
|
1913
|
-
const seat = await runHookSeat("preCompact", { timeoutMs: seatMs, ...(seatSignal !== undefined ? { signal: seatSignal } : {}) }, (sig) => pre.call(hooks, { ...ctx, signal: sig }));
|
|
1914
|
-
if (seat.expired) {
|
|
1915
|
-
report(hookSeatExpiredError("preCompact", seatMs, seat.cause, `the "${ctx.trigger}" compaction PROCEEDED unblocked and with no additional instructions`));
|
|
1916
|
-
return undefined;
|
|
1917
|
-
}
|
|
1918
|
-
const r = seat.value;
|
|
1919
|
-
if (r?.block && ctx.trigger === "forced") {
|
|
1920
|
-
report(new Error(`a preCompact callback blocked a "forced" compaction — ignored (PTL/trim-pressure compaction is not optional): ${r.block}`));
|
|
1921
|
-
}
|
|
1922
|
-
return r;
|
|
1923
|
-
}
|
|
1924
|
-
catch (err) {
|
|
1925
|
-
report(err instanceof Error ? err : new Error(String(err)));
|
|
1926
|
-
return undefined;
|
|
1927
|
-
}
|
|
1928
|
-
},
|
|
1929
|
-
}
|
|
1930
|
-
: {}),
|
|
1931
|
-
...(post
|
|
1932
|
-
? {
|
|
1933
|
-
postCompact: async (rawCtx) => {
|
|
1934
|
-
try {
|
|
1935
|
-
const seat = await runHookSeat("postCompact", { timeoutMs: seatMs, ...(seatSignal !== undefined ? { signal: seatSignal } : {}) }, (sig) => post.call(hooks, { ...withIdentity(rawCtx), signal: sig }));
|
|
1936
|
-
if (seat.expired) {
|
|
1937
|
-
try {
|
|
1938
|
-
this.deps.onError?.(hookSeatExpiredError("postCompact", seatMs, seat.cause, "the observation was abandoned; the compaction that already landed is unchanged"), { phase: "hook", sessionId });
|
|
1939
|
-
}
|
|
1940
|
-
catch {
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
catch (err) {
|
|
1945
|
-
try {
|
|
1946
|
-
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
|
|
1947
|
-
}
|
|
1948
|
-
catch {
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
},
|
|
1952
|
-
}
|
|
1953
|
-
: {}),
|
|
1954
|
-
};
|
|
1955
|
-
}
|
|
1956
|
-
recordCompactionReuse(prepared, comp) {
|
|
1957
|
-
if (!this.deps.summaryProvider || !comp.compacted)
|
|
1958
|
-
return;
|
|
1959
|
-
prepared.compactionReuseRef.consecutive = comp.reused ? prepared.compactionReuseRef.consecutive + 1 : 0;
|
|
1960
|
-
}
|
|
1961
|
-
async finish(spec, prepared, opts) {
|
|
1962
|
-
let comp;
|
|
1963
|
-
if (!opts?.skipCompaction) {
|
|
1964
|
-
try {
|
|
1965
|
-
const finishComp = await maybeCompact({
|
|
1966
|
-
session: prepared.session,
|
|
1967
|
-
epochDeclaredSections: prepared.epochDeclaredSections,
|
|
1968
|
-
...centerAdoptionOption(prepared),
|
|
1969
|
-
model: prepared.model,
|
|
1970
|
-
compactionModel: prepared.compModel,
|
|
1971
|
-
...forkContextOption(prepared, false),
|
|
1972
|
-
brain: opts?.brain ?? this.deps.brain,
|
|
1973
|
-
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1974
|
-
thinking: prepared.thinking,
|
|
1975
|
-
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
1976
|
-
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
1977
|
-
minTokens: opts?.minTokens,
|
|
1978
|
-
overheadTokens: prepared.promptOverheadTokens,
|
|
1979
|
-
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
1980
|
-
onInputTruncated: emitInputTruncated(opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId),
|
|
1981
|
-
...(opts?.windowSafety ?? {}),
|
|
1982
|
-
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
1983
|
-
...contextInstructionFilesOption(prepared),
|
|
1984
|
-
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
1985
|
-
...this.seamCCompactionOptions(prepared),
|
|
1986
|
-
...gitRestateOption(prepared),
|
|
1987
|
-
...this.compactionHookOptions(spec, prepared.sessionId, "auto", prepared.hookIdentity, {
|
|
1988
|
-
timeoutMs: prepared.hookTimeoutMs,
|
|
1989
|
-
...(opts?.cancelled === true ? { signal: prepared.abortController.signal } : {}),
|
|
1990
|
-
}),
|
|
1991
|
-
});
|
|
1992
|
-
this.recordCompactionReuse(prepared, finishComp);
|
|
1993
|
-
if (finishComp.unevaluableWindow) {
|
|
1994
|
-
discloseUnevaluableWindow(finishComp.unevaluableWindow, prepared, opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId, this.deps.onError);
|
|
1995
|
-
}
|
|
1996
|
-
comp = finishComp;
|
|
1997
|
-
}
|
|
1998
|
-
catch (err) {
|
|
1999
|
-
const failMsg = String(err instanceof Error ? err.message : err);
|
|
2000
|
-
const failReason = failMsg.length > 512 ? `${failMsg.slice(0, 512)}…` : failMsg;
|
|
2001
|
-
emitTrace(opts?.telemetry?.tracer ?? spec.tracer ?? this.deps.tracer, () => ({
|
|
2002
|
-
kind: "compaction.failed",
|
|
2003
|
-
version: 1,
|
|
2004
|
-
taskId: opts?.telemetry?.taskId ?? spec.taskId ?? prepared.sessionId,
|
|
2005
|
-
trigger: "auto",
|
|
2006
|
-
reason: failReason,
|
|
2007
|
-
ts: Date.now(),
|
|
2008
|
-
}));
|
|
2009
|
-
opts?.onCompactionFailed?.(failReason);
|
|
2010
|
-
this.deps.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
2011
|
-
}
|
|
2012
|
-
}
|
|
2013
|
-
try {
|
|
2014
|
-
await this.sessions.touch(prepared.sessionId);
|
|
2015
|
-
}
|
|
2016
|
-
catch (err) {
|
|
2017
|
-
this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
|
|
2018
|
-
}
|
|
2019
|
-
try {
|
|
2020
|
-
await prepared.mcp.dispose();
|
|
2021
|
-
}
|
|
2022
|
-
catch (err) {
|
|
2023
|
-
this.deps.onError?.(err, { phase: "mcp", sessionId: prepared.sessionId });
|
|
2024
|
-
}
|
|
2025
|
-
if (prepared.a2a !== undefined) {
|
|
2026
|
-
try {
|
|
2027
|
-
await prepared.a2a.dispose();
|
|
2028
|
-
}
|
|
2029
|
-
catch (err) {
|
|
2030
|
-
this.deps.onError?.(err, { phase: "a2a", sessionId: prepared.sessionId });
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
return comp;
|
|
2034
|
-
}
|
|
2035
|
-
async teardownOwnedEnv(prepared) {
|
|
2036
|
-
if (prepared.ownedEnv &&
|
|
2037
|
-
hasDestroy(prepared.ownedEnv) &&
|
|
2038
|
-
prepared.pausedRef.current === undefined) {
|
|
2039
|
-
try {
|
|
2040
|
-
await prepared.ownedEnv.destroy();
|
|
2041
|
-
}
|
|
2042
|
-
catch (err) {
|
|
2043
|
-
this.deps.onError?.(err, { phase: "config", sessionId: prepared.sessionId });
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
573
|
}
|
|
2048
574
|
export function runTask(spec, deps) {
|
|
2049
575
|
return new Runner(deps).runTask(spec);
|