@caupulican/pi-adaptative 0.86.2 → 0.86.5
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 +40 -0
- package/dist/core/agent-paths.d.ts +13 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +23 -0
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +7 -0
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +12 -4
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +6 -3
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/subagent-prompt.js +1 -1
- package/dist/core/autonomy/subagent-prompt.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +25 -2
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +67 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts +4 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts.map +1 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.js +20 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.js.map +1 -0
- package/dist/core/delegation/sanitized-context-fork.d.ts +25 -0
- package/dist/core/delegation/sanitized-context-fork.d.ts.map +1 -0
- package/dist/core/delegation/sanitized-context-fork.js +299 -0
- package/dist/core/delegation/sanitized-context-fork.js.map +1 -0
- package/dist/core/delegation/session-root-mailbox.d.ts +75 -0
- package/dist/core/delegation/session-root-mailbox.d.ts.map +1 -0
- package/dist/core/delegation/session-root-mailbox.js +627 -0
- package/dist/core/delegation/session-root-mailbox.js.map +1 -0
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +102 -9
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +1307 -125
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-agent-control.d.ts +183 -8
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.js +778 -48
- package/dist/core/delegation/worker-agent-control.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts +9 -0
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +479 -214
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.d.ts +9 -0
- package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.js +22 -14
- package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
- package/dist/core/delegation/worker-context-fork-store.d.ts +71 -0
- package/dist/core/delegation/worker-context-fork-store.d.ts.map +1 -0
- package/dist/core/delegation/worker-context-fork-store.js +437 -0
- package/dist/core/delegation/worker-context-fork-store.js.map +1 -0
- package/dist/core/delegation/worker-context-inheritance-policy.d.ts +18 -0
- package/dist/core/delegation/worker-context-inheritance-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-context-inheritance-policy.js +28 -0
- package/dist/core/delegation/worker-context-inheritance-policy.js.map +1 -0
- package/dist/core/delegation/worker-conversation-revision.d.ts +31 -0
- package/dist/core/delegation/worker-conversation-revision.d.ts.map +1 -0
- package/dist/core/delegation/worker-conversation-revision.js +203 -0
- package/dist/core/delegation/worker-conversation-revision.js.map +1 -0
- package/dist/core/delegation/worker-conversation-store.d.ts +130 -18
- package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
- package/dist/core/delegation/worker-conversation-store.js +1263 -79
- package/dist/core/delegation/worker-conversation-store.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts +29 -2
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +625 -111
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-delegation-request.d.ts +2 -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 +20 -1
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.js +210 -37
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.d.ts +38 -0
- package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -0
- package/dist/core/delegation/worker-fleet-limits.js +113 -0
- package/dist/core/delegation/worker-fleet-limits.js.map +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -1
- package/dist/core/delegation/worker-lane-projection.js +6 -2
- package/dist/core/delegation/worker-lane-projection.js.map +1 -1
- package/dist/core/delegation/worker-lifecycle.d.ts +20 -3
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +42 -18
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.d.ts +4 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.js +45 -4
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-provider-turn-protocol.d.ts +50 -0
- package/dist/core/delegation/worker-provider-turn-protocol.d.ts.map +1 -0
- package/dist/core/delegation/worker-provider-turn-protocol.js +185 -0
- package/dist/core/delegation/worker-provider-turn-protocol.js.map +1 -0
- package/dist/core/delegation/worker-recovery-coordinator.d.ts +45 -4
- package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.js +223 -17
- package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-retry-policy.d.ts +36 -0
- package/dist/core/delegation/worker-retry-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-retry-policy.js +41 -0
- package/dist/core/delegation/worker-retry-policy.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 +4 -2
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-task-view.d.ts +43 -0
- package/dist/core/delegation/worker-task-view.d.ts.map +1 -0
- package/dist/core/delegation/worker-task-view.js +194 -0
- package/dist/core/delegation/worker-task-view.js.map +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts +55 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js +179 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +2 -2
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-tree-budget-coordinator.js +12 -46
- package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -1
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/orchestration/attempt-ordering.d.ts +9 -0
- package/dist/core/orchestration/attempt-ordering.d.ts.map +1 -0
- package/dist/core/orchestration/attempt-ordering.js +29 -0
- package/dist/core/orchestration/attempt-ordering.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +19 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
- package/dist/core/orchestration/capability-gateway.js +19 -2
- package/dist/core/orchestration/capability-gateway.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +43 -3
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +24 -0
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/delegation-ledger.d.ts +8 -3
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
- package/dist/core/orchestration/delegation-ledger.js +148 -55
- package/dist/core/orchestration/delegation-ledger.js.map +1 -1
- package/dist/core/orchestration/event-store.d.ts +6 -3
- package/dist/core/orchestration/event-store.d.ts.map +1 -1
- package/dist/core/orchestration/event-store.js +228 -30
- package/dist/core/orchestration/event-store.js.map +1 -1
- package/dist/core/orchestration/task-runtime-codecs.d.ts +31 -0
- package/dist/core/orchestration/task-runtime-codecs.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-codecs.js +1471 -0
- package/dist/core/orchestration/task-runtime-codecs.js.map +1 -0
- package/dist/core/orchestration/task-runtime-projection.d.ts +30 -0
- package/dist/core/orchestration/task-runtime-projection.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-projection.js +340 -0
- package/dist/core/orchestration/task-runtime-projection.js.map +1 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts +38 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-reducer.js +1270 -0
- package/dist/core/orchestration/task-runtime-reducer.js.map +1 -0
- package/dist/core/orchestration/task-runtime-state.d.ts +142 -0
- package/dist/core/orchestration/task-runtime-state.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-state.js +14 -0
- package/dist/core/orchestration/task-runtime-state.js.map +1 -0
- package/dist/core/orchestration/task-runtime.d.ts +44 -98
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime.js +357 -1113
- package/dist/core/orchestration/task-runtime.js.map +1 -1
- package/dist/core/orchestration/worker-context-fork-reference.d.ts +20 -0
- package/dist/core/orchestration/worker-context-fork-reference.d.ts.map +1 -0
- package/dist/core/orchestration/worker-context-fork-reference.js +53 -0
- package/dist/core/orchestration/worker-context-fork-reference.js.map +1 -0
- package/dist/core/research/research-runner.d.ts +2 -0
- package/dist/core/research/research-runner.d.ts.map +1 -1
- package/dist/core/research/research-runner.js +1 -0
- package/dist/core/research/research-runner.js.map +1 -1
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +8 -0
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/secrets/credential-project.d.ts.map +1 -1
- package/dist/core/secrets/credential-project.js +24 -2
- package/dist/core/secrets/credential-project.js.map +1 -1
- package/dist/core/security/secret-text.d.ts +3 -0
- package/dist/core/security/secret-text.d.ts.map +1 -1
- package/dist/core/security/secret-text.js +15 -0
- package/dist/core/security/secret-text.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +160 -8
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +31 -4
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +751 -98
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts +5 -0
- package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -1
- package/dist/modes/interactive/components/activity-lane.js +133 -49
- package/dist/modes/interactive/components/activity-lane.js.map +1 -1
- package/dist/modes/interactive/components/agents-overlay.d.ts +40 -0
- package/dist/modes/interactive/components/agents-overlay.d.ts.map +1 -0
- package/dist/modes/interactive/components/agents-overlay.js +153 -0
- package/dist/modes/interactive/components/agents-overlay.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +18 -13
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.js +6 -3
- package/dist/modes/interactive/interactive-event-controller.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +51 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/key-handlers.d.ts +1 -0
- package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/key-handlers.js +1 -0
- package/dist/modes/interactive/key-handlers.js.map +1 -1
- package/docs/harness-architecture.md +61 -17
- package/docs/settings.md +17 -9
- package/docs/worker-profiles.md +6 -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,25 +1,114 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
3
|
import { compact } from "@caupulican/pi-agent-core/compaction/compaction";
|
|
4
|
+
import { classifyFailure, computeRetryDelayMs, sleepAbortable, } from "@caupulican/pi-agent-core/reliability";
|
|
3
5
|
import { sanitizeToolFailureContext } from "@caupulican/pi-agent-core/tool-failure-memory";
|
|
4
6
|
import { addUsage, createEmptyUsage } from "@caupulican/pi-agent-core/usage";
|
|
5
7
|
import { safeRealpathSync } from "../autonomy/path-scope.js";
|
|
6
8
|
import { composeSubagentSystemPrompt } from "../autonomy/subagent-prompt.js";
|
|
7
9
|
import { attemptUsageFromGatewayUsage, EMPTY_ATTEMPT_USAGE } from "../orchestration/attempt-usage.js";
|
|
10
|
+
import { CapabilityGatewayDeniedError } from "../orchestration/capability-gateway.js";
|
|
8
11
|
import { WorkerActionJournal } from "./worker-action-journal.js";
|
|
12
|
+
import { WorkerConversationOwnershipError } from "./worker-conversation-revision.js";
|
|
13
|
+
import { WorkerCompletionProtocolError, WorkerProviderTurnProtocol } from "./worker-provider-turn-protocol.js";
|
|
9
14
|
import { runWorker } from "./worker-runner.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import { WorkerTreeBudgetExceededError } from "./worker-tree-budget-coordinator.js";
|
|
16
|
+
/**
|
|
17
|
+
* Backoff policy for transient worker provider failures. Without it, a dropped provider socket
|
|
18
|
+
* kills the attempt instantly at $0 spend and an immediate re-dispatch hits the same dead
|
|
19
|
+
* connection (field session 019fd4dc: paired $0 `completion_error` lanes ~10s apart). Jitter
|
|
20
|
+
* de-synchronizes sibling workers that all lost the same connection at once.
|
|
21
|
+
*/
|
|
22
|
+
const WORKER_PROVIDER_RETRY_POLICY = {
|
|
23
|
+
maxAttempts: 3,
|
|
24
|
+
baseDelayMs: 2_000,
|
|
25
|
+
maxDelayMs: 30_000,
|
|
26
|
+
jitterRatio: 0.2,
|
|
27
|
+
};
|
|
28
|
+
export async function runProviderCompletionWithBackoff(args) {
|
|
29
|
+
for (let attempt = 1;; attempt++) {
|
|
30
|
+
try {
|
|
31
|
+
return await args.attempt();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
args.onAttemptFailure();
|
|
35
|
+
if (args.signal?.aborted)
|
|
36
|
+
throw error;
|
|
37
|
+
if (error instanceof WorkerCompletionProtocolError ||
|
|
38
|
+
error instanceof WorkerConversationOwnershipError ||
|
|
39
|
+
error instanceof CapabilityGatewayDeniedError ||
|
|
40
|
+
error instanceof WorkerTreeBudgetExceededError) {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
const classified = classifyFailure({
|
|
44
|
+
message: error instanceof Error ? error.message : String(error),
|
|
45
|
+
provider: args.provider,
|
|
46
|
+
});
|
|
47
|
+
if (!classified.retryable || attempt >= WORKER_PROVIDER_RETRY_POLICY.maxAttempts)
|
|
48
|
+
throw error;
|
|
49
|
+
const delayMs = computeRetryDelayMs(WORKER_PROVIDER_RETRY_POLICY, attempt, {
|
|
50
|
+
...(classified.retryAfterMs !== undefined ? { retryAfterMs: classified.retryAfterMs } : {}),
|
|
51
|
+
});
|
|
52
|
+
args.warn(`Worker ${args.laneId} provider request failed (${classified.reason}); retrying in ${Math.ceil(delayMs / 1000)}s (attempt ${attempt + 1}/${WORKER_PROVIDER_RETRY_POLICY.maxAttempts}).`);
|
|
53
|
+
await sleepAbortable(delayMs, args.signal);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
19
56
|
}
|
|
20
57
|
function isToolRequest(message) {
|
|
21
58
|
return message.role === "assistant" && message.content.some((content) => content.type === "toolCall");
|
|
22
59
|
}
|
|
60
|
+
function workerCompletionCallbackFailure(error) {
|
|
61
|
+
if (error instanceof WorkerCompletionProtocolError ||
|
|
62
|
+
error instanceof WorkerConversationOwnershipError ||
|
|
63
|
+
error instanceof CapabilityGatewayDeniedError ||
|
|
64
|
+
error instanceof WorkerTreeBudgetExceededError) {
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
return new WorkerCompletionProtocolError("Worker completion callback failed before its authority and durable transcript were verified.");
|
|
68
|
+
}
|
|
69
|
+
function callbackEvidencedCompletion(result, historyLength, emittedMessages) {
|
|
70
|
+
if (result.messages !== undefined && result.messages.length < historyLength) {
|
|
71
|
+
throw new WorkerCompletionProtocolError("Worker completion protocol returned fewer messages than its input history.");
|
|
72
|
+
}
|
|
73
|
+
let suffix;
|
|
74
|
+
if (result.messages !== undefined) {
|
|
75
|
+
suffix = result.messages.slice(historyLength);
|
|
76
|
+
if (!isDeepStrictEqual(suffix, emittedMessages)) {
|
|
77
|
+
throw new WorkerCompletionProtocolError("Worker completion protocol returned a suffix without exact callback evidence.");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
suffix = [...emittedMessages];
|
|
82
|
+
}
|
|
83
|
+
if (suffix.length === 0) {
|
|
84
|
+
throw new WorkerCompletionProtocolError("Worker completion protocol returned no durable message suffix.");
|
|
85
|
+
}
|
|
86
|
+
if (!suffix.some((message) => message.role === "assistant")) {
|
|
87
|
+
throw new WorkerCompletionProtocolError("Worker completion protocol returned no assistant in its durable suffix.");
|
|
88
|
+
}
|
|
89
|
+
const assistants = emittedMessages.filter((message) => message.role === "assistant");
|
|
90
|
+
const finalMessage = emittedMessages.at(-1);
|
|
91
|
+
if (!finalMessage || finalMessage.role !== "assistant") {
|
|
92
|
+
throw new WorkerCompletionProtocolError("Worker completion protocol did not durably emit an assistant as its terminal message.");
|
|
93
|
+
}
|
|
94
|
+
const usage = createEmptyUsage();
|
|
95
|
+
for (const assistant of assistants)
|
|
96
|
+
addUsage(usage, assistant.usage);
|
|
97
|
+
const text = finalMessage.content.flatMap((content) => (content.type === "text" ? [content.text] : [])).join("");
|
|
98
|
+
if (result.text !== text ||
|
|
99
|
+
result.stopReason !== finalMessage.stopReason ||
|
|
100
|
+
!isDeepStrictEqual(result.usage, usage)) {
|
|
101
|
+
throw new WorkerCompletionProtocolError("Worker completion result disagrees with its durable callback-evidenced terminal assistant.");
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
suffix,
|
|
105
|
+
completion: {
|
|
106
|
+
text,
|
|
107
|
+
usage,
|
|
108
|
+
stopReason: finalMessage.stopReason,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
23
112
|
/**
|
|
24
113
|
* Constructs one execution exactly once. The caller registers {@link ledger} before calling
|
|
25
114
|
* {@link run}, allowing synchronous disposal to observe mutations and cumulative usage while the
|
|
@@ -47,7 +136,6 @@ export function createWorkerAttemptExecutor(options) {
|
|
|
47
136
|
fencingToken: options.durableHandle.fencingToken,
|
|
48
137
|
})
|
|
49
138
|
: undefined;
|
|
50
|
-
let providerContextCompacted = options.conversation.hasProviderCompaction();
|
|
51
139
|
let retentionWarningEmitted = false;
|
|
52
140
|
let firstMailboxPoll = true;
|
|
53
141
|
let ran = false;
|
|
@@ -69,74 +157,122 @@ export function createWorkerAttemptExecutor(options) {
|
|
|
69
157
|
throw signal.reason;
|
|
70
158
|
return { maxTokens: requestedMaxTokens, release: () => undefined };
|
|
71
159
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
160
|
+
const createRetentionPolicy = (completeSignal) => {
|
|
161
|
+
if (!options.retentionPolicy)
|
|
162
|
+
return undefined;
|
|
163
|
+
let failedCompactionUsage;
|
|
164
|
+
return {
|
|
165
|
+
...options.retentionPolicy,
|
|
166
|
+
generateVerifiedCompaction: async (preparation) => {
|
|
167
|
+
const compactionUsage = createEmptyUsage();
|
|
168
|
+
failedCompactionUsage = compactionUsage;
|
|
169
|
+
return compact(preparation, options.model, undefined, undefined, undefined, completeSignal, options.thinkingLevel, undefined, undefined, {
|
|
170
|
+
completion: async (model, context, requestOptions) => {
|
|
171
|
+
const requestSignal = requestOptions.signal ?? completeSignal;
|
|
172
|
+
requestSignal.throwIfAborted();
|
|
173
|
+
if (model.provider !== options.model.provider || model.id !== options.model.id) {
|
|
174
|
+
throw new Error("Worker compaction attempted to select a model outside the lane binding.");
|
|
175
|
+
}
|
|
176
|
+
options.toolSurface.gateway?.assertBudgetAvailable("worker_compaction_provider_completion");
|
|
177
|
+
const availableTokens = remainingAttemptTokens();
|
|
178
|
+
if (availableTokens !== undefined && availableTokens <= 0) {
|
|
179
|
+
throw new Error("Worker token budget exhausted before compaction provider completion.");
|
|
180
|
+
}
|
|
181
|
+
const requestedMaxTokens = requestOptions.maxTokens;
|
|
182
|
+
const configuredMaxTokens = typeof requestedMaxTokens === "number" &&
|
|
183
|
+
Number.isSafeInteger(requestedMaxTokens) &&
|
|
184
|
+
requestedMaxTokens > 0
|
|
185
|
+
? requestedMaxTokens
|
|
186
|
+
: options.laneCapability.laneMaxOutputTokens;
|
|
187
|
+
const maxTokens = Math.min(configuredMaxTokens, availableTokens ?? configuredMaxTokens);
|
|
188
|
+
if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) {
|
|
189
|
+
throw new Error("Worker compaction provider completion has no valid remaining token budget.");
|
|
190
|
+
}
|
|
191
|
+
let preflightFailed = false;
|
|
192
|
+
let preflightFailure;
|
|
193
|
+
const providerTurn = new WorkerProviderTurnProtocol({
|
|
194
|
+
acquireReservation: () => reserveProviderBudget(maxTokens, "worker_compaction_provider_completion", requestSignal),
|
|
195
|
+
signal: requestSignal,
|
|
196
|
+
onFailure: (error) => {
|
|
197
|
+
if (preflightFailed)
|
|
198
|
+
return;
|
|
199
|
+
preflightFailed = true;
|
|
200
|
+
preflightFailure = error;
|
|
110
201
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
laneKind: "worker-compaction",
|
|
114
|
-
});
|
|
115
|
-
addUsage(compactionUsage, completion.usage);
|
|
116
|
-
const response = {
|
|
117
|
-
role: "assistant",
|
|
118
|
-
content: completion.text ? [{ type: "text", text: completion.text }] : [],
|
|
119
|
-
api: options.model.api,
|
|
120
|
-
provider: options.model.provider,
|
|
121
|
-
model: options.model.id,
|
|
122
|
-
usage: completion.usage,
|
|
123
|
-
stopReason: completion.stopReason,
|
|
124
|
-
timestamp: Date.now(),
|
|
125
|
-
...(completion.stopReason === "error"
|
|
126
|
-
? { errorMessage: completion.text || "Worker compaction provider request failed." }
|
|
202
|
+
...(options.toolSurface.gateway
|
|
203
|
+
? { recordUsage: (delta) => options.toolSurface.gateway?.recordUsage(delta) }
|
|
127
204
|
: {}),
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
205
|
+
});
|
|
206
|
+
try {
|
|
207
|
+
let completion;
|
|
208
|
+
try {
|
|
209
|
+
completion = await options.runIsolatedCompletion({
|
|
210
|
+
systemPrompt: context.systemPrompt ?? "",
|
|
211
|
+
messages: context.messages,
|
|
212
|
+
model: options.model,
|
|
213
|
+
thinkingLevel: options.thinkingLevel,
|
|
214
|
+
maxTokens,
|
|
215
|
+
requestPreflight: () => providerTurn.requestPreflight(),
|
|
216
|
+
signal: requestSignal,
|
|
217
|
+
cacheRetention: "none",
|
|
218
|
+
laneKind: "worker-compaction",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
providerTurn.close();
|
|
223
|
+
requestSignal.throwIfAborted();
|
|
224
|
+
if (preflightFailed)
|
|
225
|
+
throw workerCompletionCallbackFailure(preflightFailure);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
requestSignal.throwIfAborted();
|
|
229
|
+
const response = {
|
|
230
|
+
role: "assistant",
|
|
231
|
+
content: completion.text ? [{ type: "text", text: completion.text }] : [],
|
|
232
|
+
api: options.model.api,
|
|
233
|
+
provider: options.model.provider,
|
|
234
|
+
model: options.model.id,
|
|
235
|
+
usage: completion.usage,
|
|
236
|
+
stopReason: completion.stopReason,
|
|
237
|
+
timestamp: Date.now(),
|
|
238
|
+
...(completion.stopReason === "error"
|
|
239
|
+
? { errorMessage: completion.text || "Worker compaction provider request failed." }
|
|
240
|
+
: {}),
|
|
241
|
+
};
|
|
242
|
+
addUsage(compactionUsage, completion.usage);
|
|
243
|
+
if (providerTurn.hasOutstandingAssistantReservation()) {
|
|
244
|
+
providerTurn.accountAssistantUsage(completion.usage);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
providerTurn.accountUnverifiedResultUsageDelta(completion.usage);
|
|
248
|
+
}
|
|
249
|
+
checkpointUsage("Persisted worker compaction provider usage before verification.");
|
|
250
|
+
if (providerTurn.hasSuccessfulPreflight()) {
|
|
251
|
+
try {
|
|
252
|
+
providerTurn.consumeTerminalAssistantAndHold();
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (!preflightFailed) {
|
|
256
|
+
preflightFailed = true;
|
|
257
|
+
preflightFailure = error;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
providerTurn.close();
|
|
262
|
+
requestSignal.throwIfAborted();
|
|
263
|
+
if (preflightFailed)
|
|
264
|
+
throw workerCompletionCallbackFailure(preflightFailure);
|
|
265
|
+
providerTurn.assertProviderOutputPreflight("compaction");
|
|
266
|
+
return response;
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
providerTurn.close();
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
},
|
|
274
|
+
getFailedCompactionUsage: () => failedCompactionUsage,
|
|
275
|
+
};
|
|
140
276
|
};
|
|
141
277
|
const ledger = {
|
|
142
278
|
changedFiles,
|
|
@@ -149,6 +285,7 @@ export function createWorkerAttemptExecutor(options) {
|
|
|
149
285
|
if (ran)
|
|
150
286
|
throw new Error("A worker attempt executor may run only once.");
|
|
151
287
|
ran = true;
|
|
288
|
+
options.conversation.beginAttemptUsage(options.durableHandle.attemptId);
|
|
152
289
|
if (!options.hasPersistedUsageCheckpoint) {
|
|
153
290
|
checkpointUsage("Persisted deterministic cumulative usage baseline for the durable worker transcript.");
|
|
154
291
|
}
|
|
@@ -184,171 +321,299 @@ export function createWorkerAttemptExecutor(options) {
|
|
|
184
321
|
stopReason: options.recoveredTerminal.stopReason,
|
|
185
322
|
};
|
|
186
323
|
}
|
|
324
|
+
const retentionPolicy = createRetentionPolicy(signal);
|
|
187
325
|
const persistedToolAssistantIds = new Set();
|
|
188
326
|
const pendingToolAssistants = new Map();
|
|
189
|
-
let
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const persistToolRequest = (message) => {
|
|
195
|
-
const toolCallIds = message.content.flatMap((content) => content.type === "toolCall" ? [content.id] : []);
|
|
196
|
-
if (toolCallIds.length === 0 || toolCallIds.every((id) => persistedToolAssistantIds.has(id)))
|
|
197
|
-
return;
|
|
198
|
-
recordAssistantUsage(options.toolSurface, message);
|
|
199
|
-
options.conversation.appendMessage(message);
|
|
200
|
-
for (const id of toolCallIds) {
|
|
201
|
-
persistedToolAssistantIds.add(id);
|
|
202
|
-
pendingToolAssistants.delete(id);
|
|
203
|
-
}
|
|
204
|
-
checkpointUsage("Persisted worker assistant tool request and its cumulative provider usage.");
|
|
205
|
-
releaseProviderReservation();
|
|
327
|
+
let activeProviderTurn;
|
|
328
|
+
const closeActiveProviderTurn = () => {
|
|
329
|
+
const active = activeProviderTurn;
|
|
330
|
+
activeProviderTurn = undefined;
|
|
331
|
+
active?.close();
|
|
206
332
|
};
|
|
207
333
|
options.toolSurface.gateway?.assertBudgetAvailable("worker_provider_completion");
|
|
208
334
|
const availableTokens = remainingAttemptTokens();
|
|
209
335
|
if (availableTokens !== undefined && availableTokens <= 0) {
|
|
210
336
|
throw new Error("Worker token budget exhausted before provider completion.");
|
|
211
337
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
options.conversation.appendMessage({
|
|
215
|
-
role: "user",
|
|
216
|
-
content: [{ type: "text", text: userPrompt }],
|
|
217
|
-
timestamp: Date.now(),
|
|
218
|
-
});
|
|
219
|
-
history = options.conversation.getProviderMessages();
|
|
220
|
-
}
|
|
338
|
+
options.conversation.ensureAttemptUserPrompt(options.durableHandle.attemptId, userPrompt);
|
|
339
|
+
let history = [];
|
|
221
340
|
let completion;
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
341
|
+
const attemptProviderCompletion = async () => {
|
|
342
|
+
// Later attempts resume from the durably persisted transcript, not a stale snapshot.
|
|
343
|
+
const transcriptCommit = options.conversation.beginTranscriptCommit();
|
|
344
|
+
history = transcriptCommit.history;
|
|
345
|
+
const historyLength = history.length;
|
|
346
|
+
const transcriptCursor = transcriptCommit.cursor;
|
|
347
|
+
const durableCallbackMessages = [];
|
|
348
|
+
let callbackFailed = false;
|
|
349
|
+
let callbackFailure;
|
|
350
|
+
const retainCallbackFailure = (error) => {
|
|
351
|
+
if (callbackFailed)
|
|
352
|
+
return;
|
|
353
|
+
callbackFailed = true;
|
|
354
|
+
callbackFailure = error;
|
|
355
|
+
};
|
|
356
|
+
const providerTurn = new WorkerProviderTurnProtocol({
|
|
357
|
+
acquireReservation: () => reserveProviderBudget(options.laneCapability.laneMaxOutputTokens, "worker_provider_completion", signal),
|
|
358
|
+
signal,
|
|
359
|
+
onFailure: retainCallbackFailure,
|
|
360
|
+
...(options.toolSurface.gateway
|
|
361
|
+
? { recordUsage: (delta) => options.toolSurface.gateway?.recordUsage(delta) }
|
|
362
|
+
: {}),
|
|
363
|
+
});
|
|
364
|
+
activeProviderTurn = providerTurn;
|
|
365
|
+
const persistToolRequest = (message) => {
|
|
366
|
+
signal.throwIfAborted();
|
|
367
|
+
const toolCallIds = message.content.flatMap((content) => content.type === "toolCall" ? [content.id] : []);
|
|
368
|
+
if (toolCallIds.length === 0 || toolCallIds.every((id) => persistedToolAssistantIds.has(id)))
|
|
369
|
+
return;
|
|
370
|
+
providerTurn.accountAssistantUsage(message.usage);
|
|
371
|
+
options.conversation.appendMessage(message);
|
|
372
|
+
for (const id of toolCallIds) {
|
|
373
|
+
persistedToolAssistantIds.add(id);
|
|
374
|
+
pendingToolAssistants.delete(id);
|
|
375
|
+
}
|
|
376
|
+
checkpointUsage("Persisted worker assistant tool request and its cumulative provider usage.");
|
|
377
|
+
providerTurn.consumeToolAssistantAndRelease();
|
|
378
|
+
durableCallbackMessages.push(message);
|
|
379
|
+
};
|
|
380
|
+
let committed = false;
|
|
381
|
+
const abortTranscriptCursor = () => {
|
|
382
|
+
providerTurn.close();
|
|
383
|
+
options.conversation.abortTranscriptCommit(transcriptCursor);
|
|
384
|
+
};
|
|
385
|
+
signal.addEventListener("abort", abortTranscriptCursor, { once: true });
|
|
386
|
+
try {
|
|
387
|
+
signal.throwIfAborted();
|
|
388
|
+
let result;
|
|
389
|
+
try {
|
|
390
|
+
result = await options.runIsolatedCompletion({
|
|
391
|
+
systemPrompt: composeSubagentSystemPrompt({
|
|
392
|
+
soul: options.soul,
|
|
393
|
+
rolePrompt: [systemPrompt, options.workerResourceSystemPrompt]
|
|
394
|
+
.filter(Boolean)
|
|
395
|
+
.join("\n\n"),
|
|
396
|
+
}),
|
|
397
|
+
history,
|
|
398
|
+
messages: [],
|
|
399
|
+
model: options.model,
|
|
400
|
+
thinkingLevel: options.thinkingLevel,
|
|
401
|
+
maxTokens: Math.min(options.laneCapability.laneMaxOutputTokens, availableTokens ?? Number.POSITIVE_INFINITY),
|
|
402
|
+
tools: options.toolSurface.tools,
|
|
403
|
+
requestPreflight: () => providerTurn.requestPreflight(),
|
|
404
|
+
beforeToolCall: async (context, toolSignal) => {
|
|
261
405
|
try {
|
|
262
|
-
|
|
406
|
+
signal.throwIfAborted();
|
|
407
|
+
persistToolRequest(context.assistantMessage);
|
|
408
|
+
const decision = await options.toolSurface.beforeToolCall(context, toolSignal);
|
|
409
|
+
signal.throwIfAborted();
|
|
410
|
+
if (decision?.block) {
|
|
411
|
+
toolIssues.add(`${context.toolCall.name} blocked: ${decision.reason ?? "capability denied"}`);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
checkpointUsage(`Authorized worker tool '${context.toolCall.name}' under its durable grant.`);
|
|
415
|
+
}
|
|
416
|
+
return decision;
|
|
263
417
|
}
|
|
264
|
-
catch {
|
|
265
|
-
|
|
418
|
+
catch (error) {
|
|
419
|
+
retainCallbackFailure(error);
|
|
420
|
+
throw error;
|
|
266
421
|
}
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if (isError)
|
|
271
|
-
toolIssues.add(`${toolCall.name} failed during isolated execution`);
|
|
272
|
-
return undefined;
|
|
273
|
-
},
|
|
274
|
-
onMessage: (message) => {
|
|
275
|
-
if (message.role === "assistant" && isToolRequest(message)) {
|
|
276
|
-
// Known calls are normalized before beforeToolCall and persist from that hook.
|
|
277
|
-
// Retain the request only so immediate unknown/malformed results can close the
|
|
278
|
-
// transcript without freezing pre-repair arguments into durable history.
|
|
279
|
-
for (const content of message.content) {
|
|
280
|
-
if (content.type === "toolCall")
|
|
281
|
-
pendingToolAssistants.set(content.id, message);
|
|
282
|
-
}
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
if (message.role === "toolResult" && !persistedToolAssistantIds.has(message.toolCallId)) {
|
|
286
|
-
const pending = pendingToolAssistants.get(message.toolCallId);
|
|
287
|
-
if (pending)
|
|
288
|
-
persistToolRequest(pending);
|
|
289
|
-
}
|
|
290
|
-
if (message.role === "assistant")
|
|
291
|
-
recordAssistantUsage(options.toolSurface, message);
|
|
292
|
-
options.conversation.appendMessage(message);
|
|
293
|
-
options.agentControl.acknowledgeMailboxMessage(options.agentId, message);
|
|
294
|
-
if (message.role === "assistant") {
|
|
295
|
-
checkpointUsage("Persisted worker assistant response and its cumulative provider usage.");
|
|
296
|
-
releaseProviderReservation();
|
|
297
|
-
}
|
|
298
|
-
if (message.role === "toolResult")
|
|
299
|
-
checkpointUsage(`Persisted worker tool result '${message.toolCallId}'.`);
|
|
300
|
-
},
|
|
301
|
-
getSteeringMessages: async () => {
|
|
302
|
-
const includeFollowUp = firstMailboxPoll;
|
|
303
|
-
firstMailboxPoll = false;
|
|
304
|
-
return options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, includeFollowUp);
|
|
305
|
-
},
|
|
306
|
-
getFollowUpMessages: async () => options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, true),
|
|
307
|
-
...(retentionPolicy
|
|
308
|
-
? {
|
|
309
|
-
transformContext: async (messages) => {
|
|
422
|
+
},
|
|
423
|
+
afterToolCall: async ({ toolCall, args, isError }) => {
|
|
310
424
|
try {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
425
|
+
if ((toolCall.name === "write" || toolCall.name === "edit") &&
|
|
426
|
+
args &&
|
|
427
|
+
typeof args === "object" &&
|
|
428
|
+
!Array.isArray(args)) {
|
|
429
|
+
const rawPath = args.path;
|
|
430
|
+
if (typeof rawPath === "string" && rawPath.length > 0) {
|
|
431
|
+
const absolutePath = path.isAbsolute(rawPath)
|
|
432
|
+
? path.resolve(rawPath)
|
|
433
|
+
: path.resolve(options.cwd, rawPath);
|
|
434
|
+
let canonicalPath = absolutePath;
|
|
435
|
+
try {
|
|
436
|
+
canonicalPath = safeRealpathSync(absolutePath);
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
// The operation entered execution; retain its lexical target if canonicalization failed.
|
|
440
|
+
}
|
|
441
|
+
recordChangedFile(path.relative(options.cwd, canonicalPath).split(path.sep).join("/"));
|
|
442
|
+
}
|
|
315
443
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
444
|
+
if (isError)
|
|
445
|
+
toolIssues.add(`${toolCall.name} failed during isolated execution`);
|
|
446
|
+
signal.throwIfAborted();
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
retainCallbackFailure(error);
|
|
451
|
+
throw error;
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
onMessage: (message) => {
|
|
455
|
+
try {
|
|
456
|
+
signal.throwIfAborted();
|
|
457
|
+
if (message.role === "assistant" && isToolRequest(message)) {
|
|
458
|
+
// Known calls are normalized before beforeToolCall and persist from that hook.
|
|
459
|
+
// Retain the request only so immediate unknown/malformed results can close the
|
|
460
|
+
// transcript without freezing pre-repair arguments into durable history.
|
|
461
|
+
for (const content of message.content) {
|
|
462
|
+
if (content.type === "toolCall")
|
|
463
|
+
pendingToolAssistants.set(content.id, message);
|
|
464
|
+
}
|
|
465
|
+
return;
|
|
320
466
|
}
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
467
|
+
if (message.role === "toolResult" &&
|
|
468
|
+
!persistedToolAssistantIds.has(message.toolCallId)) {
|
|
469
|
+
const pending = pendingToolAssistants.get(message.toolCallId);
|
|
470
|
+
if (pending)
|
|
471
|
+
persistToolRequest(pending);
|
|
324
472
|
}
|
|
325
|
-
|
|
473
|
+
if (message.role === "assistant") {
|
|
474
|
+
providerTurn.accountAssistantUsage(message.usage);
|
|
475
|
+
}
|
|
476
|
+
options.conversation.appendMessage(message);
|
|
477
|
+
options.agentControl.acknowledgeMailboxMessage(options.agentId, message);
|
|
478
|
+
if (message.role === "assistant") {
|
|
479
|
+
checkpointUsage("Persisted worker assistant response and its cumulative provider usage.");
|
|
480
|
+
providerTurn.consumeTerminalAssistantAndHold();
|
|
481
|
+
}
|
|
482
|
+
if (message.role === "toolResult")
|
|
483
|
+
checkpointUsage(`Persisted worker tool result '${message.toolCallId}'.`);
|
|
484
|
+
durableCallbackMessages.push(message);
|
|
326
485
|
}
|
|
327
486
|
catch (error) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
487
|
+
retainCallbackFailure(error);
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
getSteeringMessages: async () => {
|
|
492
|
+
try {
|
|
493
|
+
signal.throwIfAborted();
|
|
494
|
+
const includeFollowUp = firstMailboxPoll;
|
|
495
|
+
firstMailboxPoll = false;
|
|
496
|
+
const messages = options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, includeFollowUp);
|
|
497
|
+
signal.throwIfAborted();
|
|
332
498
|
return messages;
|
|
333
499
|
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
retainCallbackFailure(error);
|
|
502
|
+
throw error;
|
|
503
|
+
}
|
|
334
504
|
},
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
505
|
+
getFollowUpMessages: async () => {
|
|
506
|
+
try {
|
|
507
|
+
signal.throwIfAborted();
|
|
508
|
+
const messages = options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, true);
|
|
509
|
+
signal.throwIfAborted();
|
|
510
|
+
return messages;
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
retainCallbackFailure(error);
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
...(retentionPolicy
|
|
518
|
+
? {
|
|
519
|
+
transformContext: async (messages) => {
|
|
520
|
+
try {
|
|
521
|
+
signal.throwIfAborted();
|
|
522
|
+
const retained = await options.conversation.compactProviderContext(retentionPolicy, signal);
|
|
523
|
+
signal.throwIfAborted();
|
|
524
|
+
if (retained.contextUsage.tokens > retentionPolicy.maxContextTokens &&
|
|
525
|
+
!retentionWarningEmitted) {
|
|
526
|
+
retentionWarningEmitted = true;
|
|
527
|
+
options.warn(`Worker ${options.laneId} has one retained turn larger than its context policy; provider overflow recovery may be required.`);
|
|
528
|
+
}
|
|
529
|
+
if (retained.status !== "compacted_verified" &&
|
|
530
|
+
retained.status !== "compacted_deterministic") {
|
|
531
|
+
return messages;
|
|
532
|
+
}
|
|
533
|
+
return sanitizeToolFailureContext(retained.context.messages, "").messages;
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
if (signal.aborted) {
|
|
537
|
+
retainCallbackFailure(error);
|
|
538
|
+
signal.throwIfAborted();
|
|
539
|
+
}
|
|
540
|
+
if (error instanceof WorkerConversationOwnershipError) {
|
|
541
|
+
retainCallbackFailure(error);
|
|
542
|
+
throw error;
|
|
543
|
+
}
|
|
544
|
+
if (!retentionWarningEmitted) {
|
|
545
|
+
retentionWarningEmitted = true;
|
|
546
|
+
options.warn(`Worker context retention failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
547
|
+
}
|
|
548
|
+
return messages;
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
}
|
|
552
|
+
: {}),
|
|
553
|
+
signal,
|
|
554
|
+
cacheRetention: "short",
|
|
555
|
+
laneKind: "worker",
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
catch (error) {
|
|
559
|
+
providerTurn.close();
|
|
560
|
+
if (signal.aborted)
|
|
561
|
+
throw error;
|
|
562
|
+
if (callbackFailed)
|
|
563
|
+
throw workerCompletionCallbackFailure(callbackFailure);
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
try {
|
|
567
|
+
providerTurn.assertEverySuccessfulPreflightConsumed();
|
|
568
|
+
}
|
|
569
|
+
catch (error) {
|
|
570
|
+
retainCallbackFailure(error);
|
|
571
|
+
}
|
|
572
|
+
signal.throwIfAborted();
|
|
573
|
+
if (providerTurn.accountUnverifiedResultUsageDelta(result.usage)) {
|
|
574
|
+
checkpointUsage("Persisted supplemental provider result usage before rejecting unverified completion evidence.");
|
|
575
|
+
}
|
|
576
|
+
providerTurn.close();
|
|
577
|
+
if (callbackFailed)
|
|
578
|
+
throw workerCompletionCallbackFailure(callbackFailure);
|
|
579
|
+
providerTurn.assertProviderOutputPreflight();
|
|
580
|
+
const evidenced = callbackEvidencedCompletion(result, historyLength, durableCallbackMessages);
|
|
581
|
+
signal.throwIfAborted();
|
|
582
|
+
const appended = options.conversation.commitTranscript(transcriptCursor, evidenced.suffix, {
|
|
583
|
+
appendMissing: false,
|
|
584
|
+
});
|
|
585
|
+
if (appended !== 0) {
|
|
586
|
+
throw new WorkerConversationOwnershipError("Worker callback transcript verification unexpectedly appended missing messages.");
|
|
587
|
+
}
|
|
588
|
+
committed = true;
|
|
589
|
+
return evidenced.completion;
|
|
590
|
+
}
|
|
591
|
+
finally {
|
|
592
|
+
signal.removeEventListener("abort", abortTranscriptCursor);
|
|
593
|
+
providerTurn.close();
|
|
594
|
+
if (activeProviderTurn === providerTurn)
|
|
595
|
+
activeProviderTurn = undefined;
|
|
596
|
+
if (!committed)
|
|
597
|
+
options.conversation.abortTranscriptCommit(transcriptCursor);
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
try {
|
|
601
|
+
completion = await runProviderCompletionWithBackoff({
|
|
602
|
+
attempt: attemptProviderCompletion,
|
|
603
|
+
onAttemptFailure: closeActiveProviderTurn,
|
|
604
|
+
provider: options.model.provider,
|
|
605
|
+
laneId: options.laneId,
|
|
606
|
+
warn: options.warn,
|
|
607
|
+
...(signal ? { signal } : {}),
|
|
340
608
|
});
|
|
341
609
|
}
|
|
342
610
|
finally {
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
if (!providerContextCompacted) {
|
|
346
|
-
options.conversation.commitTranscript(completion.messages ?? options.conversation.getProviderMessages());
|
|
611
|
+
closeActiveProviderTurn();
|
|
347
612
|
}
|
|
348
|
-
checkpointUsage(
|
|
613
|
+
const cumulativeUsage = checkpointUsage("Verified the callback-persisted worker conversation terminal suffix.");
|
|
349
614
|
return {
|
|
350
615
|
text: completion.text,
|
|
351
|
-
costUsd:
|
|
616
|
+
costUsd: cumulativeUsage.costUsd,
|
|
352
617
|
stopReason: String(completion.stopReason),
|
|
353
618
|
changedFiles: [...changedFiles],
|
|
354
619
|
blockers: [...toolIssues],
|