@caupulican/pi-adaptative 0.86.3 → 0.86.6
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 +44 -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 +21 -2
- 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 +30 -2
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +675 -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-execution-policy.d.ts.map +1 -1
- package/dist/core/delegation/worker-execution-policy.js +14 -2
- package/dist/core/delegation/worker-execution-policy.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/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +15 -0
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +6 -0
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +1 -0
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +1 -0
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +25 -3
- package/dist/core/goals/goal-tool-core.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/orchestration/worker-execution-contract.d.ts.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.js +4 -2
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
- 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 +33 -15
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +763 -115
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/goal.d.ts +1 -0
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +9 -1
- package/dist/core/tools/goal.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
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { ATTEMPT_STATUSES, MAX_ORCHESTRATION_COLLECTION_LENGTH, MAX_ORCHESTRATION_DESCRIPTION_LENGTH, MAX_ORCHESTRATION_IDENTIFIER_LENGTH, TASK_STATUSES, WORKER_ROLES, } from "../orchestration/contracts.js";
|
|
3
|
+
import { isPlainRecord } from "../util/value-guards.js";
|
|
4
|
+
import { DEFAULT_WORKER_FLEET_LIMITS } from "./worker-fleet-limits.js";
|
|
5
|
+
/** Keep task views within the same durable session fan-out ceiling as the worker fleet. */
|
|
6
|
+
export const MAX_WORKER_TASK_VIEW_ENTRIES = Math.min(DEFAULT_WORKER_FLEET_LIMITS.maxAgentsPerSession, DEFAULT_WORKER_FLEET_LIMITS.maxQueuedDispatches);
|
|
7
|
+
/** Bound model-facing serialization independently of task-count and field-count limits. */
|
|
8
|
+
export const MAX_WORKER_TASK_VIEW_BYTES = 64 * 1024;
|
|
9
|
+
function boundedString(value, maxLength) {
|
|
10
|
+
return typeof value === "string" && value.length > 0 ? value.slice(0, maxLength) : undefined;
|
|
11
|
+
}
|
|
12
|
+
function isListedValue(value, allowed) {
|
|
13
|
+
return typeof value === "string" && allowed.some((candidate) => candidate === value);
|
|
14
|
+
}
|
|
15
|
+
function boundedIdentifierArray(value) {
|
|
16
|
+
if (!Array.isArray(value))
|
|
17
|
+
return [];
|
|
18
|
+
const identifiers = [];
|
|
19
|
+
for (let index = 0; index < Math.min(value.length, MAX_ORCHESTRATION_COLLECTION_LENGTH); index += 1) {
|
|
20
|
+
const identifier = boundedString(value[index], MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
21
|
+
if (identifier)
|
|
22
|
+
identifiers.push(identifier);
|
|
23
|
+
}
|
|
24
|
+
return identifiers;
|
|
25
|
+
}
|
|
26
|
+
function taskViewCandidate(recordTaskId, value) {
|
|
27
|
+
if (!isPlainRecord(value) || !isPlainRecord(value.task))
|
|
28
|
+
return undefined;
|
|
29
|
+
if (value.task.taskId !== recordTaskId)
|
|
30
|
+
return undefined;
|
|
31
|
+
const taskId = boundedString(value.task.taskId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
32
|
+
const title = boundedString(value.task.title, MAX_ORCHESTRATION_DESCRIPTION_LENGTH);
|
|
33
|
+
const createdAt = boundedString(value.task.createdAt, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
34
|
+
if (!taskId ||
|
|
35
|
+
!title ||
|
|
36
|
+
!createdAt ||
|
|
37
|
+
!isListedValue(value.task.role, WORKER_ROLES) ||
|
|
38
|
+
!isListedValue(value.task.status, TASK_STATUSES)) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const verificationOfTaskId = boundedString(value.task.verificationOfTaskId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
42
|
+
return {
|
|
43
|
+
attemptIds: value.attemptIds,
|
|
44
|
+
verification: value.verification,
|
|
45
|
+
taskId,
|
|
46
|
+
title,
|
|
47
|
+
role: value.task.role,
|
|
48
|
+
status: value.task.status,
|
|
49
|
+
dependsOn: boundedIdentifierArray(value.task.dependsOn),
|
|
50
|
+
createdAt,
|
|
51
|
+
...(verificationOfTaskId ? { verificationOfTaskId } : {}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function latestExistingAttempt(snapshot, attemptIds) {
|
|
55
|
+
if (!Array.isArray(attemptIds))
|
|
56
|
+
return undefined;
|
|
57
|
+
const firstInspectedIndex = Math.max(0, attemptIds.length - MAX_ORCHESTRATION_COLLECTION_LENGTH);
|
|
58
|
+
for (let index = attemptIds.length - 1; index >= firstInspectedIndex; index -= 1) {
|
|
59
|
+
const attemptId = attemptIds[index];
|
|
60
|
+
if (typeof attemptId !== "string" || !Object.hasOwn(snapshot.attempts, attemptId))
|
|
61
|
+
continue;
|
|
62
|
+
return snapshot.attempts[attemptId];
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
function projectAttempt(value) {
|
|
67
|
+
if (!isPlainRecord(value) || !isListedValue(value.status, ATTEMPT_STATUSES))
|
|
68
|
+
return undefined;
|
|
69
|
+
const dispatch = isPlainRecord(value.dispatch) ? value.dispatch : undefined;
|
|
70
|
+
const agentId = boundedString(value.agentId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH) ??
|
|
71
|
+
boundedString(dispatch?.logicalLaneId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
72
|
+
const reasonCode = boundedString(value.reasonCode, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
73
|
+
const retry = isPlainRecord(value.retry) ? value.retry : undefined;
|
|
74
|
+
const retriesUsed = retry?.retriesUsed;
|
|
75
|
+
const notBefore = boundedString(retry?.notBefore, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
76
|
+
const validRetry = Number.isSafeInteger(retriesUsed) && Number(retriesUsed) > 0 && notBefore;
|
|
77
|
+
return {
|
|
78
|
+
...(agentId ? { agentId } : {}),
|
|
79
|
+
status: value.status,
|
|
80
|
+
...(reasonCode ? { reasonCode } : {}),
|
|
81
|
+
...(validRetry ? { retry: { retriesUsed: Number(retriesUsed), notBefore } } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function projectVerification(value) {
|
|
85
|
+
if (!isPlainRecord(value) || !isListedValue(value.verdict, ["accepted", "rejected", "inconclusive"])) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
const verifierTaskId = boundedString(value.verifierTaskId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
89
|
+
const reasonCode = boundedString(value.reasonCode, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);
|
|
90
|
+
return verifierTaskId && reasonCode ? { verifierTaskId, verdict: value.verdict, reasonCode } : undefined;
|
|
91
|
+
}
|
|
92
|
+
function projectTask(snapshot, candidate) {
|
|
93
|
+
const attempt = projectAttempt(latestExistingAttempt(snapshot, candidate.attemptIds));
|
|
94
|
+
const verificationOutcome = projectVerification(candidate.verification);
|
|
95
|
+
return {
|
|
96
|
+
taskId: candidate.taskId,
|
|
97
|
+
title: candidate.title,
|
|
98
|
+
role: candidate.role,
|
|
99
|
+
status: candidate.status,
|
|
100
|
+
dependsOn: candidate.dependsOn,
|
|
101
|
+
...(candidate.verificationOfTaskId ? { verificationOfTaskId: candidate.verificationOfTaskId } : {}),
|
|
102
|
+
...(verificationOutcome ? { verificationOutcome } : {}),
|
|
103
|
+
...(attempt ? { latestAttempt: attempt } : {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function compareTasksNewestFirst(left, right) {
|
|
107
|
+
return right.createdAt.localeCompare(left.createdAt) || left.taskId.localeCompare(right.taskId);
|
|
108
|
+
}
|
|
109
|
+
/** Maintain the worst retained task at index zero, without allocating for the full task history. */
|
|
110
|
+
function admitToNewestWindow(window, selection) {
|
|
111
|
+
if (window.length < MAX_WORKER_TASK_VIEW_ENTRIES) {
|
|
112
|
+
window.push(selection);
|
|
113
|
+
let childIndex = window.length - 1;
|
|
114
|
+
while (childIndex > 0) {
|
|
115
|
+
const parentIndex = Math.floor((childIndex - 1) / 2);
|
|
116
|
+
if (compareTasksNewestFirst(window[childIndex].candidate, window[parentIndex].candidate) <= 0) {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
[window[parentIndex], window[childIndex]] = [
|
|
120
|
+
window[childIndex],
|
|
121
|
+
window[parentIndex],
|
|
122
|
+
];
|
|
123
|
+
childIndex = parentIndex;
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (compareTasksNewestFirst(selection.candidate, window[0].candidate) >= 0)
|
|
128
|
+
return;
|
|
129
|
+
window[0] = selection;
|
|
130
|
+
let parentIndex = 0;
|
|
131
|
+
while (true) {
|
|
132
|
+
const leftIndex = parentIndex * 2 + 1;
|
|
133
|
+
if (leftIndex >= window.length)
|
|
134
|
+
return;
|
|
135
|
+
const rightIndex = leftIndex + 1;
|
|
136
|
+
const worseChildIndex = rightIndex < window.length &&
|
|
137
|
+
compareTasksNewestFirst(window[rightIndex].candidate, window[leftIndex].candidate) > 0
|
|
138
|
+
? rightIndex
|
|
139
|
+
: leftIndex;
|
|
140
|
+
if (compareTasksNewestFirst(window[worseChildIndex].candidate, window[parentIndex].candidate) <= 0) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
[window[parentIndex], window[worseChildIndex]] = [
|
|
144
|
+
window[worseChildIndex],
|
|
145
|
+
window[parentIndex],
|
|
146
|
+
];
|
|
147
|
+
parentIndex = worseChildIndex;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const MIN_WORKER_TASK_VIEW_ENVELOPE_BYTES = Buffer.byteLength(JSON.stringify({ totalTasks: 0, omittedTaskCount: 0, tasks: [] }), "utf8");
|
|
151
|
+
function serializedSessionViewByteLength(totalTasks, taskCount, taskBytes) {
|
|
152
|
+
const envelopeBytes = Buffer.byteLength(JSON.stringify({ totalTasks, omittedTaskCount: totalTasks - taskCount, tasks: [] }), "utf8");
|
|
153
|
+
return envelopeBytes + taskBytes + Math.max(0, taskCount - 1);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Derive a provider-neutral, bounded team-task read model from the durable runtime projection.
|
|
157
|
+
* Every returned field is selected explicitly so execution authority and provider diagnostics
|
|
158
|
+
* cannot cross this read boundary through object spread.
|
|
159
|
+
*/
|
|
160
|
+
export function projectWorkerTaskSessionView(snapshot) {
|
|
161
|
+
let totalTasks = 0;
|
|
162
|
+
const selected = [];
|
|
163
|
+
for (const taskId in snapshot.tasks) {
|
|
164
|
+
if (!Object.hasOwn(snapshot.tasks, taskId))
|
|
165
|
+
continue;
|
|
166
|
+
totalTasks += 1;
|
|
167
|
+
const candidate = taskViewCandidate(taskId, snapshot.tasks[taskId]);
|
|
168
|
+
if (!candidate)
|
|
169
|
+
continue;
|
|
170
|
+
const view = projectTask(snapshot, candidate);
|
|
171
|
+
const serializedByteLength = Buffer.byteLength(JSON.stringify(view), "utf8");
|
|
172
|
+
if (serializedByteLength + MIN_WORKER_TASK_VIEW_ENVELOPE_BYTES > MAX_WORKER_TASK_VIEW_BYTES)
|
|
173
|
+
continue;
|
|
174
|
+
admitToNewestWindow(selected, { candidate, view, serializedByteLength });
|
|
175
|
+
}
|
|
176
|
+
selected.sort((left, right) => compareTasksNewestFirst(left.candidate, right.candidate));
|
|
177
|
+
const tasks = [];
|
|
178
|
+
let taskBytes = 0;
|
|
179
|
+
for (const selection of selected) {
|
|
180
|
+
const nextTaskCount = tasks.length + 1;
|
|
181
|
+
const nextTaskBytes = taskBytes + selection.serializedByteLength;
|
|
182
|
+
if (serializedSessionViewByteLength(totalTasks, nextTaskCount, nextTaskBytes) > MAX_WORKER_TASK_VIEW_BYTES) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
tasks.push(selection.view);
|
|
186
|
+
taskBytes = nextTaskBytes;
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
totalTasks,
|
|
190
|
+
omittedTaskCount: totalTasks - tasks.length,
|
|
191
|
+
tasks,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=worker-task-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-task-view.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-task-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport type { AttemptStatus, OrchestrationTaskStatus, WorkerRole } from \"../orchestration/contracts.ts\";\nimport {\n\tATTEMPT_STATUSES,\n\tMAX_ORCHESTRATION_COLLECTION_LENGTH,\n\tMAX_ORCHESTRATION_DESCRIPTION_LENGTH,\n\tMAX_ORCHESTRATION_IDENTIFIER_LENGTH,\n\tTASK_STATUSES,\n\tWORKER_ROLES,\n} from \"../orchestration/contracts.ts\";\nimport type { TaskRuntimeProjection } from \"../orchestration/task-runtime.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { DEFAULT_WORKER_FLEET_LIMITS } from \"./worker-fleet-limits.ts\";\n\n/** Keep task views within the same durable session fan-out ceiling as the worker fleet. */\nexport const MAX_WORKER_TASK_VIEW_ENTRIES = Math.min(\n\tDEFAULT_WORKER_FLEET_LIMITS.maxAgentsPerSession,\n\tDEFAULT_WORKER_FLEET_LIMITS.maxQueuedDispatches,\n);\n\n/** Bound model-facing serialization independently of task-count and field-count limits. */\nexport const MAX_WORKER_TASK_VIEW_BYTES = 64 * 1024;\n\nexport interface WorkerTaskAttemptView {\n\tagentId?: string;\n\tstatus: AttemptStatus;\n\treasonCode?: string;\n\tretry?: {\n\t\tretriesUsed: number;\n\t\tnotBefore: string;\n\t};\n}\n\nexport interface WorkerTaskView {\n\ttaskId: string;\n\ttitle: string;\n\trole: WorkerRole;\n\tstatus: OrchestrationTaskStatus;\n\tdependsOn: readonly string[];\n\tverificationOfTaskId?: string;\n\tverificationOutcome?: {\n\t\tverifierTaskId: string;\n\t\tverdict: VerificationVerdict;\n\t\treasonCode: string;\n\t};\n\tlatestAttempt?: WorkerTaskAttemptView;\n}\n\ntype VerificationVerdict = \"accepted\" | \"rejected\" | \"inconclusive\";\n\ninterface TaskViewCandidate {\n\tattemptIds: unknown;\n\tverification: unknown;\n\ttaskId: string;\n\ttitle: string;\n\trole: WorkerRole;\n\tstatus: OrchestrationTaskStatus;\n\tdependsOn: readonly string[];\n\tcreatedAt: string;\n\tverificationOfTaskId?: string;\n}\n\ninterface TaskViewSelection {\n\tcandidate: TaskViewCandidate;\n\tview: WorkerTaskView;\n\tserializedByteLength: number;\n}\n\nexport interface WorkerTaskSessionView {\n\ttotalTasks: number;\n\tomittedTaskCount: number;\n\ttasks: readonly WorkerTaskView[];\n}\n\nfunction boundedString(value: unknown, maxLength: number): string | undefined {\n\treturn typeof value === \"string\" && value.length > 0 ? value.slice(0, maxLength) : undefined;\n}\n\nfunction isListedValue<const Value extends string>(value: unknown, allowed: readonly Value[]): value is Value {\n\treturn typeof value === \"string\" && allowed.some((candidate) => candidate === value);\n}\n\nfunction boundedIdentifierArray(value: unknown): readonly string[] {\n\tif (!Array.isArray(value)) return [];\n\tconst identifiers: string[] = [];\n\tfor (let index = 0; index < Math.min(value.length, MAX_ORCHESTRATION_COLLECTION_LENGTH); index += 1) {\n\t\tconst identifier = boundedString(value[index], MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\t\tif (identifier) identifiers.push(identifier);\n\t}\n\treturn identifiers;\n}\n\nfunction taskViewCandidate(recordTaskId: string, value: unknown): TaskViewCandidate | undefined {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.task)) return undefined;\n\tif (value.task.taskId !== recordTaskId) return undefined;\n\tconst taskId = boundedString(value.task.taskId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\tconst title = boundedString(value.task.title, MAX_ORCHESTRATION_DESCRIPTION_LENGTH);\n\tconst createdAt = boundedString(value.task.createdAt, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\tif (\n\t\t!taskId ||\n\t\t!title ||\n\t\t!createdAt ||\n\t\t!isListedValue(value.task.role, WORKER_ROLES) ||\n\t\t!isListedValue(value.task.status, TASK_STATUSES)\n\t) {\n\t\treturn undefined;\n\t}\n\tconst verificationOfTaskId = boundedString(value.task.verificationOfTaskId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\treturn {\n\t\tattemptIds: value.attemptIds,\n\t\tverification: value.verification,\n\t\ttaskId,\n\t\ttitle,\n\t\trole: value.task.role,\n\t\tstatus: value.task.status,\n\t\tdependsOn: boundedIdentifierArray(value.task.dependsOn),\n\t\tcreatedAt,\n\t\t...(verificationOfTaskId ? { verificationOfTaskId } : {}),\n\t};\n}\n\nfunction latestExistingAttempt(snapshot: TaskRuntimeProjection, attemptIds: unknown): unknown {\n\tif (!Array.isArray(attemptIds)) return undefined;\n\tconst firstInspectedIndex = Math.max(0, attemptIds.length - MAX_ORCHESTRATION_COLLECTION_LENGTH);\n\tfor (let index = attemptIds.length - 1; index >= firstInspectedIndex; index -= 1) {\n\t\tconst attemptId = attemptIds[index];\n\t\tif (typeof attemptId !== \"string\" || !Object.hasOwn(snapshot.attempts, attemptId)) continue;\n\t\treturn snapshot.attempts[attemptId];\n\t}\n\treturn undefined;\n}\n\nfunction projectAttempt(value: unknown): WorkerTaskAttemptView | undefined {\n\tif (!isPlainRecord(value) || !isListedValue(value.status, ATTEMPT_STATUSES)) return undefined;\n\tconst dispatch = isPlainRecord(value.dispatch) ? value.dispatch : undefined;\n\tconst agentId =\n\t\tboundedString(value.agentId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH) ??\n\t\tboundedString(dispatch?.logicalLaneId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\tconst reasonCode = boundedString(value.reasonCode, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\tconst retry = isPlainRecord(value.retry) ? value.retry : undefined;\n\tconst retriesUsed = retry?.retriesUsed;\n\tconst notBefore = boundedString(retry?.notBefore, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\tconst validRetry = Number.isSafeInteger(retriesUsed) && Number(retriesUsed) > 0 && notBefore;\n\treturn {\n\t\t...(agentId ? { agentId } : {}),\n\t\tstatus: value.status,\n\t\t...(reasonCode ? { reasonCode } : {}),\n\t\t...(validRetry ? { retry: { retriesUsed: Number(retriesUsed), notBefore } } : {}),\n\t};\n}\n\nfunction projectVerification(value: unknown): WorkerTaskView[\"verificationOutcome\"] {\n\tif (!isPlainRecord(value) || !isListedValue(value.verdict, [\"accepted\", \"rejected\", \"inconclusive\"] as const)) {\n\t\treturn undefined;\n\t}\n\tconst verifierTaskId = boundedString(value.verifierTaskId, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\tconst reasonCode = boundedString(value.reasonCode, MAX_ORCHESTRATION_IDENTIFIER_LENGTH);\n\treturn verifierTaskId && reasonCode ? { verifierTaskId, verdict: value.verdict, reasonCode } : undefined;\n}\n\nfunction projectTask(snapshot: TaskRuntimeProjection, candidate: TaskViewCandidate): WorkerTaskView {\n\tconst attempt = projectAttempt(latestExistingAttempt(snapshot, candidate.attemptIds));\n\tconst verificationOutcome = projectVerification(candidate.verification);\n\treturn {\n\t\ttaskId: candidate.taskId,\n\t\ttitle: candidate.title,\n\t\trole: candidate.role,\n\t\tstatus: candidate.status,\n\t\tdependsOn: candidate.dependsOn,\n\t\t...(candidate.verificationOfTaskId ? { verificationOfTaskId: candidate.verificationOfTaskId } : {}),\n\t\t...(verificationOutcome ? { verificationOutcome } : {}),\n\t\t...(attempt ? { latestAttempt: attempt } : {}),\n\t};\n}\n\nfunction compareTasksNewestFirst(left: TaskViewCandidate, right: TaskViewCandidate): number {\n\treturn right.createdAt.localeCompare(left.createdAt) || left.taskId.localeCompare(right.taskId);\n}\n\n/** Maintain the worst retained task at index zero, without allocating for the full task history. */\nfunction admitToNewestWindow(window: TaskViewSelection[], selection: TaskViewSelection): void {\n\tif (window.length < MAX_WORKER_TASK_VIEW_ENTRIES) {\n\t\twindow.push(selection);\n\t\tlet childIndex = window.length - 1;\n\t\twhile (childIndex > 0) {\n\t\t\tconst parentIndex = Math.floor((childIndex - 1) / 2);\n\t\t\tif (\n\t\t\t\tcompareTasksNewestFirst(\n\t\t\t\t\t(window[childIndex] as TaskViewSelection).candidate,\n\t\t\t\t\t(window[parentIndex] as TaskViewSelection).candidate,\n\t\t\t\t) <= 0\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t[window[parentIndex], window[childIndex]] = [\n\t\t\t\twindow[childIndex] as TaskViewSelection,\n\t\t\t\twindow[parentIndex] as TaskViewSelection,\n\t\t\t];\n\t\t\tchildIndex = parentIndex;\n\t\t}\n\t\treturn;\n\t}\n\tif (compareTasksNewestFirst(selection.candidate, (window[0] as TaskViewSelection).candidate) >= 0) return;\n\n\twindow[0] = selection;\n\tlet parentIndex = 0;\n\twhile (true) {\n\t\tconst leftIndex = parentIndex * 2 + 1;\n\t\tif (leftIndex >= window.length) return;\n\t\tconst rightIndex = leftIndex + 1;\n\t\tconst worseChildIndex =\n\t\t\trightIndex < window.length &&\n\t\t\tcompareTasksNewestFirst(\n\t\t\t\t(window[rightIndex] as TaskViewSelection).candidate,\n\t\t\t\t(window[leftIndex] as TaskViewSelection).candidate,\n\t\t\t) > 0\n\t\t\t\t? rightIndex\n\t\t\t\t: leftIndex;\n\t\tif (\n\t\t\tcompareTasksNewestFirst(\n\t\t\t\t(window[worseChildIndex] as TaskViewSelection).candidate,\n\t\t\t\t(window[parentIndex] as TaskViewSelection).candidate,\n\t\t\t) <= 0\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\t[window[parentIndex], window[worseChildIndex]] = [\n\t\t\twindow[worseChildIndex] as TaskViewSelection,\n\t\t\twindow[parentIndex] as TaskViewSelection,\n\t\t];\n\t\tparentIndex = worseChildIndex;\n\t}\n}\n\nconst MIN_WORKER_TASK_VIEW_ENVELOPE_BYTES = Buffer.byteLength(\n\tJSON.stringify({ totalTasks: 0, omittedTaskCount: 0, tasks: [] }),\n\t\"utf8\",\n);\n\nfunction serializedSessionViewByteLength(totalTasks: number, taskCount: number, taskBytes: number): number {\n\tconst envelopeBytes = Buffer.byteLength(\n\t\tJSON.stringify({ totalTasks, omittedTaskCount: totalTasks - taskCount, tasks: [] }),\n\t\t\"utf8\",\n\t);\n\treturn envelopeBytes + taskBytes + Math.max(0, taskCount - 1);\n}\n\n/**\n * Derive a provider-neutral, bounded team-task read model from the durable runtime projection.\n * Every returned field is selected explicitly so execution authority and provider diagnostics\n * cannot cross this read boundary through object spread.\n */\nexport function projectWorkerTaskSessionView(snapshot: TaskRuntimeProjection): WorkerTaskSessionView {\n\tlet totalTasks = 0;\n\tconst selected: TaskViewSelection[] = [];\n\tfor (const taskId in snapshot.tasks) {\n\t\tif (!Object.hasOwn(snapshot.tasks, taskId)) continue;\n\t\ttotalTasks += 1;\n\t\tconst candidate = taskViewCandidate(taskId, snapshot.tasks[taskId]);\n\t\tif (!candidate) continue;\n\t\tconst view = projectTask(snapshot, candidate);\n\t\tconst serializedByteLength = Buffer.byteLength(JSON.stringify(view), \"utf8\");\n\t\tif (serializedByteLength + MIN_WORKER_TASK_VIEW_ENVELOPE_BYTES > MAX_WORKER_TASK_VIEW_BYTES) continue;\n\t\tadmitToNewestWindow(selected, { candidate, view, serializedByteLength });\n\t}\n\tselected.sort((left, right) => compareTasksNewestFirst(left.candidate, right.candidate));\n\tconst tasks: WorkerTaskView[] = [];\n\tlet taskBytes = 0;\n\tfor (const selection of selected) {\n\t\tconst nextTaskCount = tasks.length + 1;\n\t\tconst nextTaskBytes = taskBytes + selection.serializedByteLength;\n\t\tif (serializedSessionViewByteLength(totalTasks, nextTaskCount, nextTaskBytes) > MAX_WORKER_TASK_VIEW_BYTES) {\n\t\t\tcontinue;\n\t\t}\n\t\ttasks.push(selection.view);\n\t\ttaskBytes = nextTaskBytes;\n\t}\n\treturn {\n\t\ttotalTasks,\n\t\tomittedTaskCount: totalTasks - tasks.length,\n\t\ttasks,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type LaneRecord } from "../autonomy/lane-tracker.ts";
|
|
2
|
+
export interface WorkerTerminalHandoff {
|
|
3
|
+
terminalAttemptId: string;
|
|
4
|
+
parentAgentId: string;
|
|
5
|
+
childAgentId: string;
|
|
6
|
+
record: LaneRecord;
|
|
7
|
+
}
|
|
8
|
+
/** Per-entry ceiling; combined with the durable attempt cap this bounds retained memory to 4 MiB. */
|
|
9
|
+
export declare const MAX_WORKER_TERMINAL_HANDOFF_BYTES: number;
|
|
10
|
+
export type WorkerTerminalHandoffDelivery = "delivered" | "retained";
|
|
11
|
+
export type WorkerTerminalHandoffRetention = "retained" | "replay";
|
|
12
|
+
export interface WorkerTerminalHandoffDrainResult {
|
|
13
|
+
attempted: number;
|
|
14
|
+
delivered: number;
|
|
15
|
+
errors: number;
|
|
16
|
+
retained: number;
|
|
17
|
+
}
|
|
18
|
+
export interface WorkerTerminalHandoffRehydrationResult {
|
|
19
|
+
added: number;
|
|
20
|
+
replayed: number;
|
|
21
|
+
drain: WorkerTerminalHandoffDrainResult;
|
|
22
|
+
}
|
|
23
|
+
export interface WorkerTerminalHandoffCoordinatorOptions {
|
|
24
|
+
deliver(handoff: Readonly<WorkerTerminalHandoff>): WorkerTerminalHandoffDelivery;
|
|
25
|
+
onDeliveryError?(handoff: Readonly<WorkerTerminalHandoff>, error: unknown): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Bounded process-local outbox for parent terminal handoffs. Delivery is driven only by explicit
|
|
29
|
+
* terminal, capacity, state-change, and recovery calls; this owner never creates a timer or poll.
|
|
30
|
+
*/
|
|
31
|
+
export declare class WorkerTerminalHandoffCoordinator {
|
|
32
|
+
private readonly options;
|
|
33
|
+
private readonly pending;
|
|
34
|
+
private draining;
|
|
35
|
+
private redrainRequested;
|
|
36
|
+
private disposed;
|
|
37
|
+
constructor(options: WorkerTerminalHandoffCoordinatorOptions);
|
|
38
|
+
get retainedCount(): number;
|
|
39
|
+
retained(): WorkerTerminalHandoff[];
|
|
40
|
+
/** Retain one terminal handoff snapshot. Structurally identical pending replays are inert; conflicts fail closed. */
|
|
41
|
+
retain(handoff: WorkerTerminalHandoff): WorkerTerminalHandoffRetention;
|
|
42
|
+
/**
|
|
43
|
+
* Atomically adopt a durable recovery snapshot, then make one explicit delivery pass. Repeated
|
|
44
|
+
* attempt IDs in the recovery snapshot must carry structurally equivalent data.
|
|
45
|
+
*/
|
|
46
|
+
rehydrate(handoffs: readonly WorkerTerminalHandoff[]): WorkerTerminalHandoffRehydrationResult;
|
|
47
|
+
/** Explicit terminal/capacity/state-change event. Reentrant signals coalesce into a redrain. */
|
|
48
|
+
signal(): WorkerTerminalHandoffDrainResult;
|
|
49
|
+
clear(): void;
|
|
50
|
+
dispose(): void;
|
|
51
|
+
private assertActive;
|
|
52
|
+
private assertCapacity;
|
|
53
|
+
private reportDeliveryError;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=worker-terminal-handoff-coordinator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-terminal-handoff-coordinator.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-terminal-handoff-coordinator.ts"],"names":[],"mappings":"AACA,OAAO,EAAsC,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGlG,MAAM,WAAW,qBAAqB;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACnB;AAED,qGAAqG;AACrG,eAAO,MAAM,iCAAiC,QAAY,CAAC;AAE3D,MAAM,MAAM,6BAA6B,GAAG,WAAW,GAAG,UAAU,CAAC;AACrE,MAAM,MAAM,8BAA8B,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,gCAAgC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sCAAsC;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gCAAgC,CAAC;CACxC;AAED,MAAM,WAAW,uCAAuC;IACvD,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GAAG,6BAA6B,CAAC;IACjF,eAAe,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACjF;AAkCD;;;GAGG;AACH,qBAAa,gCAAgC;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4C;IACpE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,OAAO,EAAE,uCAAuC,EAE3D;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,QAAQ,IAAI,qBAAqB,EAAE,CAElC;IAED,qHAAqH;IACrH,MAAM,CAAC,OAAO,EAAE,qBAAqB,GAAG,8BAA8B,CAcrE;IAED;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,SAAS,qBAAqB,EAAE,GAAG,sCAAsC,CAuB5F;IAED,gGAAgG;IAChG,MAAM,IAAI,gCAAgC,CAoDzC;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,OAAO,IAAI,IAAI,CAGd;IAED,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,mBAAmB;CAO3B"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { isLaneRecord, isLaneTerminalStatus } from "../autonomy/lane-tracker.js";
|
|
3
|
+
import { MAX_ORCHESTRATION_ATTEMPTS, MAX_ORCHESTRATION_IDENTIFIER_LENGTH } from "../orchestration/contracts.js";
|
|
4
|
+
/** Per-entry ceiling; combined with the durable attempt cap this bounds retained memory to 4 MiB. */
|
|
5
|
+
export const MAX_WORKER_TERMINAL_HANDOFF_BYTES = 16 * 1024;
|
|
6
|
+
function assertIdentifier(value, label) {
|
|
7
|
+
if (typeof value !== "string" ||
|
|
8
|
+
!value ||
|
|
9
|
+
value.trim() !== value ||
|
|
10
|
+
value.length > MAX_ORCHESTRATION_IDENTIFIER_LENGTH) {
|
|
11
|
+
throw new TypeError(`${label} must be a bounded non-empty canonical string.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function snapshotHandoff(handoff) {
|
|
15
|
+
assertIdentifier(handoff.terminalAttemptId, "Worker terminal attempt ID");
|
|
16
|
+
assertIdentifier(handoff.parentAgentId, "Worker terminal parent agent ID");
|
|
17
|
+
assertIdentifier(handoff.childAgentId, "Worker terminal child agent ID");
|
|
18
|
+
if (!isLaneRecord(handoff.record)) {
|
|
19
|
+
throw new TypeError("Worker terminal handoff requires a valid lane record.");
|
|
20
|
+
}
|
|
21
|
+
if (!isLaneTerminalStatus(handoff.record.status)) {
|
|
22
|
+
throw new TypeError("Worker terminal handoff requires a terminal lane record.");
|
|
23
|
+
}
|
|
24
|
+
const snapshot = { ...handoff, record: { ...handoff.record } };
|
|
25
|
+
if (Buffer.byteLength(JSON.stringify(snapshot), "utf-8") > MAX_WORKER_TERMINAL_HANDOFF_BYTES) {
|
|
26
|
+
throw new TypeError(`Worker terminal handoff exceeds its ${MAX_WORKER_TERMINAL_HANDOFF_BYTES} byte limit.`);
|
|
27
|
+
}
|
|
28
|
+
return snapshot;
|
|
29
|
+
}
|
|
30
|
+
function cloneHandoff(handoff) {
|
|
31
|
+
return { ...handoff, record: { ...handoff.record } };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Bounded process-local outbox for parent terminal handoffs. Delivery is driven only by explicit
|
|
35
|
+
* terminal, capacity, state-change, and recovery calls; this owner never creates a timer or poll.
|
|
36
|
+
*/
|
|
37
|
+
export class WorkerTerminalHandoffCoordinator {
|
|
38
|
+
constructor(options) {
|
|
39
|
+
this.pending = new Map();
|
|
40
|
+
this.draining = false;
|
|
41
|
+
this.redrainRequested = false;
|
|
42
|
+
this.disposed = false;
|
|
43
|
+
this.options = options;
|
|
44
|
+
}
|
|
45
|
+
get retainedCount() {
|
|
46
|
+
return this.pending.size;
|
|
47
|
+
}
|
|
48
|
+
retained() {
|
|
49
|
+
return [...this.pending.values()].map(cloneHandoff);
|
|
50
|
+
}
|
|
51
|
+
/** Retain one terminal handoff snapshot. Structurally identical pending replays are inert; conflicts fail closed. */
|
|
52
|
+
retain(handoff) {
|
|
53
|
+
this.assertActive();
|
|
54
|
+
const snapshot = snapshotHandoff(handoff);
|
|
55
|
+
const existing = this.pending.get(snapshot.terminalAttemptId);
|
|
56
|
+
if (existing) {
|
|
57
|
+
if (isDeepStrictEqual(existing, snapshot))
|
|
58
|
+
return "replay";
|
|
59
|
+
throw new Error(`Worker terminal handoff attempt ${snapshot.terminalAttemptId} conflicts with its retained payload.`);
|
|
60
|
+
}
|
|
61
|
+
this.assertCapacity(this.pending.size + 1);
|
|
62
|
+
this.pending.set(snapshot.terminalAttemptId, snapshot);
|
|
63
|
+
if (this.draining)
|
|
64
|
+
this.redrainRequested = true;
|
|
65
|
+
return "retained";
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Atomically adopt a durable recovery snapshot, then make one explicit delivery pass. Repeated
|
|
69
|
+
* attempt IDs in the recovery snapshot must carry structurally equivalent data.
|
|
70
|
+
*/
|
|
71
|
+
rehydrate(handoffs) {
|
|
72
|
+
this.assertActive();
|
|
73
|
+
const staged = new Map(this.pending);
|
|
74
|
+
const additions = [];
|
|
75
|
+
let replayed = 0;
|
|
76
|
+
for (const handoff of handoffs) {
|
|
77
|
+
const snapshot = snapshotHandoff(handoff);
|
|
78
|
+
const existing = staged.get(snapshot.terminalAttemptId);
|
|
79
|
+
if (existing) {
|
|
80
|
+
if (!isDeepStrictEqual(existing, snapshot)) {
|
|
81
|
+
throw new Error(`Worker terminal handoff attempt ${snapshot.terminalAttemptId} conflicts with its retained payload.`);
|
|
82
|
+
}
|
|
83
|
+
replayed += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
this.assertCapacity(staged.size + 1);
|
|
87
|
+
staged.set(snapshot.terminalAttemptId, snapshot);
|
|
88
|
+
additions.push(snapshot);
|
|
89
|
+
}
|
|
90
|
+
for (const handoff of additions)
|
|
91
|
+
this.pending.set(handoff.terminalAttemptId, handoff);
|
|
92
|
+
return { added: additions.length, replayed, drain: this.signal() };
|
|
93
|
+
}
|
|
94
|
+
/** Explicit terminal/capacity/state-change event. Reentrant signals coalesce into a redrain. */
|
|
95
|
+
signal() {
|
|
96
|
+
if (this.disposed)
|
|
97
|
+
return { attempted: 0, delivered: 0, errors: 0, retained: 0 };
|
|
98
|
+
if (this.draining) {
|
|
99
|
+
this.redrainRequested = true;
|
|
100
|
+
return { attempted: 0, delivered: 0, errors: 0, retained: this.pending.size };
|
|
101
|
+
}
|
|
102
|
+
let attempted = 0;
|
|
103
|
+
let delivered = 0;
|
|
104
|
+
let errors = 0;
|
|
105
|
+
let stagnantPasses = 0;
|
|
106
|
+
this.draining = true;
|
|
107
|
+
this.redrainRequested = true;
|
|
108
|
+
try {
|
|
109
|
+
while (this.redrainRequested && !this.disposed) {
|
|
110
|
+
this.redrainRequested = false;
|
|
111
|
+
const batch = [...this.pending.values()];
|
|
112
|
+
let passChanged = false;
|
|
113
|
+
for (const retained of batch) {
|
|
114
|
+
if (this.disposed || this.pending.get(retained.terminalAttemptId) !== retained)
|
|
115
|
+
continue;
|
|
116
|
+
attempted += 1;
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = this.options.deliver(cloneHandoff(retained));
|
|
120
|
+
if (result !== "delivered" && result !== "retained") {
|
|
121
|
+
throw new TypeError("Worker terminal handoff delivery returned an invalid result.");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
errors += 1;
|
|
126
|
+
this.reportDeliveryError(retained, error);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (result !== "delivered" || this.pending.get(retained.terminalAttemptId) !== retained)
|
|
130
|
+
continue;
|
|
131
|
+
this.pending.delete(retained.terminalAttemptId);
|
|
132
|
+
delivered += 1;
|
|
133
|
+
passChanged = true;
|
|
134
|
+
}
|
|
135
|
+
if (passChanged || batch.some((entry) => this.pending.get(entry.terminalAttemptId) !== entry)) {
|
|
136
|
+
stagnantPasses = 0;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
stagnantPasses += 1;
|
|
140
|
+
}
|
|
141
|
+
// A self-signalling retained callback cannot spin forever. One unchanged redrain is enough
|
|
142
|
+
// to observe every synchronous state mutation that preceded the nested signal.
|
|
143
|
+
if (this.redrainRequested && stagnantPasses >= 2)
|
|
144
|
+
this.redrainRequested = false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
this.draining = false;
|
|
149
|
+
this.redrainRequested = false;
|
|
150
|
+
}
|
|
151
|
+
return { attempted, delivered, errors, retained: this.pending.size };
|
|
152
|
+
}
|
|
153
|
+
clear() {
|
|
154
|
+
this.pending.clear();
|
|
155
|
+
this.redrainRequested = false;
|
|
156
|
+
}
|
|
157
|
+
dispose() {
|
|
158
|
+
this.disposed = true;
|
|
159
|
+
this.clear();
|
|
160
|
+
}
|
|
161
|
+
assertActive() {
|
|
162
|
+
if (this.disposed)
|
|
163
|
+
throw new Error("Worker terminal handoff coordinator is disposed.");
|
|
164
|
+
}
|
|
165
|
+
assertCapacity(size) {
|
|
166
|
+
if (size <= MAX_ORCHESTRATION_ATTEMPTS)
|
|
167
|
+
return;
|
|
168
|
+
throw new Error(`Worker terminal handoff retention reached its ${MAX_ORCHESTRATION_ATTEMPTS} attempt limit.`);
|
|
169
|
+
}
|
|
170
|
+
reportDeliveryError(handoff, error) {
|
|
171
|
+
try {
|
|
172
|
+
this.options.onDeliveryError?.(cloneHandoff(handoff), error);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Diagnostics cannot consume or unblock a retained terminal handoff.
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=worker-terminal-handoff-coordinator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-terminal-handoff-coordinator.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-terminal-handoff-coordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { isLaneRecord, isLaneTerminalStatus, type LaneRecord } from \"../autonomy/lane-tracker.ts\";\nimport { MAX_ORCHESTRATION_ATTEMPTS, MAX_ORCHESTRATION_IDENTIFIER_LENGTH } from \"../orchestration/contracts.ts\";\n\nexport interface WorkerTerminalHandoff {\n\tterminalAttemptId: string;\n\tparentAgentId: string;\n\tchildAgentId: string;\n\trecord: LaneRecord;\n}\n\n/** Per-entry ceiling; combined with the durable attempt cap this bounds retained memory to 4 MiB. */\nexport const MAX_WORKER_TERMINAL_HANDOFF_BYTES = 16 * 1024;\n\nexport type WorkerTerminalHandoffDelivery = \"delivered\" | \"retained\";\nexport type WorkerTerminalHandoffRetention = \"retained\" | \"replay\";\n\nexport interface WorkerTerminalHandoffDrainResult {\n\tattempted: number;\n\tdelivered: number;\n\terrors: number;\n\tretained: number;\n}\n\nexport interface WorkerTerminalHandoffRehydrationResult {\n\tadded: number;\n\treplayed: number;\n\tdrain: WorkerTerminalHandoffDrainResult;\n}\n\nexport interface WorkerTerminalHandoffCoordinatorOptions {\n\tdeliver(handoff: Readonly<WorkerTerminalHandoff>): WorkerTerminalHandoffDelivery;\n\tonDeliveryError?(handoff: Readonly<WorkerTerminalHandoff>, error: unknown): void;\n}\n\nfunction assertIdentifier(value: unknown, label: string): asserts value is string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t!value ||\n\t\tvalue.trim() !== value ||\n\t\tvalue.length > MAX_ORCHESTRATION_IDENTIFIER_LENGTH\n\t) {\n\t\tthrow new TypeError(`${label} must be a bounded non-empty canonical string.`);\n\t}\n}\n\nfunction snapshotHandoff(handoff: WorkerTerminalHandoff): WorkerTerminalHandoff {\n\tassertIdentifier(handoff.terminalAttemptId, \"Worker terminal attempt ID\");\n\tassertIdentifier(handoff.parentAgentId, \"Worker terminal parent agent ID\");\n\tassertIdentifier(handoff.childAgentId, \"Worker terminal child agent ID\");\n\tif (!isLaneRecord(handoff.record)) {\n\t\tthrow new TypeError(\"Worker terminal handoff requires a valid lane record.\");\n\t}\n\tif (!isLaneTerminalStatus(handoff.record.status)) {\n\t\tthrow new TypeError(\"Worker terminal handoff requires a terminal lane record.\");\n\t}\n\tconst snapshot = { ...handoff, record: { ...handoff.record } };\n\tif (Buffer.byteLength(JSON.stringify(snapshot), \"utf-8\") > MAX_WORKER_TERMINAL_HANDOFF_BYTES) {\n\t\tthrow new TypeError(`Worker terminal handoff exceeds its ${MAX_WORKER_TERMINAL_HANDOFF_BYTES} byte limit.`);\n\t}\n\treturn snapshot;\n}\n\nfunction cloneHandoff(handoff: WorkerTerminalHandoff): WorkerTerminalHandoff {\n\treturn { ...handoff, record: { ...handoff.record } };\n}\n\n/**\n * Bounded process-local outbox for parent terminal handoffs. Delivery is driven only by explicit\n * terminal, capacity, state-change, and recovery calls; this owner never creates a timer or poll.\n */\nexport class WorkerTerminalHandoffCoordinator {\n\tprivate readonly options: WorkerTerminalHandoffCoordinatorOptions;\n\tprivate readonly pending = new Map<string, WorkerTerminalHandoff>();\n\tprivate draining = false;\n\tprivate redrainRequested = false;\n\tprivate disposed = false;\n\n\tconstructor(options: WorkerTerminalHandoffCoordinatorOptions) {\n\t\tthis.options = options;\n\t}\n\n\tget retainedCount(): number {\n\t\treturn this.pending.size;\n\t}\n\n\tretained(): WorkerTerminalHandoff[] {\n\t\treturn [...this.pending.values()].map(cloneHandoff);\n\t}\n\n\t/** Retain one terminal handoff snapshot. Structurally identical pending replays are inert; conflicts fail closed. */\n\tretain(handoff: WorkerTerminalHandoff): WorkerTerminalHandoffRetention {\n\t\tthis.assertActive();\n\t\tconst snapshot = snapshotHandoff(handoff);\n\t\tconst existing = this.pending.get(snapshot.terminalAttemptId);\n\t\tif (existing) {\n\t\t\tif (isDeepStrictEqual(existing, snapshot)) return \"replay\";\n\t\t\tthrow new Error(\n\t\t\t\t`Worker terminal handoff attempt ${snapshot.terminalAttemptId} conflicts with its retained payload.`,\n\t\t\t);\n\t\t}\n\t\tthis.assertCapacity(this.pending.size + 1);\n\t\tthis.pending.set(snapshot.terminalAttemptId, snapshot);\n\t\tif (this.draining) this.redrainRequested = true;\n\t\treturn \"retained\";\n\t}\n\n\t/**\n\t * Atomically adopt a durable recovery snapshot, then make one explicit delivery pass. Repeated\n\t * attempt IDs in the recovery snapshot must carry structurally equivalent data.\n\t */\n\trehydrate(handoffs: readonly WorkerTerminalHandoff[]): WorkerTerminalHandoffRehydrationResult {\n\t\tthis.assertActive();\n\t\tconst staged = new Map(this.pending);\n\t\tconst additions: WorkerTerminalHandoff[] = [];\n\t\tlet replayed = 0;\n\t\tfor (const handoff of handoffs) {\n\t\t\tconst snapshot = snapshotHandoff(handoff);\n\t\t\tconst existing = staged.get(snapshot.terminalAttemptId);\n\t\t\tif (existing) {\n\t\t\t\tif (!isDeepStrictEqual(existing, snapshot)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Worker terminal handoff attempt ${snapshot.terminalAttemptId} conflicts with its retained payload.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treplayed += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.assertCapacity(staged.size + 1);\n\t\t\tstaged.set(snapshot.terminalAttemptId, snapshot);\n\t\t\tadditions.push(snapshot);\n\t\t}\n\t\tfor (const handoff of additions) this.pending.set(handoff.terminalAttemptId, handoff);\n\t\treturn { added: additions.length, replayed, drain: this.signal() };\n\t}\n\n\t/** Explicit terminal/capacity/state-change event. Reentrant signals coalesce into a redrain. */\n\tsignal(): WorkerTerminalHandoffDrainResult {\n\t\tif (this.disposed) return { attempted: 0, delivered: 0, errors: 0, retained: 0 };\n\t\tif (this.draining) {\n\t\t\tthis.redrainRequested = true;\n\t\t\treturn { attempted: 0, delivered: 0, errors: 0, retained: this.pending.size };\n\t\t}\n\n\t\tlet attempted = 0;\n\t\tlet delivered = 0;\n\t\tlet errors = 0;\n\t\tlet stagnantPasses = 0;\n\t\tthis.draining = true;\n\t\tthis.redrainRequested = true;\n\t\ttry {\n\t\t\twhile (this.redrainRequested && !this.disposed) {\n\t\t\t\tthis.redrainRequested = false;\n\t\t\t\tconst batch = [...this.pending.values()];\n\t\t\t\tlet passChanged = false;\n\t\t\t\tfor (const retained of batch) {\n\t\t\t\t\tif (this.disposed || this.pending.get(retained.terminalAttemptId) !== retained) continue;\n\t\t\t\t\tattempted += 1;\n\t\t\t\t\tlet result: WorkerTerminalHandoffDelivery;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = this.options.deliver(cloneHandoff(retained));\n\t\t\t\t\t\tif (result !== \"delivered\" && result !== \"retained\") {\n\t\t\t\t\t\t\tthrow new TypeError(\"Worker terminal handoff delivery returned an invalid result.\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors += 1;\n\t\t\t\t\t\tthis.reportDeliveryError(retained, error);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (result !== \"delivered\" || this.pending.get(retained.terminalAttemptId) !== retained) continue;\n\t\t\t\t\tthis.pending.delete(retained.terminalAttemptId);\n\t\t\t\t\tdelivered += 1;\n\t\t\t\t\tpassChanged = true;\n\t\t\t\t}\n\n\t\t\t\tif (passChanged || batch.some((entry) => this.pending.get(entry.terminalAttemptId) !== entry)) {\n\t\t\t\t\tstagnantPasses = 0;\n\t\t\t\t} else {\n\t\t\t\t\tstagnantPasses += 1;\n\t\t\t\t}\n\t\t\t\t// A self-signalling retained callback cannot spin forever. One unchanged redrain is enough\n\t\t\t\t// to observe every synchronous state mutation that preceded the nested signal.\n\t\t\t\tif (this.redrainRequested && stagnantPasses >= 2) this.redrainRequested = false;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.draining = false;\n\t\t\tthis.redrainRequested = false;\n\t\t}\n\t\treturn { attempted, delivered, errors, retained: this.pending.size };\n\t}\n\n\tclear(): void {\n\t\tthis.pending.clear();\n\t\tthis.redrainRequested = false;\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tthis.clear();\n\t}\n\n\tprivate assertActive(): void {\n\t\tif (this.disposed) throw new Error(\"Worker terminal handoff coordinator is disposed.\");\n\t}\n\n\tprivate assertCapacity(size: number): void {\n\t\tif (size <= MAX_ORCHESTRATION_ATTEMPTS) return;\n\t\tthrow new Error(`Worker terminal handoff retention reached its ${MAX_ORCHESTRATION_ATTEMPTS} attempt limit.`);\n\t}\n\n\tprivate reportDeliveryError(handoff: WorkerTerminalHandoff, error: unknown): void {\n\t\ttry {\n\t\t\tthis.options.onDeliveryError?.(cloneHandoff(handoff), error);\n\t\t} catch {\n\t\t\t// Diagnostics cannot consume or unblock a retained terminal handoff.\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SharedCapabilityBudget } from "../orchestration/capability-gateway.ts";
|
|
2
2
|
import type { AttemptUsageSnapshot, RiskBudget } from "../orchestration/contracts.ts";
|
|
3
3
|
export declare class WorkerTreeBudgetExceededError extends Error {
|
|
4
4
|
readonly field: "maxTokens" | "maxCostUsd" | "maxToolCalls" | "maxWallClockMs" | "maxAttempts";
|
|
@@ -40,7 +40,7 @@ export declare class WorkerTreeBudgetCoordinator {
|
|
|
40
40
|
private reserveProviderBudget;
|
|
41
41
|
private drainWaiters;
|
|
42
42
|
private detachAbort;
|
|
43
|
-
private
|
|
43
|
+
private budgetedTokens;
|
|
44
44
|
private reservedTokens;
|
|
45
45
|
private assertAvailable;
|
|
46
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-tree-budget-coordinator.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-tree-budget-coordinator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker-tree-budget-coordinator.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-tree-budget-coordinator.ts"],"names":[],"mappings":"AAKA,OAAO,EAIN,KAAK,sBAAsB,EAC3B,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGtF,qBAAa,6BAA8B,SAAQ,KAAK;IACvD,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,gBAAgB,GAAG,aAAa,CAAC;IAE/F,YAAY,KAAK,EAAE,6BAA6B,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,EAIzE;CACD;AAmBD,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,oBAAoB,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B;IAC1C,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC1D,QAAQ,EAAE,QAAQ,CACjB,MAAM,CACL,MAAM,EACN;QACC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE;YAAE,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACrC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;KACjC,CACD,CACD,CAAC;IACF,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAC,CAAC,CAAC;CACxE;AAED,2FAA2F;AAC3F,wBAAgB,4BAA4B,CAC3C,QAAQ,EAAE,0BAA0B,EACpC,WAAW,EAAE,MAAM,GACjB,oBAAoB,EAAE,CAYxB;AAMD,oGAAoG;AACpG,qBAAa,2BAA2B;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsC;IAE5D,UAAU,CAAC,IAAI,EAAE;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,UAAU,CAAC;QACnB,KAAK,EAAE,SAAS,oBAAoB,EAAE,CAAC;QACvC,YAAY,EAAE,oBAAoB,CAAC;KACnC,GAAG,sBAAsB,CA+BzB;IAED,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,YAAY;IA4CpB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,eAAe;CA2BvB"}
|