@caupulican/pi-adaptative 0.81.41 → 0.81.42
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 +16 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +5 -1
- package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +3 -3
- package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +8 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +26 -3
- package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +3 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +4 -1
- package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +2 -1
- package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +2 -0
- package/dist/core/agent-paths.d.ts +32 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +65 -0
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/agent-session-contracts.d.ts +19 -1
- package/dist/core/agent-session-contracts.d.ts.map +1 -1
- package/dist/core/agent-session-contracts.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -9
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +2 -0
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.js +4 -9
- package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +3 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +17 -4
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/path-scope.d.ts +14 -0
- package/dist/core/autonomy/path-scope.d.ts.map +1 -1
- package/dist/core/autonomy/path-scope.js +37 -6
- package/dist/core/autonomy/path-scope.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +27 -1
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +32 -1
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/delegation/managed-lane-controller.d.ts +11 -1
- package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -1
- package/dist/core/delegation/managed-lane-controller.js +84 -41
- package/dist/core/delegation/managed-lane-controller.js.map +1 -1
- package/dist/core/delegation/session-worker-claim.d.ts.map +1 -1
- package/dist/core/delegation/session-worker-claim.js +4 -3
- package/dist/core/delegation/session-worker-claim.js.map +1 -1
- package/dist/core/delegation/worker-action-journal.d.ts +73 -0
- package/dist/core/delegation/worker-action-journal.d.ts.map +1 -0
- package/dist/core/delegation/worker-action-journal.js +297 -0
- package/dist/core/delegation/worker-action-journal.js.map +1 -0
- package/dist/core/delegation/worker-actions.d.ts +36 -1
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +171 -46
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +75 -0
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-agent-control-coordinator.js +218 -0
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-agent-control.d.ts +66 -0
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -0
- package/dist/core/delegation/worker-agent-control.js +163 -0
- package/dist/core/delegation/worker-agent-control.js.map +1 -0
- package/dist/core/delegation/worker-attempt-executor.d.ts +80 -0
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -0
- package/dist/core/delegation/worker-attempt-executor.js +343 -0
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -0
- package/dist/core/delegation/worker-claim.d.ts +30 -0
- package/dist/core/delegation/worker-claim.d.ts.map +1 -1
- package/dist/core/delegation/worker-claim.js +205 -53
- package/dist/core/delegation/worker-claim.js.map +1 -1
- package/dist/core/delegation/worker-conversation-store.d.ts +116 -0
- package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -0
- package/dist/core/delegation/worker-conversation-store.js +442 -0
- package/dist/core/delegation/worker-conversation-store.js.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts +21 -5
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +386 -251
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-delegation-request.d.ts +14 -0
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-request.js.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts +4 -2
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.js +16 -6
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
- package/dist/core/delegation/worker-execution-policy.d.ts +2 -1
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
- package/dist/core/delegation/worker-execution-policy.js +5 -7
- package/dist/core/delegation/worker-execution-policy.js.map +1 -1
- package/dist/core/delegation/worker-lifecycle.d.ts +44 -1
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +120 -0
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-process-owner.d.ts +18 -0
- package/dist/core/delegation/worker-process-owner.d.ts.map +1 -0
- package/dist/core/delegation/worker-process-owner.js +54 -0
- package/dist/core/delegation/worker-process-owner.js.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts +5 -1
- package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-profile-resolver.js +8 -0
- package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.d.ts +54 -0
- package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-recovery-coordinator.js +181 -0
- package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-resource-catalog.d.ts +32 -0
- package/dist/core/delegation/worker-resource-catalog.d.ts.map +1 -0
- package/dist/core/delegation/worker-resource-catalog.js +116 -0
- package/dist/core/delegation/worker-resource-catalog.js.map +1 -0
- package/dist/core/delegation/worker-resource-materializer.d.ts +47 -0
- package/dist/core/delegation/worker-resource-materializer.d.ts.map +1 -0
- package/dist/core/delegation/worker-resource-materializer.js +137 -0
- package/dist/core/delegation/worker-resource-materializer.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +3 -1
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +189 -54
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-terminal-finalizer.d.ts +23 -0
- package/dist/core/delegation/worker-terminal-finalizer.d.ts.map +1 -0
- package/dist/core/delegation/worker-terminal-finalizer.js +12 -0
- package/dist/core/delegation/worker-terminal-finalizer.js.map +1 -0
- package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +38 -0
- package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-write-reservation-coordinator.js +107 -0
- package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-write-reservation.d.ts +90 -0
- package/dist/core/delegation/worker-write-reservation.d.ts.map +1 -0
- package/dist/core/delegation/worker-write-reservation.js +348 -0
- package/dist/core/delegation/worker-write-reservation.js.map +1 -0
- package/dist/core/models/model-endpoint.d.ts +3 -0
- package/dist/core/models/model-endpoint.d.ts.map +1 -0
- package/dist/core/models/model-endpoint.js +11 -0
- package/dist/core/models/model-endpoint.js.map +1 -0
- package/dist/core/orchestration/agent-resume.d.ts +2 -0
- package/dist/core/orchestration/agent-resume.d.ts.map +1 -1
- package/dist/core/orchestration/agent-resume.js +7 -0
- package/dist/core/orchestration/agent-resume.js.map +1 -1
- package/dist/core/orchestration/attempt-usage.d.ts +28 -0
- package/dist/core/orchestration/attempt-usage.d.ts.map +1 -0
- package/dist/core/orchestration/attempt-usage.js +168 -0
- package/dist/core/orchestration/attempt-usage.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +19 -1
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
- package/dist/core/orchestration/capability-gateway.js +128 -28
- package/dist/core/orchestration/capability-gateway.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +35 -0
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +17 -0
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/delegation-ledger.d.ts +17 -2
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
- package/dist/core/orchestration/delegation-ledger.js +78 -3
- package/dist/core/orchestration/delegation-ledger.js.map +1 -1
- package/dist/core/orchestration/event-store.d.ts +1 -0
- package/dist/core/orchestration/event-store.d.ts.map +1 -1
- package/dist/core/orchestration/event-store.js +78 -38
- package/dist/core/orchestration/event-store.js.map +1 -1
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
- package/dist/core/orchestration/profile-registry.js +81 -41
- package/dist/core/orchestration/profile-registry.js.map +1 -1
- package/dist/core/orchestration/profile-store.d.ts.map +1 -1
- package/dist/core/orchestration/profile-store.js +18 -4
- package/dist/core/orchestration/profile-store.js.map +1 -1
- package/dist/core/orchestration/risk-budget.d.ts +2 -0
- package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
- package/dist/core/orchestration/risk-budget.js +26 -0
- package/dist/core/orchestration/risk-budget.js.map +1 -1
- package/dist/core/orchestration/task-runtime.d.ts +13 -1
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime.js +127 -16
- package/dist/core/orchestration/task-runtime.js.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.d.ts +3 -1
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.js +75 -23
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
- package/dist/core/orchestration/worker-result-adapter.d.ts +9 -3
- package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -1
- package/dist/core/orchestration/worker-result-adapter.js +167 -14
- package/dist/core/orchestration/worker-result-adapter.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +1 -1
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +90 -54
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/research/evidence-bundle.d.ts +20 -0
- package/dist/core/research/evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/evidence-bundle.js +173 -69
- package/dist/core/research/evidence-bundle.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +7 -0
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +15 -1
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/session-artifact-bundle.d.ts +35 -0
- package/dist/core/session-artifact-bundle.d.ts.map +1 -0
- package/dist/core/session-artifact-bundle.js +61 -0
- package/dist/core/session-artifact-bundle.js.map +1 -0
- package/dist/core/tools/delegate-status.d.ts +6 -1
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +100 -18
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +9 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +323 -61
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/run-process.d.ts +1 -1
- package/dist/core/tools/run-process.d.ts.map +1 -1
- package/dist/core/tools/run-process.js +69 -9
- package/dist/core/tools/run-process.js.map +1 -1
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
- package/dist/core/tools/shell-contract-router.js +12 -5
- package/dist/core/tools/shell-contract-router.js.map +1 -1
- package/dist/core/util/bounded-file.d.ts +15 -0
- package/dist/core/util/bounded-file.d.ts.map +1 -0
- package/dist/core/util/bounded-file.js +120 -0
- package/dist/core/util/bounded-file.js.map +1 -0
- package/dist/core/util/bounded-jsonl.d.ts.map +1 -1
- package/dist/core/util/bounded-jsonl.js +119 -9
- package/dist/core/util/bounded-jsonl.js.map +1 -1
- package/dist/core/util/bounded-value.d.ts +3 -0
- package/dist/core/util/bounded-value.d.ts.map +1 -0
- package/dist/core/util/bounded-value.js +12 -0
- package/dist/core/util/bounded-value.js.map +1 -0
- package/dist/core/util/value-guards.d.ts +2 -0
- package/dist/core/util/value-guards.d.ts.map +1 -1
- package/dist/core/util/value-guards.js +5 -0
- package/dist/core/util/value-guards.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +27 -44
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/docs/tool-repair.md +3 -0
- package/docs/windows.md +4 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1,43 +1,67 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { getPrivateLaneDeniedPaths } from "../autonomy/lane-private-paths.js";
|
|
3
3
|
import { createLaneToolSurface } from "../autonomy/lane-tool-surface.js";
|
|
4
|
-
import { safeRealpathSync } from "../autonomy/path-scope.js";
|
|
5
4
|
import { appendLaneRecordSnapshot } from "../autonomy/session-lane-record.js";
|
|
6
|
-
import { composeSubagentSystemPrompt } from "../autonomy/subagent-prompt.js";
|
|
7
5
|
import { AUTONOMY_TELEMETRY_EVENT_TYPES } from "../autonomy/telemetry-events.js";
|
|
8
6
|
import { deriveModelCapabilityProfile } from "../model-capability.js";
|
|
7
|
+
import { isLoopbackModelEndpoint } from "../models/model-endpoint.js";
|
|
8
|
+
import { providerUsageFromAttemptUsage } from "../orchestration/attempt-usage.js";
|
|
9
9
|
import { createWorkerExecutionContract, verifierWorkerExecutionContract, } from "../orchestration/worker-execution-contract.js";
|
|
10
|
-
import { createWorkerResultContract } from "../orchestration/worker-result-adapter.js";
|
|
11
10
|
import { registerInFlightWork } from "../reload-blockers.js";
|
|
12
11
|
import { applyWorkerActions } from "./worker-actions.js";
|
|
12
|
+
import { WorkerAgentControlCoordinator } from "./worker-agent-control-coordinator.js";
|
|
13
|
+
import { createWorkerAttemptExecutor } from "./worker-attempt-executor.js";
|
|
14
|
+
import { WorkerConversationStore, } from "./worker-conversation-store.js";
|
|
13
15
|
import { WorkerDispatchScheduler } from "./worker-dispatch-scheduler.js";
|
|
14
16
|
import { buildWorkerExecutionPlan, compileWorkerExecutionGrant, narrowWorkerExecutionPlan, workerExecutionAuthorityFromPlan, } from "./worker-execution-policy.js";
|
|
17
|
+
import { createLocalWorkerProcessOwnerId } from "./worker-process-owner.js";
|
|
15
18
|
import { WorkerProfileResolver } from "./worker-profile-resolver.js";
|
|
16
|
-
import {
|
|
19
|
+
import { WorkerRecoveryCoordinator } from "./worker-recovery-coordinator.js";
|
|
20
|
+
import { selectWorkerResourcePointers } from "./worker-resource-catalog.js";
|
|
21
|
+
import { materializeWorkerResourceBundle } from "./worker-resource-materializer.js";
|
|
22
|
+
import { finalizeWorkerClaim } from "./worker-terminal-finalizer.js";
|
|
23
|
+
import { WorkerWriteReservationCoordinator } from "./worker-write-reservation-coordinator.js";
|
|
17
24
|
export function isLocalExecutionModel(model) {
|
|
18
25
|
if (model.provider === "ollama" || model.provider === "transformers" || model.provider === "llama-cpp") {
|
|
19
26
|
return true;
|
|
20
27
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
return isLoopbackModelEndpoint(model.baseUrl);
|
|
29
|
+
}
|
|
30
|
+
function workerConversationRetentionPolicy(model, settingsManager) {
|
|
31
|
+
const settings = settingsManager.getCompactionSettings();
|
|
32
|
+
const contextWindow = Math.floor(model.contextWindow ?? 0);
|
|
33
|
+
if (!settings.enabled || contextWindow < 4)
|
|
34
|
+
return undefined;
|
|
35
|
+
const reserveTokens = Math.min(settings.reserveTokens, Math.floor(contextWindow * 0.25));
|
|
36
|
+
const reserveTrigger = contextWindow - reserveTokens;
|
|
37
|
+
const fractionalTrigger = settings.triggerPercent > 0 && settings.triggerPercent < 1
|
|
38
|
+
? Math.floor(contextWindow * settings.triggerPercent)
|
|
39
|
+
: reserveTrigger;
|
|
40
|
+
const modelTrigger = model.autoCompactionTriggerTokens && model.autoCompactionTriggerTokens > 0
|
|
41
|
+
? Math.floor(model.autoCompactionTriggerTokens)
|
|
42
|
+
: reserveTrigger;
|
|
43
|
+
const maxContextTokens = Math.min(reserveTrigger, fractionalTrigger, modelTrigger);
|
|
44
|
+
if (maxContextTokens < 2)
|
|
45
|
+
return undefined;
|
|
46
|
+
return {
|
|
47
|
+
maxContextTokens,
|
|
48
|
+
keepRecentTokens: Math.max(1, Math.min(settings.keepRecentTokens, Math.floor(contextWindow * 0.5), maxContextTokens - 1)),
|
|
49
|
+
};
|
|
28
50
|
}
|
|
29
51
|
export class WorkerDelegationController {
|
|
30
52
|
deps;
|
|
31
53
|
workerAbort = new AbortController();
|
|
32
54
|
lifecycle;
|
|
33
55
|
profileResolver;
|
|
34
|
-
|
|
35
|
-
recoveringDurableState = false;
|
|
36
|
-
verificationRecoveryFailures = new Map();
|
|
56
|
+
recovery;
|
|
37
57
|
notifications;
|
|
38
58
|
scheduler;
|
|
39
59
|
laneAbortControllers = new Map();
|
|
60
|
+
/** Sole logical-agent control/mailbox owner; execution only calls its narrow delivery hooks. */
|
|
61
|
+
agentControl;
|
|
40
62
|
publishedTerminalAttemptIds = new Set();
|
|
63
|
+
conversations = new WorkerConversationStore();
|
|
64
|
+
writeReservations;
|
|
41
65
|
inFlightLedgers = new Map();
|
|
42
66
|
constructor(deps, notifications, lifecycle) {
|
|
43
67
|
this.deps = deps;
|
|
@@ -51,6 +75,7 @@ export class WorkerDelegationController {
|
|
|
51
75
|
run: (request, record) => this.runOnce(request, undefined, record),
|
|
52
76
|
cancel: (laneId, reasonCode) => {
|
|
53
77
|
try {
|
|
78
|
+
this.writeReservations.release(laneId);
|
|
54
79
|
const terminal = this.getWorkerLifecycle().cancel(laneId, reasonCode);
|
|
55
80
|
if (terminal)
|
|
56
81
|
this.publishTerminalRecord(terminal);
|
|
@@ -61,6 +86,45 @@ export class WorkerDelegationController {
|
|
|
61
86
|
},
|
|
62
87
|
warn: (message) => this.safeWarn(message),
|
|
63
88
|
});
|
|
89
|
+
this.recovery = new WorkerRecoveryCoordinator({
|
|
90
|
+
lifecycle: this.lifecycle,
|
|
91
|
+
scheduler: this.scheduler,
|
|
92
|
+
recoverWriteReservations: () => this.writeReservations.recoverProvenStale(),
|
|
93
|
+
publishTerminalRecord: (record) => this.publishTerminalRecord(record),
|
|
94
|
+
dispatchVerification: (recovery) => this.dispatchRecoveredVerification(recovery),
|
|
95
|
+
warn: (message) => this.safeWarn(message),
|
|
96
|
+
});
|
|
97
|
+
this.agentControl = new WorkerAgentControlCoordinator({
|
|
98
|
+
agentDir: this.deps.getAgentDir(),
|
|
99
|
+
parentSessionId: this.deps.getSessionId(),
|
|
100
|
+
processOwnerId: createLocalWorkerProcessOwnerId(process.pid, randomUUID()),
|
|
101
|
+
isControlAvailable: () => this.deps.isDelegateToolActive(),
|
|
102
|
+
getLifecycle: () => this.getWorkerLifecycle(),
|
|
103
|
+
recoveredRequest: (attempt) => this.recovery.recoveredRequest(attempt),
|
|
104
|
+
run: (request, record) => this.runOnce(request, undefined, record),
|
|
105
|
+
scheduler: this.scheduler,
|
|
106
|
+
statusChanged: () => this.notifications.statusChanged(),
|
|
107
|
+
abortLane: (laneId, reasonCode) => this.laneAbortControllers.get(laneId)?.abort(reasonCode),
|
|
108
|
+
cancelLane: (laneId, reasonCode) => {
|
|
109
|
+
this.scheduler.dropQueued(laneId);
|
|
110
|
+
this.writeReservations.release(laneId);
|
|
111
|
+
const terminal = this.getWorkerLifecycle().cancel(laneId, reasonCode);
|
|
112
|
+
if (terminal)
|
|
113
|
+
this.publishTerminalRecord(terminal);
|
|
114
|
+
return terminal;
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
this.writeReservations = new WorkerWriteReservationCoordinator({
|
|
118
|
+
agentDir: this.deps.getAgentDir(),
|
|
119
|
+
getCwd: () => this.deps.getCwd(),
|
|
120
|
+
getParentSessionId: () => this.deps.getSessionId(),
|
|
121
|
+
ownerId: this.agentControl.getProcessOwnerId(),
|
|
122
|
+
drainQueuedWorkers: () => {
|
|
123
|
+
if (!this.deps.isDisposed())
|
|
124
|
+
this.scheduler.drain(true);
|
|
125
|
+
},
|
|
126
|
+
warn: (message) => this.safeWarn(message),
|
|
127
|
+
});
|
|
64
128
|
}
|
|
65
129
|
safeWarn(message) {
|
|
66
130
|
try {
|
|
@@ -70,6 +134,14 @@ export class WorkerDelegationController {
|
|
|
70
134
|
// Disposal and recovery diagnostics must never throw.
|
|
71
135
|
}
|
|
72
136
|
}
|
|
137
|
+
/** Narrow model-facing control port. It owns control state; this controller only composes it. */
|
|
138
|
+
getAgentControl() {
|
|
139
|
+
return this.agentControl;
|
|
140
|
+
}
|
|
141
|
+
/** The coordinator's process identity is also the durable execution lease owner. */
|
|
142
|
+
getAgentControlProcessOwnerId() {
|
|
143
|
+
return this.agentControl.getProcessOwnerId();
|
|
144
|
+
}
|
|
73
145
|
recordTerminal(record) {
|
|
74
146
|
if (record.status === "queued" || record.status === "running")
|
|
75
147
|
return;
|
|
@@ -86,7 +158,7 @@ export class WorkerDelegationController {
|
|
|
86
158
|
}
|
|
87
159
|
getRecords() {
|
|
88
160
|
if (this.deps.isDelegateToolActive?.())
|
|
89
|
-
this.
|
|
161
|
+
this.recovery.recover();
|
|
90
162
|
return this.lifecycle.getRecords();
|
|
91
163
|
}
|
|
92
164
|
/** Process-local worker records for the shared terminal notifier; never triggers durable recovery. */
|
|
@@ -98,10 +170,18 @@ export class WorkerDelegationController {
|
|
|
98
170
|
}
|
|
99
171
|
abort() {
|
|
100
172
|
this.workerAbort.abort();
|
|
173
|
+
// Bound attempts have an authoritative transcript and agent identity. A normal owner-session
|
|
174
|
+
// shutdown is an execution interruption, not an explicit worker cancellation: fence it into
|
|
175
|
+
// suspended state before the abort continuation can observe the signal.
|
|
176
|
+
const suspendedAttemptIds = new Set(this.lifecycle.suspendBoundInProcessAttemptsForRestart(this.agentControl.getProcessOwnerId()));
|
|
101
177
|
for (const record of this.lifecycle.getRecords()) {
|
|
102
178
|
if (record.status !== "queued" && record.status !== "running")
|
|
103
179
|
continue;
|
|
104
180
|
const ledger = this.inFlightLedgers.get(record.laneId);
|
|
181
|
+
if (ledger && suspendedAttemptIds.has(ledger.handle.attemptId)) {
|
|
182
|
+
this.inFlightLedgers.delete(record.laneId);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
105
185
|
if (!ledger) {
|
|
106
186
|
try {
|
|
107
187
|
const canceled = this.lifecycle.cancel(record.laneId, "session_disposed");
|
|
@@ -116,7 +196,8 @@ export class WorkerDelegationController {
|
|
|
116
196
|
}
|
|
117
197
|
this.inFlightLedgers.delete(record.laneId);
|
|
118
198
|
try {
|
|
119
|
-
const
|
|
199
|
+
const usage = ledger.getUsage();
|
|
200
|
+
const reportedUsage = providerUsageFromAttemptUsage(usage);
|
|
120
201
|
const reportId = `worker:${this.deps.getSessionId()}:${record.laneId}`;
|
|
121
202
|
const claim = {
|
|
122
203
|
requestId: ledger.request.id,
|
|
@@ -126,31 +207,24 @@ export class WorkerDelegationController {
|
|
|
126
207
|
usageReportId: reportId,
|
|
127
208
|
createdAt: new Date().toISOString(),
|
|
128
209
|
};
|
|
129
|
-
const canceled = this.getWorkerLifecycle()
|
|
210
|
+
const canceled = finalizeWorkerClaim(this.getWorkerLifecycle(), {
|
|
130
211
|
handle: ledger.handle,
|
|
131
212
|
claim,
|
|
132
213
|
accepted: false,
|
|
133
|
-
costUsd:
|
|
214
|
+
costUsd: usage.costUsd,
|
|
134
215
|
cwd: this.deps.getCwd(),
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
: {}),
|
|
142
|
-
wallClockMs: Date.now() - ledger.startedAt,
|
|
143
|
-
toolCalls: ledger.getToolCalls(),
|
|
216
|
+
inputTokens: usage.inputTokens,
|
|
217
|
+
outputTokens: usage.outputTokens,
|
|
218
|
+
totalTokens: reportedUsage.totalTokens,
|
|
219
|
+
wallClockMs: usage.activeWallClockMs,
|
|
220
|
+
toolCalls: usage.toolCalls,
|
|
144
221
|
reasonCode: "session_disposed",
|
|
145
|
-
})
|
|
222
|
+
}).record;
|
|
146
223
|
this.publishTerminalRecord(canceled);
|
|
147
|
-
//
|
|
148
|
-
// returns, which a mid-flight abort preempts) — record what `getSpend()` knows. Same
|
|
149
|
-
// deterministic reportId scheme as the normal path, so a later duplicate report (there
|
|
150
|
-
// is none in practice here, since the lane is now terminal) stays idempotent.
|
|
224
|
+
// The terminal result and owner usage report share the same fenced cumulative snapshot.
|
|
151
225
|
this.deps.saveWorkerClaimSnapshot(claim, ledger.request);
|
|
152
|
-
if (
|
|
153
|
-
this.deps.addSpawnedUsage(
|
|
226
|
+
if (reportedUsage.cost.total > 0 || reportedUsage.totalTokens > 0) {
|
|
227
|
+
this.deps.addSpawnedUsage(reportedUsage, { label: "worker-delegation", reportId });
|
|
154
228
|
}
|
|
155
229
|
}
|
|
156
230
|
catch (error) {
|
|
@@ -158,6 +232,7 @@ export class WorkerDelegationController {
|
|
|
158
232
|
}
|
|
159
233
|
}
|
|
160
234
|
this.scheduler.cancelQueued();
|
|
235
|
+
this.writeReservations.dispose();
|
|
161
236
|
}
|
|
162
237
|
getWorkerLifecycle() {
|
|
163
238
|
return this.lifecycle;
|
|
@@ -169,6 +244,7 @@ export class WorkerDelegationController {
|
|
|
169
244
|
agentDir: this.deps.getAgentDir(),
|
|
170
245
|
cwd: this.deps.getCwd(),
|
|
171
246
|
getSettingsManager: () => this.deps.getSettingsManager(),
|
|
247
|
+
getResourceLoader: () => this.deps.getResourceLoader(),
|
|
172
248
|
getModelRegistry: () => this.deps.getModelRegistry(),
|
|
173
249
|
isModelExhausted: (model) => this.deps.isModelExhausted(model),
|
|
174
250
|
getActiveOrchestrationProfile: () => this.deps.getActiveOrchestrationProfile?.(),
|
|
@@ -191,66 +267,23 @@ export class WorkerDelegationController {
|
|
|
191
267
|
this.publishTerminalRecord(record);
|
|
192
268
|
}
|
|
193
269
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
for (const recovery of lifecycle.getPendingVerificationRecoveries()) {
|
|
212
|
-
if (recovery.action === "reconcile") {
|
|
213
|
-
try {
|
|
214
|
-
this.publishTerminalRecord(lifecycle.reconcileVerification(recovery));
|
|
215
|
-
this.verificationRecoveryFailures.delete(recovery.subjectTaskId);
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
this.safeWarn(`Failed to reconcile recovered verification for ${recovery.subjectTaskId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
219
|
-
}
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
const legacyImplementation = recovery.verifierExecutionContract
|
|
223
|
-
? undefined
|
|
224
|
-
: this.resolveWorkerAdmission({
|
|
225
|
-
instructions: recovery.summary,
|
|
226
|
-
profileId: recovery.implementationProfileId,
|
|
227
|
-
});
|
|
228
|
-
const verifierProfileId = recovery.verifierExecutionContract?.worker.profile.profileId ??
|
|
229
|
-
(legacyImplementation?.ok ? legacyImplementation.verifierShipment?.profile.profileId : undefined);
|
|
230
|
-
const started = verifierProfileId
|
|
231
|
-
? this.startInternal(this.buildVerifierRequest({
|
|
232
|
-
subjectTaskId: recovery.subjectTaskId,
|
|
233
|
-
verifierProfileId,
|
|
234
|
-
summary: recovery.summary,
|
|
235
|
-
artifactUris: recovery.artifactUris,
|
|
236
|
-
}), recovery.verifierExecutionContract)
|
|
237
|
-
: { started: false, skipReason: "independent_verifier_unavailable" };
|
|
238
|
-
if (started.started) {
|
|
239
|
-
this.verificationRecoveryFailures.delete(recovery.subjectTaskId);
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (this.verificationRecoveryFailures.get(recovery.subjectTaskId) !== started.skipReason) {
|
|
243
|
-
this.verificationRecoveryFailures.set(recovery.subjectTaskId, started.skipReason);
|
|
244
|
-
this.safeWarn(`Recovered verification for ${recovery.subjectTaskId} did not start: ${started.skipReason}`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
for (const notification of lifecycle.getPendingTerminalNotifications()) {
|
|
248
|
-
this.notifications.recordTerminal(notification.record, notification.notificationId);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
finally {
|
|
252
|
-
this.recoveringDurableState = false;
|
|
253
|
-
}
|
|
270
|
+
dispatchRecoveredVerification(recovery) {
|
|
271
|
+
const legacyImplementation = recovery.verifierExecutionContract
|
|
272
|
+
? undefined
|
|
273
|
+
: this.resolveWorkerAdmission({
|
|
274
|
+
instructions: recovery.summary,
|
|
275
|
+
profileId: recovery.implementationProfileId,
|
|
276
|
+
});
|
|
277
|
+
const verifierProfileId = recovery.verifierExecutionContract?.worker.profile.profileId ??
|
|
278
|
+
(legacyImplementation?.ok ? legacyImplementation.verifierShipment?.profile.profileId : undefined);
|
|
279
|
+
return verifierProfileId
|
|
280
|
+
? this.startInternal(this.buildVerifierRequest({
|
|
281
|
+
subjectTaskId: recovery.subjectTaskId,
|
|
282
|
+
verifierProfileId,
|
|
283
|
+
summary: recovery.summary,
|
|
284
|
+
artifactUris: recovery.artifactUris,
|
|
285
|
+
}), recovery.verifierExecutionContract)
|
|
286
|
+
: { started: false, skipReason: "independent_verifier_unavailable" };
|
|
254
287
|
}
|
|
255
288
|
laneCapabilityProfile(model) {
|
|
256
289
|
return deriveModelCapabilityProfile({
|
|
@@ -318,6 +351,16 @@ export class WorkerDelegationController {
|
|
|
318
351
|
if (!this.laneCapabilityProfile(shipment.model).backgroundLanesEnabled) {
|
|
319
352
|
return { ok: false, skipReason: "model_delegation_unsupported" };
|
|
320
353
|
}
|
|
354
|
+
// The model-facing delegate surface cannot name profile resources. An absent or empty
|
|
355
|
+
// runtime selection therefore means "use the owner-admitted profile set", not "drop it".
|
|
356
|
+
// Non-empty task metadata is an exact narrowing and remains fail-closed for unknown or
|
|
357
|
+
// duplicate ids before any durable task or provider request is created.
|
|
358
|
+
const requestedResourcePointerIds = request.taskContext?.resourcePointerIds ?? [];
|
|
359
|
+
const selectedResources = selectWorkerResourcePointers(shipment.resourcePointers, requestedResourcePointerIds.length > 0
|
|
360
|
+
? requestedResourcePointerIds
|
|
361
|
+
: shipment.resourcePointers.map((pointer) => pointer.id));
|
|
362
|
+
if (!selectedResources.ok)
|
|
363
|
+
return { ok: false, skipReason: selectedResources.reason };
|
|
321
364
|
const currentExecutionPlan = this.buildWorkerExecutionPlan(shipment.profile, settings);
|
|
322
365
|
const executionPlan = pinnedContract
|
|
323
366
|
? narrowWorkerExecutionPlan(pinnedContract.worker.authority, currentExecutionPlan)
|
|
@@ -343,6 +386,7 @@ export class WorkerDelegationController {
|
|
|
343
386
|
settings,
|
|
344
387
|
shipment,
|
|
345
388
|
...(verifierShipment ? { verifierShipment } : {}),
|
|
389
|
+
resourcePointerIds: selectedResources.pointers.map((pointer) => pointer.id),
|
|
346
390
|
executionContract,
|
|
347
391
|
executionPlan,
|
|
348
392
|
};
|
|
@@ -384,6 +428,18 @@ export class WorkerDelegationController {
|
|
|
384
428
|
if (attemptId)
|
|
385
429
|
this.publishedTerminalAttemptIds.add(attemptId);
|
|
386
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* A pre-execution denial still owns a prepared durable lane. Publish its terminal projection at
|
|
433
|
+
* this boundary: immediate callers have no scheduler promise to observe and must not wait for a
|
|
434
|
+
* future recovery read to receive the handoff. Repeated late cancellation paths are inert via
|
|
435
|
+
* the attempt-id publication fence above.
|
|
436
|
+
*/
|
|
437
|
+
cancelAndPublish(lifecycle, laneId, reasonCode) {
|
|
438
|
+
const record = lifecycle.cancel(laneId, reasonCode);
|
|
439
|
+
if (record)
|
|
440
|
+
this.publishTerminalRecord(record);
|
|
441
|
+
return record;
|
|
442
|
+
}
|
|
387
443
|
hasWorkerCapacity(settings, profile) {
|
|
388
444
|
const lifecycle = this.getWorkerLifecycle();
|
|
389
445
|
if (lifecycle.getRunningCount() >= settings.maxConcurrent)
|
|
@@ -395,9 +451,15 @@ export class WorkerDelegationController {
|
|
|
395
451
|
const admission = this.resolveWorkerAdmission(request, contract);
|
|
396
452
|
if (!admission.ok)
|
|
397
453
|
return { action: "cancel", reasonCode: admission.skipReason };
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
454
|
+
if (!this.hasWorkerCapacity(admission.settings, admission.shipment.profile))
|
|
455
|
+
return { action: "wait", reason: "capacity" };
|
|
456
|
+
const attempt = this.getWorkerLifecycle().getActiveAttempt(record.laneId);
|
|
457
|
+
if (!attempt)
|
|
458
|
+
return { action: "cancel", reasonCode: "orchestration_attempt_missing" };
|
|
459
|
+
const reservation = this.writeReservations.acquire(record.laneId, attempt, admission.executionPlan);
|
|
460
|
+
if (reservation.kind === "denied")
|
|
461
|
+
return { action: "cancel", reasonCode: reservation.reasonCode };
|
|
462
|
+
return reservation.kind === "granted" ? { action: "start" } : { action: "wait", reason: "write_reservation" };
|
|
401
463
|
}
|
|
402
464
|
buildWorkerExecutionPlan(profile, settings) {
|
|
403
465
|
return buildWorkerExecutionPlan({
|
|
@@ -426,6 +488,12 @@ export class WorkerDelegationController {
|
|
|
426
488
|
executionContract: admission.executionContract,
|
|
427
489
|
requiredCapabilities: admission.executionPlan.requiredCapabilities,
|
|
428
490
|
...(request.verificationOfTaskId ? { verificationOfTaskId: request.verificationOfTaskId } : {}),
|
|
491
|
+
taskContext: {
|
|
492
|
+
requirementIds: request.taskContext?.requirementIds ?? [],
|
|
493
|
+
dependsOnTaskIds: request.taskContext?.dependsOnTaskIds ?? [],
|
|
494
|
+
acceptanceCriterionIds: request.taskContext?.acceptanceCriterionIds ?? [],
|
|
495
|
+
resourcePointerIds: admission.resourcePointerIds,
|
|
496
|
+
},
|
|
429
497
|
...(goal ? { goal } : {}),
|
|
430
498
|
});
|
|
431
499
|
return { executionPlan: admission.executionPlan, lifecycle, ...prepared };
|
|
@@ -438,7 +506,7 @@ export class WorkerDelegationController {
|
|
|
438
506
|
if (!admission.ok)
|
|
439
507
|
return { started: false, skipReason: admission.skipReason };
|
|
440
508
|
if (!request.verificationOfTaskId)
|
|
441
|
-
this.
|
|
509
|
+
this.recovery.recover();
|
|
442
510
|
const { settings, shipment } = admission;
|
|
443
511
|
const foreground = this.deps.getModel();
|
|
444
512
|
const contendsWithLocalForeground = foreground !== undefined && isLocalExecutionModel(foreground) && isLocalExecutionModel(shipment.model);
|
|
@@ -485,13 +553,14 @@ export class WorkerDelegationController {
|
|
|
485
553
|
if (!admission.ok)
|
|
486
554
|
return { started: false, skipReason: admission.skipReason };
|
|
487
555
|
if (!request.verificationOfTaskId)
|
|
488
|
-
this.
|
|
556
|
+
this.recovery.recover();
|
|
489
557
|
const { instructions, settings, verifierShipment } = admission;
|
|
490
558
|
const { model, modelBinding, profile: orchestrationProfile, soul } = admission.shipment;
|
|
491
559
|
if (!this.hasWorkerCapacity(settings, orchestrationProfile)) {
|
|
492
560
|
return { started: false, skipReason: "worker_delegation_already_running" };
|
|
493
561
|
}
|
|
494
562
|
const laneCapability = this.laneCapabilityProfile(model);
|
|
563
|
+
const retentionPolicy = workerConversationRetentionPolicy(model, this.deps.getSettingsManager());
|
|
495
564
|
const prepared = this.prepareWorkerAttempt(request, admission, existingRecord);
|
|
496
565
|
const { executionPlan, lifecycle } = prepared;
|
|
497
566
|
if (!prepared.attempt)
|
|
@@ -499,26 +568,145 @@ export class WorkerDelegationController {
|
|
|
499
568
|
const durableTask = lifecycle.getTask(prepared.record.laneId);
|
|
500
569
|
if (!durableTask)
|
|
501
570
|
return { started: false, skipReason: "orchestration_task_missing" };
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
571
|
+
const immutableWorker = prepared.attempt.dispatch.executionContract?.worker;
|
|
572
|
+
if (!immutableWorker)
|
|
573
|
+
return { started: false, skipReason: "orchestration_execution_contract_missing" };
|
|
574
|
+
const selectedResources = selectWorkerResourcePointers(immutableWorker.resourcePointers, prepared.attempt.dispatch.resourcePointerIds.length > 0
|
|
575
|
+
? prepared.attempt.dispatch.resourcePointerIds
|
|
576
|
+
: admission.resourcePointerIds);
|
|
577
|
+
if (!selectedResources.ok) {
|
|
578
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, selectedResources.reason);
|
|
579
|
+
return { started: false, skipReason: selectedResources.reason };
|
|
580
|
+
}
|
|
581
|
+
let grant;
|
|
582
|
+
let workerResourceSystemPrompt;
|
|
583
|
+
if (prepared.attempt.grant) {
|
|
584
|
+
if (!this.recovery.durableGrantIsStillPermitted(prepared.attempt.grant, executionPlan, selectedResources.pointers)) {
|
|
585
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, "recovered_grant_revoked");
|
|
586
|
+
return { started: false, skipReason: "recovered_grant_revoked" };
|
|
587
|
+
}
|
|
588
|
+
grant = prepared.attempt.grant;
|
|
589
|
+
const materialized = materializeWorkerResourceBundle(grant.resources);
|
|
590
|
+
if (!materialized.ok) {
|
|
591
|
+
const reason = `worker_resource_materialization_${materialized.code}`;
|
|
592
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, reason);
|
|
593
|
+
return { started: false, skipReason: reason };
|
|
594
|
+
}
|
|
595
|
+
workerResourceSystemPrompt = materialized.systemPrompt;
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
const materialized = materializeWorkerResourceBundle(selectedResources.pointers);
|
|
599
|
+
if (!materialized.ok) {
|
|
600
|
+
const reason = `worker_resource_materialization_${materialized.code}`;
|
|
601
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, reason);
|
|
602
|
+
return { started: false, skipReason: reason };
|
|
603
|
+
}
|
|
604
|
+
const compiled = compileWorkerExecutionGrant({
|
|
605
|
+
target: {
|
|
606
|
+
objectiveId: durableTask.task.objectiveId,
|
|
607
|
+
taskId: prepared.attempt.taskId,
|
|
608
|
+
attemptId: prepared.attempt.attemptId,
|
|
609
|
+
},
|
|
610
|
+
profile: orchestrationProfile,
|
|
611
|
+
plan: executionPlan,
|
|
612
|
+
resources: materialized.pointers,
|
|
613
|
+
});
|
|
614
|
+
if (!compiled.ok) {
|
|
615
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, compiled.reasonCodes.join(","));
|
|
616
|
+
return { started: false, skipReason: `execution_policy_denied:${compiled.reasonCodes.join(",")}` };
|
|
617
|
+
}
|
|
618
|
+
lifecycle.bindGrant(prepared.attempt.attemptId, compiled.grant);
|
|
619
|
+
grant = compiled.grant;
|
|
620
|
+
workerResourceSystemPrompt = materialized.systemPrompt;
|
|
621
|
+
}
|
|
622
|
+
const immutableProfile = immutableWorker.profile;
|
|
623
|
+
// Follow-up tasks deliberately receive unique task ids while retaining the original logical
|
|
624
|
+
// agent id in dispatch metadata. Never derive a new agent identity from a follow-up task id.
|
|
625
|
+
const agentId = prepared.attempt.agentId ?? prepared.attempt.dispatch.logicalLaneId ?? prepared.record.laneId;
|
|
626
|
+
const registeredAgent = lifecycle.getAgent(agentId);
|
|
627
|
+
if (prepared.attempt.agentId && !registeredAgent) {
|
|
628
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, "orchestration_agent_missing");
|
|
629
|
+
return { started: false, skipReason: "orchestration_agent_missing" };
|
|
630
|
+
}
|
|
631
|
+
let conversation;
|
|
632
|
+
try {
|
|
633
|
+
if (registeredAgent) {
|
|
634
|
+
// Recovery must trust only the registered resume context; reconstructing one here could
|
|
635
|
+
// silently redirect a logical worker to a different transcript or resource scope.
|
|
636
|
+
conversation = this.conversations.open({
|
|
637
|
+
agentDir: this.deps.getAgentDir(),
|
|
638
|
+
resumeContext: registeredAgent.resumeContext,
|
|
639
|
+
expectedLogicalAgentId: registeredAgent.agentId,
|
|
640
|
+
});
|
|
641
|
+
if (prepared.attempt.status === "suspended")
|
|
642
|
+
this.recovery.repairInterruptedToolResults(conversation);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
conversation = this.conversations.ensure({
|
|
646
|
+
agentDir: this.deps.getAgentDir(),
|
|
647
|
+
parentSessionId: this.deps.getSessionId(),
|
|
648
|
+
logicalAgentId: agentId,
|
|
649
|
+
cwd: this.deps.getCwd(),
|
|
650
|
+
orchestrationProfileId: immutableProfile.profileId,
|
|
651
|
+
modelRef: `${prepared.attempt.dispatch.executionContract.worker.modelBinding.provider}/${prepared.attempt.dispatch.executionContract.worker.modelBinding.modelId}`,
|
|
652
|
+
resourceProfileNames: immutableProfile.resourceProfileNames,
|
|
653
|
+
contextPointers: grant.resources,
|
|
654
|
+
});
|
|
655
|
+
lifecycle.ensureAgent({
|
|
656
|
+
agentId,
|
|
657
|
+
role: immutableProfile.role,
|
|
658
|
+
resumeContext: conversation.getResumeContext(),
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
if (prepared.attempt.status !== "suspended") {
|
|
664
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, "worker_conversation_unavailable");
|
|
665
|
+
}
|
|
666
|
+
this.safeWarn(`Worker conversation setup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
667
|
+
return { started: false, skipReason: "worker_conversation_unavailable" };
|
|
668
|
+
}
|
|
669
|
+
const reservation = this.writeReservations.acquire(prepared.record.laneId, prepared.attempt, executionPlan);
|
|
670
|
+
if (reservation.kind === "denied") {
|
|
671
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, reservation.reasonCode);
|
|
672
|
+
return { started: false, skipReason: reservation.reasonCode };
|
|
673
|
+
}
|
|
674
|
+
if (reservation.kind === "blocked") {
|
|
675
|
+
this.scheduler.enqueue(prepared.record, { ...request, profileId: admission.shipment.profile.profileId });
|
|
676
|
+
this.notifications.statusChanged();
|
|
677
|
+
onStarted?.(prepared.record);
|
|
678
|
+
return { started: true, record: prepared.record };
|
|
679
|
+
}
|
|
680
|
+
let durableHandle;
|
|
681
|
+
try {
|
|
682
|
+
durableHandle =
|
|
683
|
+
registeredAgent && prepared.attempt.status === "suspended"
|
|
684
|
+
? lifecycle.resumeAgent(prepared.record.laneId, registeredAgent.agentId, immutableProfile.leaseTtlMs, this.agentControl.getProcessOwnerId())
|
|
685
|
+
: lifecycle.startAgent(prepared.record.laneId, agentId, immutableProfile.leaseTtlMs, this.agentControl.getProcessOwnerId());
|
|
686
|
+
}
|
|
687
|
+
catch (error) {
|
|
688
|
+
this.writeReservations.release(prepared.record.laneId);
|
|
689
|
+
if (prepared.attempt.status !== "suspended")
|
|
690
|
+
this.cancelAndPublish(lifecycle, prepared.record.laneId, "worker_start_unavailable");
|
|
691
|
+
this.safeWarn(`Worker start failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
692
|
+
return { started: false, skipReason: "worker_start_unavailable" };
|
|
514
693
|
}
|
|
515
|
-
lifecycle.bindGrant(prepared.attempt.attemptId, compiled.grant);
|
|
516
|
-
const durableHandle = lifecycle.start(prepared.record.laneId, orchestrationProfile.leaseTtlMs);
|
|
517
694
|
const startedRecord = lifecycle.getRecord(prepared.record.laneId);
|
|
518
|
-
if (!startedRecord)
|
|
695
|
+
if (!startedRecord) {
|
|
696
|
+
this.writeReservations.release(prepared.record.laneId);
|
|
519
697
|
return { started: false, skipReason: "orchestration_projection_missing" };
|
|
698
|
+
}
|
|
699
|
+
if (this.writeReservations.hasFenceMismatch(startedRecord.laneId, durableHandle.attemptId, durableHandle.fencingToken)) {
|
|
700
|
+
this.writeReservations.release(startedRecord.laneId);
|
|
701
|
+
const record = this.cancelAndPublish(lifecycle, startedRecord.laneId, "write_reservation_fence_mismatch");
|
|
702
|
+
this.safeWarn("Worker write reservation fence did not match the durable attempt lease.");
|
|
703
|
+
return { started: false, skipReason: record?.reasonCode ?? "write_reservation_fence_mismatch" };
|
|
704
|
+
}
|
|
705
|
+
const recoveredTerminal = prepared.attempt.status === "suspended" ? this.recovery.recoveredTerminalCompletion(conversation) : undefined;
|
|
706
|
+
const checkpointUsage = lifecycle.getAttemptUsage(startedRecord.laneId);
|
|
707
|
+
const initialUsage = this.recovery.initialUsage(conversation, checkpointUsage);
|
|
520
708
|
onStarted?.(startedRecord);
|
|
521
|
-
const maxUsd =
|
|
709
|
+
const maxUsd = grant.budget.maxCostUsd;
|
|
522
710
|
const executionPolicy = orchestrationProfile.executionPolicy;
|
|
523
711
|
const toolSurface = createLaneToolSurface({
|
|
524
712
|
cwd: this.deps.getCwd(),
|
|
@@ -526,9 +714,10 @@ export class WorkerDelegationController {
|
|
|
526
714
|
writeEnabled: executionPlan.writeEnabled,
|
|
527
715
|
writePaths: executionPlan.writePaths,
|
|
528
716
|
...(executionPlan.processEnabled && executionPolicy ? { executionPolicy } : {}),
|
|
529
|
-
processMaxWallClockMs:
|
|
530
|
-
grant
|
|
717
|
+
processMaxWallClockMs: grant.budget.maxWallClockMs ?? 0,
|
|
718
|
+
grant,
|
|
531
719
|
toolManifests: executionPlan.toolManifests,
|
|
720
|
+
initialUsage,
|
|
532
721
|
});
|
|
533
722
|
const writeGranted = executionPlan.writeEnabled &&
|
|
534
723
|
toolSurface.gateway !== undefined &&
|
|
@@ -548,9 +737,9 @@ export class WorkerDelegationController {
|
|
|
548
737
|
profileId: orchestrationProfile.profileId,
|
|
549
738
|
// filesystem.write requires BOTH the opt-in AND an explicit non-empty path scope —
|
|
550
739
|
// an unscoped write grant is refused here, not discovered at validation time.
|
|
551
|
-
capabilities: [...
|
|
552
|
-
...(writeGranted ? { allowedPaths: [...
|
|
553
|
-
deniedPaths: [...
|
|
740
|
+
capabilities: [...grant.capabilities],
|
|
741
|
+
...(writeGranted ? { allowedPaths: [...grant.writePaths] } : {}),
|
|
742
|
+
deniedPaths: [...grant.deniedPaths],
|
|
554
743
|
allowedTools: [...toolSurface.allowedTools],
|
|
555
744
|
deniedTools: [...toolSurface.deniedTools],
|
|
556
745
|
...(maxUsd !== undefined ? { maxEstimatedUsd: maxUsd } : {}),
|
|
@@ -579,120 +768,55 @@ export class WorkerDelegationController {
|
|
|
579
768
|
// registerInFlightWork is a pure sync map op (cannot throw), so placing it as the last
|
|
580
769
|
// statement before `try` still guarantees the matching finally always runs.
|
|
581
770
|
const deregisterInFlight = registerInFlightWork(this.deps.getAgentDir(), "lane", `worker:${startedRecord.laneId}`);
|
|
582
|
-
const
|
|
771
|
+
const executor = createWorkerAttemptExecutor({
|
|
772
|
+
request: workerRequest,
|
|
773
|
+
grant,
|
|
774
|
+
executionPlan,
|
|
775
|
+
toolSurface,
|
|
776
|
+
conversation,
|
|
777
|
+
lifecycle,
|
|
778
|
+
laneId: startedRecord.laneId,
|
|
779
|
+
agentId,
|
|
780
|
+
durableHandle,
|
|
781
|
+
parentSessionId: this.deps.getSessionId(),
|
|
782
|
+
agentDir: this.deps.getAgentDir(),
|
|
783
|
+
cwd: this.deps.getCwd(),
|
|
784
|
+
model,
|
|
785
|
+
thinkingLevel: modelBinding.thinkingLevel,
|
|
786
|
+
laneCapability,
|
|
787
|
+
...(soul ? { soul } : {}),
|
|
788
|
+
workerResourceSystemPrompt,
|
|
789
|
+
initialUsage,
|
|
790
|
+
hasPersistedUsageCheckpoint: checkpointUsage !== undefined,
|
|
791
|
+
usageReportId,
|
|
792
|
+
processCapable: executionPlan.processEnabled,
|
|
793
|
+
...(request.verificationOfTaskId ? { verificationSubjectTaskId: request.verificationOfTaskId } : {}),
|
|
794
|
+
...(recoveredTerminal ? { recoveredTerminal } : {}),
|
|
795
|
+
...(retentionPolicy ? { retentionPolicy } : {}),
|
|
796
|
+
signal: workerSignal,
|
|
797
|
+
runIsolatedCompletion: (options) => this.deps.runIsolatedCompletion(options),
|
|
798
|
+
agentControl: this.agentControl,
|
|
799
|
+
applyActions: workerRequest.envelope.capabilities.includes("filesystem.write")
|
|
800
|
+
? (actions, actionJournal) => applyWorkerActions({
|
|
801
|
+
actions,
|
|
802
|
+
gateway: toolSurface.gateway,
|
|
803
|
+
toolManifests: executionPlan.toolManifests,
|
|
804
|
+
cwd: this.deps.getCwd(),
|
|
805
|
+
...(actionJournal ? { actionJournal } : {}),
|
|
806
|
+
})
|
|
807
|
+
: undefined,
|
|
808
|
+
warn: (message) => this.safeWarn(message),
|
|
809
|
+
});
|
|
583
810
|
try {
|
|
584
|
-
|
|
585
|
-
const toolChangedFiles = new Set();
|
|
586
|
-
const toolIssues = new Set();
|
|
587
|
-
// Register the live mutation ledger BEFORE the suspend point below so a synchronous
|
|
588
|
-
// disposal cutoff (`abortInFlightLanes`) can read a race-free snapshot of whatever this
|
|
589
|
-
// worker has already applied — the worker is suspended at the `await runWorker(...)` below
|
|
590
|
-
// whenever abort runs, and the abort signal stops further tool calls. Deleted in the
|
|
591
|
-
// `finally` on every exit path (normal completion, throw, or already consumed by abort).
|
|
811
|
+
// Register before the first execution await: disposal sees the live mutable ledger.
|
|
592
812
|
this.inFlightLedgers.set(startedRecord.laneId, {
|
|
593
|
-
changedFiles:
|
|
594
|
-
|
|
595
|
-
getToolCalls: () => toolSurface.gateway?.getUsage().toolCalls ?? 0,
|
|
813
|
+
changedFiles: executor.ledger.changedFiles,
|
|
814
|
+
getUsage: executor.ledger.getUsage,
|
|
596
815
|
request: workerRequest,
|
|
597
816
|
handle: durableHandle,
|
|
598
|
-
startedAt: durableStartedAt,
|
|
599
|
-
});
|
|
600
|
-
const maxToolCalls = compiled.grant.budget.maxToolCalls ?? 0;
|
|
601
|
-
const rawOutcome = await runWorker({
|
|
602
|
-
request: workerRequest,
|
|
603
|
-
maxUsd,
|
|
604
|
-
maxWallClockMs: compiled.grant.budget.maxWallClockMs ?? 0,
|
|
605
|
-
usageReportId,
|
|
606
|
-
getChangedFiles: () => [...toolChangedFiles],
|
|
607
|
-
signal: workerSignal,
|
|
608
|
-
// Parent validation must use the same relative-path baseline the runner reports in.
|
|
609
|
-
cwd: this.deps.getCwd(),
|
|
610
|
-
processCapable: executionPlan.processEnabled,
|
|
611
|
-
...(request.verificationOfTaskId ? { verificationSubjectTaskId: request.verificationOfTaskId } : {}),
|
|
612
|
-
// Write lane: runner-side action application through the envelope path scope.
|
|
613
|
-
applyActions: workerRequest.envelope.capabilities.includes("filesystem.write")
|
|
614
|
-
? (actions) => {
|
|
615
|
-
return applyWorkerActions({
|
|
616
|
-
actions,
|
|
617
|
-
gateway: toolSurface.gateway,
|
|
618
|
-
toolManifests: executionPlan.toolManifests,
|
|
619
|
-
cwd: this.deps.getCwd(),
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
: undefined,
|
|
623
|
-
complete: async ({ systemPrompt, userPrompt, signal }) => {
|
|
624
|
-
const completion = await this.deps.runIsolatedCompletion({
|
|
625
|
-
// Level-0 core and owner-authored profile remain authoritative. The delegating
|
|
626
|
-
// model supplies task instructions only; it cannot replace the worker role prompt.
|
|
627
|
-
systemPrompt: composeSubagentSystemPrompt({
|
|
628
|
-
soul,
|
|
629
|
-
rolePrompt: systemPrompt,
|
|
630
|
-
}),
|
|
631
|
-
messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
|
|
632
|
-
model,
|
|
633
|
-
thinkingLevel: modelBinding.thinkingLevel,
|
|
634
|
-
maxTokens: Math.min(laneCapability.laneMaxOutputTokens, compiled.grant.budget.maxTokens ?? Number.POSITIVE_INFINITY),
|
|
635
|
-
tools: toolSurface.tools,
|
|
636
|
-
maxTurns: Math.max(1, Math.min(6, maxToolCalls + 1)),
|
|
637
|
-
finalTextPrompt: "The tool-turn budget is exhausted. Do not call more tools. Return the required worker-claim JSON envelope now using only evidence already gathered. If the investigation is incomplete, say so in the summary or blockers instead of omitting the envelope.",
|
|
638
|
-
beforeToolCall: async (context, toolSignal) => {
|
|
639
|
-
const decision = await toolSurface.beforeToolCall(context, toolSignal);
|
|
640
|
-
if (decision?.block) {
|
|
641
|
-
toolIssues.add(`${context.toolCall.name} blocked: ${decision.reason ?? "capability denied"}`);
|
|
642
|
-
}
|
|
643
|
-
return decision;
|
|
644
|
-
},
|
|
645
|
-
afterToolCall: async ({ toolCall, args, isError }) => {
|
|
646
|
-
// This hook runs only for a validated, gate-approved tool that actually entered
|
|
647
|
-
// execution. Record a direct mutation target before inspecting `isError`: write/edit
|
|
648
|
-
// may have changed disk and then observed cancellation, timeout, or a late I/O error.
|
|
649
|
-
// Pre-gate/profile/path refusals never reach afterToolCall, so they remain unreported.
|
|
650
|
-
if (toolCall.name === "write" || toolCall.name === "edit") {
|
|
651
|
-
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
652
|
-
const rawPath = args.path;
|
|
653
|
-
if (typeof rawPath === "string" && rawPath.length > 0) {
|
|
654
|
-
const absolutePath = path.isAbsolute(rawPath)
|
|
655
|
-
? path.resolve(rawPath)
|
|
656
|
-
: path.resolve(this.deps.getCwd(), rawPath);
|
|
657
|
-
let canonicalPath = absolutePath;
|
|
658
|
-
try {
|
|
659
|
-
canonicalPath = safeRealpathSync(absolutePath);
|
|
660
|
-
}
|
|
661
|
-
catch {
|
|
662
|
-
// Execution was attempted; preserve conservative accounting with the lexical path.
|
|
663
|
-
}
|
|
664
|
-
toolChangedFiles.add(path.relative(this.deps.getCwd(), canonicalPath).split(path.sep).join("/"));
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
if (isError) {
|
|
669
|
-
toolIssues.add(`${toolCall.name} failed during isolated execution`);
|
|
670
|
-
return undefined;
|
|
671
|
-
}
|
|
672
|
-
return undefined;
|
|
673
|
-
},
|
|
674
|
-
signal,
|
|
675
|
-
// Core/soul/role are all static per configuration — the provider can cache the prefix.
|
|
676
|
-
cacheRetention: "short",
|
|
677
|
-
// Stable per-lane synthetic affinity key so repeat worker-delegation calls route to
|
|
678
|
-
// the same cache-warm backend without carrying the real session id.
|
|
679
|
-
laneKind: "worker",
|
|
680
|
-
});
|
|
681
|
-
spentUsage = completion.usage;
|
|
682
|
-
toolSurface.gateway?.recordUsage({
|
|
683
|
-
inputTokens: completion.usage.input,
|
|
684
|
-
outputTokens: completion.usage.output,
|
|
685
|
-
costUsd: completion.usage.cost.total,
|
|
686
|
-
});
|
|
687
|
-
return {
|
|
688
|
-
text: completion.text,
|
|
689
|
-
costUsd: completion.usage.cost.total,
|
|
690
|
-
stopReason: String(completion.stopReason),
|
|
691
|
-
changedFiles: [...toolChangedFiles],
|
|
692
|
-
blockers: [...toolIssues],
|
|
693
|
-
};
|
|
694
|
-
},
|
|
695
817
|
});
|
|
818
|
+
const executionResult = await executor.run();
|
|
819
|
+
const rawOutcome = executionResult.rawOutcome;
|
|
696
820
|
const verificationRequired = orchestrationProfile.requireIndependentVerification &&
|
|
697
821
|
orchestrationProfile.role !== "verifier" &&
|
|
698
822
|
rawOutcome.claim.status === "completed";
|
|
@@ -729,33 +853,32 @@ export class WorkerDelegationController {
|
|
|
729
853
|
const verificationSubject = request.verificationOfTaskId
|
|
730
854
|
? lifecycle.getTask(request.verificationOfTaskId)
|
|
731
855
|
: undefined;
|
|
732
|
-
|
|
856
|
+
const finalUsage = executionResult.usage;
|
|
857
|
+
const reportedUsage = providerUsageFromAttemptUsage(finalUsage);
|
|
858
|
+
let record = finalizeWorkerClaim(lifecycle, {
|
|
733
859
|
handle: durableHandle,
|
|
734
860
|
claim: outcome.claim,
|
|
735
861
|
accepted: outcome.accepted,
|
|
736
|
-
costUsd:
|
|
862
|
+
costUsd: finalUsage.costUsd,
|
|
737
863
|
reasonCode: outcome.reasonCode,
|
|
738
864
|
cwd: this.deps.getCwd(),
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
}
|
|
745
|
-
: {}),
|
|
746
|
-
wallClockMs: Date.now() - durableStartedAt,
|
|
747
|
-
toolCalls: toolSurface.gateway?.getUsage().toolCalls ?? 0,
|
|
865
|
+
inputTokens: finalUsage.inputTokens,
|
|
866
|
+
outputTokens: finalUsage.outputTokens,
|
|
867
|
+
totalTokens: reportedUsage.totalTokens,
|
|
868
|
+
wallClockMs: finalUsage.activeWallClockMs,
|
|
869
|
+
toolCalls: finalUsage.toolCalls,
|
|
748
870
|
verificationRequired,
|
|
749
871
|
verificationCriterionIds: verificationSubject?.task.acceptanceCriterionIds,
|
|
750
|
-
|
|
872
|
+
notify: !verificationRequired,
|
|
873
|
+
}).record;
|
|
751
874
|
try {
|
|
752
875
|
this.deps.saveWorkerClaimSnapshot(outcome.claim, workerRequest);
|
|
753
876
|
}
|
|
754
877
|
catch (error) {
|
|
755
878
|
this.safeWarn(`Failed to persist worker claim ${startedRecord.laneId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
756
879
|
}
|
|
757
|
-
if (
|
|
758
|
-
this.deps.addSpawnedUsage(
|
|
880
|
+
if (reportedUsage.cost.total > 0 || reportedUsage.totalTokens > 0) {
|
|
881
|
+
this.deps.addSpawnedUsage(reportedUsage, { label: "worker-delegation", reportId: usageReportId });
|
|
759
882
|
}
|
|
760
883
|
const terminalRecords = [record];
|
|
761
884
|
if (request.verificationOfTaskId) {
|
|
@@ -809,6 +932,11 @@ export class WorkerDelegationController {
|
|
|
809
932
|
}
|
|
810
933
|
catch (error) {
|
|
811
934
|
const durableState = lifecycle.ledger.runtime.getSnapshot().attempts[durableHandle.attemptId];
|
|
935
|
+
if (durableState?.status === "suspended") {
|
|
936
|
+
// Disposal/reload fences agent-bound work before its aborted completion unwinds. Do not
|
|
937
|
+
// convert that resumable interruption into a terminal claim or cancellation.
|
|
938
|
+
return { started: true, record: lifecycle.getRecord(startedRecord.laneId) };
|
|
939
|
+
}
|
|
812
940
|
if (durableState?.status === "cancelled" && (laneAbortController.signal.aborted || this.deps.isDisposed())) {
|
|
813
941
|
return { started: true, record: lifecycle.getRecord(startedRecord.laneId) };
|
|
814
942
|
}
|
|
@@ -821,16 +949,21 @@ export class WorkerDelegationController {
|
|
|
821
949
|
createdAt: new Date().toISOString(),
|
|
822
950
|
};
|
|
823
951
|
try {
|
|
824
|
-
|
|
952
|
+
const failureUsage = executor.checkpointUsage("Persisted cumulative usage while recording worker failure.");
|
|
953
|
+
const reportedUsage = providerUsageFromAttemptUsage(failureUsage);
|
|
954
|
+
finalizeWorkerClaim(lifecycle, {
|
|
825
955
|
handle: durableHandle,
|
|
826
956
|
claim: failureClaim,
|
|
827
957
|
accepted: false,
|
|
828
|
-
costUsd:
|
|
958
|
+
costUsd: failureUsage.costUsd,
|
|
829
959
|
cwd: this.deps.getCwd(),
|
|
830
|
-
|
|
831
|
-
|
|
960
|
+
inputTokens: failureUsage.inputTokens,
|
|
961
|
+
outputTokens: failureUsage.outputTokens,
|
|
962
|
+
totalTokens: reportedUsage.totalTokens,
|
|
963
|
+
wallClockMs: failureUsage.activeWallClockMs,
|
|
964
|
+
toolCalls: failureUsage.toolCalls,
|
|
832
965
|
reasonCode: "worker_delegation_error",
|
|
833
|
-
})
|
|
966
|
+
});
|
|
834
967
|
}
|
|
835
968
|
catch (persistError) {
|
|
836
969
|
this.safeWarn(`Failed to persist durable worker failure ${startedRecord.laneId}: ${persistError instanceof Error ? persistError.message : String(persistError)}`);
|
|
@@ -838,7 +971,7 @@ export class WorkerDelegationController {
|
|
|
838
971
|
}
|
|
839
972
|
let record = lifecycle.getRecord(startedRecord.laneId);
|
|
840
973
|
if (record?.status === "queued" || record?.status === "running") {
|
|
841
|
-
record =
|
|
974
|
+
record = this.cancelAndPublish(lifecycle, startedRecord.laneId, "worker_delegation_error");
|
|
842
975
|
}
|
|
843
976
|
if (record && !this.deps.isDisposed())
|
|
844
977
|
this.publishTerminalRecord(record);
|
|
@@ -847,14 +980,16 @@ export class WorkerDelegationController {
|
|
|
847
980
|
return { started: true, record };
|
|
848
981
|
}
|
|
849
982
|
finally {
|
|
983
|
+
this.writeReservations.release(startedRecord.laneId, durableHandle.attemptId, durableHandle.fencingToken);
|
|
850
984
|
this.inFlightLedgers.delete(startedRecord.laneId);
|
|
851
985
|
this.laneAbortControllers.delete(startedRecord.laneId);
|
|
986
|
+
this.agentControl.signalStateChanged();
|
|
852
987
|
deregisterInFlight();
|
|
853
988
|
}
|
|
854
989
|
}
|
|
855
990
|
/** Start every capacity-eligible queued worker at the owner session's foreground-idle boundary. */
|
|
856
991
|
drain() {
|
|
857
|
-
this.
|
|
992
|
+
this.recovery.recover();
|
|
858
993
|
this.scheduler.drain();
|
|
859
994
|
}
|
|
860
995
|
}
|