@caupulican/pi-adaptative 0.90.2 → 0.90.4
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 +35 -0
- package/dist/core/agent-session-contracts.d.ts +2 -0
- package/dist/core/agent-session-contracts.d.ts.map +1 -1
- package/dist/core/agent-session-contracts.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +10 -1
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +24 -2
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +9 -1
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +1 -3
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/compaction-controller.d.ts.map +1 -1
- package/dist/core/compaction-controller.js +0 -1
- package/dist/core/compaction-controller.js.map +1 -1
- package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -1
- package/dist/core/delegation/managed-lane-controller.js +5 -1
- package/dist/core/delegation/managed-lane-controller.js.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +8 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +94 -36
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-agent-control.d.ts +1 -0
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +7 -2
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.js +26 -3
- package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts +7 -4
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +110 -27
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.d.ts +9 -2
- package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.js +39 -3
- package/dist/core/delegation/worker-fleet-limits.js.map +1 -1
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -1
- package/dist/core/delegation/worker-lane-projection.js +4 -0
- package/dist/core/delegation/worker-lane-projection.js.map +1 -1
- package/dist/core/delegation/worker-lease-heartbeat.d.ts +16 -0
- package/dist/core/delegation/worker-lease-heartbeat.d.ts.map +1 -0
- package/dist/core/delegation/worker-lease-heartbeat.js +37 -0
- package/dist/core/delegation/worker-lease-heartbeat.js.map +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +6 -0
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.js +2 -1
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-runner.js +2 -2
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +4 -2
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-tree-budget-coordinator.js +10 -2
- package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +14 -0
- package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-write-reservation-coordinator.js +121 -33
- package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -1
- package/dist/core/foreground-terminal-handoff-controller.d.ts +5 -0
- package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -1
- package/dist/core/foreground-terminal-handoff-controller.js +28 -12
- package/dist/core/foreground-terminal-handoff-controller.js.map +1 -1
- package/dist/core/orchestration/capability-gateway.d.ts +2 -1
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
- package/dist/core/orchestration/capability-gateway.js +9 -2
- package/dist/core/orchestration/capability-gateway.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +16 -1
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +5 -0
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
- package/dist/core/orchestration/profile-registry.js +38 -1
- package/dist/core/orchestration/profile-registry.js.map +1 -1
- package/dist/core/orchestration/risk-budget.d.ts +10 -0
- package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
- package/dist/core/orchestration/risk-budget.js +14 -0
- package/dist/core/orchestration/risk-budget.js.map +1 -1
- package/dist/core/orchestration/task-runtime-reducer.d.ts +1 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime-reducer.js +34 -0
- package/dist/core/orchestration/task-runtime-reducer.js.map +1 -1
- package/dist/core/orchestration/task-runtime.d.ts +1 -0
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime.js +24 -1
- package/dist/core/orchestration/task-runtime.js.map +1 -1
- package/dist/core/provider-prompt-contracts.d.ts.map +1 -1
- package/dist/core/provider-prompt-contracts.js +2 -1
- package/dist/core/provider-prompt-contracts.js.map +1 -1
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +8 -1
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +2 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts +3 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +13 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +1 -0
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +19 -1
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +9 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +198 -64
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +14 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-failure-recovery.d.ts +3 -0
- package/dist/core/tools/file-failure-recovery.d.ts.map +1 -1
- package/dist/core/tools/file-failure-recovery.js +11 -0
- package/dist/core/tools/file-failure-recovery.js.map +1 -1
- package/dist/core/tools/orchestration-panel.d.ts +1 -1
- package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
- package/dist/core/tools/orchestration-panel.js +1 -0
- package/dist/core/tools/orchestration-panel.js.map +1 -1
- package/dist/core/tools/profile-writer.d.ts.map +1 -1
- package/dist/core/tools/profile-writer.js +2 -8
- package/dist/core/tools/profile-writer.js.map +1 -1
- package/dist/core/tools/search-command-guard.d.ts.map +1 -1
- package/dist/core/tools/search-command-guard.js +10 -3
- package/dist/core/tools/search-command-guard.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +10 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -1
- package/dist/modes/interactive/components/activity-lane.js +11 -2
- package/dist/modes/interactive/components/activity-lane.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/utils/shell.d.ts +2 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +3 -1
- package/dist/utils/shell.js.map +1 -1
- package/docs/harness-architecture.md +6 -3
- package/docs/settings.md +8 -6
- package/docs/tool-repair.md +1 -1
- package/docs/worker-profiles.md +2 -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,5 +1,5 @@
|
|
|
1
1
|
import { isLocalProcessAlive, localWorkerProcessOwnerLiveness } from "./worker-process-owner.js";
|
|
2
|
-
import { WorkerWriteReservationStore } from "./worker-write-reservation.js";
|
|
2
|
+
import { WorkerWriteReservationStore, } from "./worker-write-reservation.js";
|
|
3
3
|
/**
|
|
4
4
|
* Owns one controller process's write-reservation lifecycle: admission, exact-fence release,
|
|
5
5
|
* event-driven queue wakeup, and fail-closed recovery of positively dead owners.
|
|
@@ -7,54 +7,92 @@ import { WorkerWriteReservationStore } from "./worker-write-reservation.js";
|
|
|
7
7
|
export class WorkerWriteReservationCoordinator {
|
|
8
8
|
constructor(options) {
|
|
9
9
|
this.leases = new Map();
|
|
10
|
+
this.blockedByLocalLaneIds = new Map();
|
|
10
11
|
this.options = options;
|
|
11
12
|
this.store = new WorkerWriteReservationStore({ agentDir: options.agentDir });
|
|
12
13
|
}
|
|
13
14
|
acquire(laneId, attempt, plan) {
|
|
14
|
-
if (!plan.writeEnabled || plan.writePaths.length === 0)
|
|
15
|
+
if (!plan.writeEnabled || plan.writePaths.length === 0) {
|
|
16
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
15
17
|
return { kind: "granted" };
|
|
18
|
+
}
|
|
16
19
|
const fencingToken = (attempt.lease?.fencingToken ?? 0) + 1;
|
|
17
20
|
const current = this.leases.get(laneId);
|
|
18
21
|
if (current && current.attemptId === attempt.attemptId && current.fencingToken === fencingToken) {
|
|
22
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
19
23
|
return { kind: "granted" };
|
|
20
24
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
if (result.kind !== "granted" || !result.lease) {
|
|
33
|
-
this.ensureWatch();
|
|
34
|
-
return { kind: "blocked" };
|
|
35
|
-
}
|
|
36
|
-
this.leases.set(laneId, result.lease);
|
|
37
|
-
return { kind: "granted" };
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
-
this.options.warn(`Worker write reservation denied: ${message}`);
|
|
42
|
-
return {
|
|
43
|
-
kind: "denied",
|
|
44
|
-
reasonCode: message.includes("within the execution root")
|
|
45
|
-
? "write_reservation_scope_invalid"
|
|
46
|
-
: "write_reservation_unavailable",
|
|
47
|
-
};
|
|
48
|
-
}
|
|
25
|
+
return this.acquireLease(laneId, {
|
|
26
|
+
parentSessionId: this.options.getParentSessionId(),
|
|
27
|
+
ownerId: this.options.ownerId,
|
|
28
|
+
taskId: laneId,
|
|
29
|
+
attemptId: attempt.attemptId,
|
|
30
|
+
fencingToken,
|
|
31
|
+
access: "write",
|
|
32
|
+
workspace: this.workspace(),
|
|
33
|
+
writeScopes: plan.writePaths,
|
|
34
|
+
}, "Worker write reservation denied", true);
|
|
49
35
|
}
|
|
50
36
|
hasFenceMismatch(laneId, attemptId, fencingToken) {
|
|
51
37
|
const lease = this.leases.get(laneId);
|
|
52
38
|
return lease !== undefined && (lease.attemptId !== attemptId || lease.fencingToken !== fencingToken);
|
|
53
39
|
}
|
|
54
|
-
|
|
40
|
+
isBlockedBy(targetLaneId, blockerLaneId) {
|
|
41
|
+
return this.blockedByLocalLaneIds.get(targetLaneId)?.has(blockerLaneId) === true;
|
|
42
|
+
}
|
|
43
|
+
/** Release one exact live caller lane while its model turn is blocked inside a worker wait. */
|
|
44
|
+
yieldForWait(laneId, expectedAttemptId, expectedFencingToken) {
|
|
55
45
|
const held = this.leases.get(laneId);
|
|
56
46
|
if (!held)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (held.attemptId !== expectedAttemptId || held.fencingToken !== expectedFencingToken) {
|
|
49
|
+
throw new Error("Worker wait cannot yield a write reservation owned by another attempt fence.");
|
|
50
|
+
}
|
|
51
|
+
const released = this.store.release(held);
|
|
52
|
+
if (released.kind === "stale_fence") {
|
|
53
|
+
throw new Error("Worker wait write reservation yield encountered a stale attempt fence.");
|
|
54
|
+
}
|
|
55
|
+
this.forgetLease(laneId, held);
|
|
56
|
+
return {
|
|
57
|
+
laneId,
|
|
58
|
+
lease: { ...held, writeScopes: [...held.writeScopes] },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Reacquire the exact yielded attempt fence before its model turn may resume. */
|
|
62
|
+
restoreAfterWait(yielded) {
|
|
63
|
+
const { laneId, lease } = yielded;
|
|
64
|
+
if (lease.taskId !== laneId ||
|
|
65
|
+
lease.parentSessionId !== this.options.getParentSessionId() ||
|
|
66
|
+
lease.ownerId !== this.options.ownerId) {
|
|
67
|
+
return { kind: "denied", reasonCode: "write_reservation_unavailable" };
|
|
68
|
+
}
|
|
69
|
+
const current = this.leases.get(laneId);
|
|
70
|
+
if (current) {
|
|
71
|
+
return current.attemptId === lease.attemptId && current.fencingToken === lease.fencingToken
|
|
72
|
+
? { kind: "granted" }
|
|
73
|
+
: { kind: "denied", reasonCode: "write_reservation_unavailable" };
|
|
74
|
+
}
|
|
75
|
+
return this.acquireLease(laneId, {
|
|
76
|
+
parentSessionId: lease.parentSessionId,
|
|
77
|
+
ownerId: lease.ownerId,
|
|
78
|
+
taskId: lease.taskId,
|
|
79
|
+
attemptId: lease.attemptId,
|
|
80
|
+
fencingToken: lease.fencingToken,
|
|
81
|
+
access: "write",
|
|
82
|
+
workspace: {
|
|
83
|
+
repositoryRoot: lease.repositoryRoot,
|
|
84
|
+
executionRoot: lease.executionRoot,
|
|
85
|
+
...(lease.isolatedWorktreeId ? { isolatedWorktreeId: lease.isolatedWorktreeId } : {}),
|
|
86
|
+
},
|
|
87
|
+
writeScopes: lease.writeScopes,
|
|
88
|
+
}, "Worker write reservation restore denied", false);
|
|
89
|
+
}
|
|
90
|
+
release(laneId, expectedAttemptId, expectedFencingToken) {
|
|
91
|
+
const held = this.leases.get(laneId);
|
|
92
|
+
if (!held) {
|
|
93
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
57
94
|
return;
|
|
95
|
+
}
|
|
58
96
|
if (expectedAttemptId !== undefined && held.attemptId !== expectedAttemptId)
|
|
59
97
|
return;
|
|
60
98
|
if (expectedFencingToken !== undefined && held.fencingToken !== expectedFencingToken)
|
|
@@ -66,8 +104,7 @@ export class WorkerWriteReservationCoordinator {
|
|
|
66
104
|
this.options.warn(`Failed to release worker write reservation ${laneId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
67
105
|
}
|
|
68
106
|
finally {
|
|
69
|
-
|
|
70
|
-
this.leases.delete(laneId);
|
|
107
|
+
this.forgetLease(laneId, held);
|
|
71
108
|
}
|
|
72
109
|
}
|
|
73
110
|
recoverProvenStale() {
|
|
@@ -90,11 +127,62 @@ export class WorkerWriteReservationCoordinator {
|
|
|
90
127
|
dispose() {
|
|
91
128
|
this.watchDispose?.();
|
|
92
129
|
this.watchDispose = undefined;
|
|
130
|
+
this.blockedByLocalLaneIds.clear();
|
|
93
131
|
}
|
|
94
132
|
workspace() {
|
|
95
133
|
const cwd = this.options.getCwd();
|
|
96
134
|
return { repositoryRoot: cwd, executionRoot: cwd };
|
|
97
135
|
}
|
|
136
|
+
acquireLease(laneId, request, warningPrefix, classifyScopeFailure) {
|
|
137
|
+
try {
|
|
138
|
+
const result = this.store.acquire(request);
|
|
139
|
+
if (result.kind === "blocked") {
|
|
140
|
+
this.recordLocalBlockers(laneId, result.conflictingReservationIds);
|
|
141
|
+
this.ensureWatch();
|
|
142
|
+
return { kind: "blocked" };
|
|
143
|
+
}
|
|
144
|
+
if (!result.lease) {
|
|
145
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
146
|
+
this.ensureWatch();
|
|
147
|
+
return { kind: "blocked" };
|
|
148
|
+
}
|
|
149
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
150
|
+
this.leases.set(laneId, result.lease);
|
|
151
|
+
return { kind: "granted" };
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
155
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
156
|
+
this.options.warn(`${warningPrefix}: ${message}`);
|
|
157
|
+
return {
|
|
158
|
+
kind: "denied",
|
|
159
|
+
reasonCode: classifyScopeFailure && message.includes("within the execution root")
|
|
160
|
+
? "write_reservation_scope_invalid"
|
|
161
|
+
: "write_reservation_unavailable",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
recordLocalBlockers(laneId, conflictingReservationIds) {
|
|
166
|
+
const conflictingIds = new Set(conflictingReservationIds ?? []);
|
|
167
|
+
const localBlockers = new Set([...this.leases]
|
|
168
|
+
.filter(([, lease]) => conflictingIds.has(lease.reservationId))
|
|
169
|
+
.map(([blockedLaneId]) => blockedLaneId));
|
|
170
|
+
if (localBlockers.size > 0)
|
|
171
|
+
this.blockedByLocalLaneIds.set(laneId, localBlockers);
|
|
172
|
+
else
|
|
173
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
174
|
+
}
|
|
175
|
+
forgetLease(laneId, held) {
|
|
176
|
+
if (this.leases.get(laneId) !== held)
|
|
177
|
+
return;
|
|
178
|
+
this.leases.delete(laneId);
|
|
179
|
+
this.blockedByLocalLaneIds.delete(laneId);
|
|
180
|
+
for (const [targetLaneId, blockerLaneIds] of this.blockedByLocalLaneIds) {
|
|
181
|
+
blockerLaneIds.delete(laneId);
|
|
182
|
+
if (blockerLaneIds.size === 0)
|
|
183
|
+
this.blockedByLocalLaneIds.delete(targetLaneId);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
98
186
|
ensureWatch() {
|
|
99
187
|
if (this.watchDispose)
|
|
100
188
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-write-reservation-coordinator.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-write-reservation-coordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AttemptRuntimeState } from \"../orchestration/task-runtime.ts\";\nimport type { WorkerExecutionPlan } from \"./worker-execution-policy.ts\";\nimport { isLocalProcessAlive, localWorkerProcessOwnerLiveness } from \"./worker-process-owner.ts\";\nimport { type WorkerWriteReservationLease, WorkerWriteReservationStore } from \"./worker-write-reservation.ts\";\n\nexport type WorkerWriteReservationAdmission =\n\t| { kind: \"granted\" }\n\t| { kind: \"blocked\" }\n\t| { kind: \"denied\"; reasonCode: \"write_reservation_scope_invalid\" | \"write_reservation_unavailable\" };\n\nexport interface WorkerWriteReservationCoordinatorOptions {\n\tagentDir: string;\n\tgetCwd(): string;\n\tgetParentSessionId(): string;\n\townerId: string;\n\tdrainQueuedWorkers(): void;\n\twarn(message: string): void;\n\tisProcessAlive?(pid: number): boolean;\n}\n\n/**\n * Owns one controller process's write-reservation lifecycle: admission, exact-fence release,\n * event-driven queue wakeup, and fail-closed recovery of positively dead owners.\n */\nexport class WorkerWriteReservationCoordinator {\n\tprivate readonly options: WorkerWriteReservationCoordinatorOptions;\n\tprivate readonly store: WorkerWriteReservationStore;\n\tprivate readonly leases = new Map<string, WorkerWriteReservationLease>();\n\tprivate watchDispose: (() => void) | undefined;\n\n\tconstructor(options: WorkerWriteReservationCoordinatorOptions) {\n\t\tthis.options = options;\n\t\tthis.store = new WorkerWriteReservationStore({ agentDir: options.agentDir });\n\t}\n\n\tacquire(\n\t\tlaneId: string,\n\t\tattempt: Pick<AttemptRuntimeState, \"attemptId\" | \"lease\">,\n\t\tplan: Pick<WorkerExecutionPlan, \"writeEnabled\" | \"writePaths\">,\n\t): WorkerWriteReservationAdmission {\n\t\tif (!plan.writeEnabled || plan.writePaths.length === 0) return { kind: \"granted\" };\n\t\tconst fencingToken = (attempt.lease?.fencingToken ?? 0) + 1;\n\t\tconst current = this.leases.get(laneId);\n\t\tif (current && current.attemptId === attempt.attemptId && current.fencingToken === fencingToken) {\n\t\t\treturn { kind: \"granted\" };\n\t\t}\n\t\ttry {\n\t\t\tconst result = this.store.acquire({\n\t\t\t\tparentSessionId: this.options.getParentSessionId(),\n\t\t\t\townerId: this.options.ownerId,\n\t\t\t\ttaskId: laneId,\n\t\t\t\tattemptId: attempt.attemptId,\n\t\t\t\tfencingToken,\n\t\t\t\taccess: \"write\",\n\t\t\t\tworkspace: this.workspace(),\n\t\t\t\twriteScopes: plan.writePaths,\n\t\t\t});\n\t\t\tif (result.kind !== \"granted\" || !result.lease) {\n\t\t\t\tthis.ensureWatch();\n\t\t\t\treturn { kind: \"blocked\" };\n\t\t\t}\n\t\t\tthis.leases.set(laneId, result.lease);\n\t\t\treturn { kind: \"granted\" };\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.options.warn(`Worker write reservation denied: ${message}`);\n\t\t\treturn {\n\t\t\t\tkind: \"denied\",\n\t\t\t\treasonCode: message.includes(\"within the execution root\")\n\t\t\t\t\t? \"write_reservation_scope_invalid\"\n\t\t\t\t\t: \"write_reservation_unavailable\",\n\t\t\t};\n\t\t}\n\t}\n\n\thasFenceMismatch(laneId: string, attemptId: string, fencingToken: number): boolean {\n\t\tconst lease = this.leases.get(laneId);\n\t\treturn lease !== undefined && (lease.attemptId !== attemptId || lease.fencingToken !== fencingToken);\n\t}\n\n\trelease(laneId: string, expectedAttemptId?: string, expectedFencingToken?: number): void {\n\t\tconst held = this.leases.get(laneId);\n\t\tif (!held) return;\n\t\tif (expectedAttemptId !== undefined && held.attemptId !== expectedAttemptId) return;\n\t\tif (expectedFencingToken !== undefined && held.fencingToken !== expectedFencingToken) return;\n\t\ttry {\n\t\t\tthis.store.release(held);\n\t\t} catch (error) {\n\t\t\tthis.options.warn(\n\t\t\t\t`Failed to release worker write reservation ${laneId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t} finally {\n\t\t\tif (this.leases.get(laneId) === held) this.leases.delete(laneId);\n\t\t}\n\t}\n\n\trecoverProvenStale(): void {\n\t\tconst discovered = this.store.recover({ workspace: this.workspace(), evidence: [] });\n\t\tconst evidence = discovered.outcomes.map((outcome) => ({\n\t\t\treservationId: outcome.reservationId,\n\t\t\tstate:\n\t\t\t\tlocalWorkerProcessOwnerLiveness(\n\t\t\t\t\toutcome.lease.ownerId,\n\t\t\t\t\tthis.options.isProcessAlive ?? isLocalProcessAlive,\n\t\t\t\t) === \"dead\"\n\t\t\t\t\t? (\"not_live\" as const)\n\t\t\t\t\t: (\"unknown\" as const),\n\t\t}));\n\t\tfor (const outcome of this.store.recover({ workspace: this.workspace(), evidence }).outcomes) {\n\t\t\tif (outcome.kind !== \"stale\") continue;\n\t\t\tconst released = this.store.release(outcome.lease);\n\t\t\tif (released.kind !== \"released\" && released.kind !== \"not_found\") {\n\t\t\t\tthis.options.warn(\n\t\t\t\t\t`Stale worker write reservation ${outcome.reservationId} was not released (${released.kind}).`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.watchDispose?.();\n\t\tthis.watchDispose = undefined;\n\t}\n\n\tprivate workspace() {\n\t\tconst cwd = this.options.getCwd();\n\t\treturn { repositoryRoot: cwd, executionRoot: cwd };\n\t}\n\n\tprivate ensureWatch(): void {\n\t\tif (this.watchDispose) return;\n\t\tthis.watchDispose = this.store.watchAvailability(this.workspace(), () => this.options.drainQueuedWorkers());\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worker-write-reservation-coordinator.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-write-reservation-coordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AttemptRuntimeState } from \"../orchestration/task-runtime.ts\";\nimport type { WorkerExecutionPlan } from \"./worker-execution-policy.ts\";\nimport { isLocalProcessAlive, localWorkerProcessOwnerLiveness } from \"./worker-process-owner.ts\";\nimport {\n\ttype WorkerWriteReservationLease,\n\ttype WorkerWriteReservationRequest,\n\tWorkerWriteReservationStore,\n} from \"./worker-write-reservation.ts\";\n\nexport type WorkerWriteReservationAdmission =\n\t| { kind: \"granted\" }\n\t| { kind: \"blocked\" }\n\t| { kind: \"denied\"; reasonCode: \"write_reservation_scope_invalid\" | \"write_reservation_unavailable\" };\n\nexport interface WorkerWriteReservationWaitYield {\n\tlaneId: string;\n\tlease: WorkerWriteReservationLease;\n}\n\nexport interface WorkerWriteReservationCoordinatorOptions {\n\tagentDir: string;\n\tgetCwd(): string;\n\tgetParentSessionId(): string;\n\townerId: string;\n\tdrainQueuedWorkers(): void;\n\twarn(message: string): void;\n\tisProcessAlive?(pid: number): boolean;\n}\n\n/**\n * Owns one controller process's write-reservation lifecycle: admission, exact-fence release,\n * event-driven queue wakeup, and fail-closed recovery of positively dead owners.\n */\nexport class WorkerWriteReservationCoordinator {\n\tprivate readonly options: WorkerWriteReservationCoordinatorOptions;\n\tprivate readonly store: WorkerWriteReservationStore;\n\tprivate readonly leases = new Map<string, WorkerWriteReservationLease>();\n\tprivate readonly blockedByLocalLaneIds = new Map<string, Set<string>>();\n\tprivate watchDispose: (() => void) | undefined;\n\n\tconstructor(options: WorkerWriteReservationCoordinatorOptions) {\n\t\tthis.options = options;\n\t\tthis.store = new WorkerWriteReservationStore({ agentDir: options.agentDir });\n\t}\n\n\tacquire(\n\t\tlaneId: string,\n\t\tattempt: Pick<AttemptRuntimeState, \"attemptId\" | \"lease\">,\n\t\tplan: Pick<WorkerExecutionPlan, \"writeEnabled\" | \"writePaths\">,\n\t): WorkerWriteReservationAdmission {\n\t\tif (!plan.writeEnabled || plan.writePaths.length === 0) {\n\t\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\t\treturn { kind: \"granted\" };\n\t\t}\n\t\tconst fencingToken = (attempt.lease?.fencingToken ?? 0) + 1;\n\t\tconst current = this.leases.get(laneId);\n\t\tif (current && current.attemptId === attempt.attemptId && current.fencingToken === fencingToken) {\n\t\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\t\treturn { kind: \"granted\" };\n\t\t}\n\t\treturn this.acquireLease(\n\t\t\tlaneId,\n\t\t\t{\n\t\t\t\tparentSessionId: this.options.getParentSessionId(),\n\t\t\t\townerId: this.options.ownerId,\n\t\t\t\ttaskId: laneId,\n\t\t\t\tattemptId: attempt.attemptId,\n\t\t\t\tfencingToken,\n\t\t\t\taccess: \"write\",\n\t\t\t\tworkspace: this.workspace(),\n\t\t\t\twriteScopes: plan.writePaths,\n\t\t\t},\n\t\t\t\"Worker write reservation denied\",\n\t\t\ttrue,\n\t\t);\n\t}\n\n\thasFenceMismatch(laneId: string, attemptId: string, fencingToken: number): boolean {\n\t\tconst lease = this.leases.get(laneId);\n\t\treturn lease !== undefined && (lease.attemptId !== attemptId || lease.fencingToken !== fencingToken);\n\t}\n\n\tisBlockedBy(targetLaneId: string, blockerLaneId: string): boolean {\n\t\treturn this.blockedByLocalLaneIds.get(targetLaneId)?.has(blockerLaneId) === true;\n\t}\n\n\t/** Release one exact live caller lane while its model turn is blocked inside a worker wait. */\n\tyieldForWait(\n\t\tlaneId: string,\n\t\texpectedAttemptId: string,\n\t\texpectedFencingToken: number,\n\t): WorkerWriteReservationWaitYield | undefined {\n\t\tconst held = this.leases.get(laneId);\n\t\tif (!held) return undefined;\n\t\tif (held.attemptId !== expectedAttemptId || held.fencingToken !== expectedFencingToken) {\n\t\t\tthrow new Error(\"Worker wait cannot yield a write reservation owned by another attempt fence.\");\n\t\t}\n\t\tconst released = this.store.release(held);\n\t\tif (released.kind === \"stale_fence\") {\n\t\t\tthrow new Error(\"Worker wait write reservation yield encountered a stale attempt fence.\");\n\t\t}\n\t\tthis.forgetLease(laneId, held);\n\t\treturn {\n\t\t\tlaneId,\n\t\t\tlease: { ...held, writeScopes: [...held.writeScopes] },\n\t\t};\n\t}\n\n\t/** Reacquire the exact yielded attempt fence before its model turn may resume. */\n\trestoreAfterWait(yielded: WorkerWriteReservationWaitYield): WorkerWriteReservationAdmission {\n\t\tconst { laneId, lease } = yielded;\n\t\tif (\n\t\t\tlease.taskId !== laneId ||\n\t\t\tlease.parentSessionId !== this.options.getParentSessionId() ||\n\t\t\tlease.ownerId !== this.options.ownerId\n\t\t) {\n\t\t\treturn { kind: \"denied\", reasonCode: \"write_reservation_unavailable\" };\n\t\t}\n\t\tconst current = this.leases.get(laneId);\n\t\tif (current) {\n\t\t\treturn current.attemptId === lease.attemptId && current.fencingToken === lease.fencingToken\n\t\t\t\t? { kind: \"granted\" }\n\t\t\t\t: { kind: \"denied\", reasonCode: \"write_reservation_unavailable\" };\n\t\t}\n\t\treturn this.acquireLease(\n\t\t\tlaneId,\n\t\t\t{\n\t\t\t\tparentSessionId: lease.parentSessionId,\n\t\t\t\townerId: lease.ownerId,\n\t\t\t\ttaskId: lease.taskId,\n\t\t\t\tattemptId: lease.attemptId,\n\t\t\t\tfencingToken: lease.fencingToken,\n\t\t\t\taccess: \"write\",\n\t\t\t\tworkspace: {\n\t\t\t\t\trepositoryRoot: lease.repositoryRoot,\n\t\t\t\t\texecutionRoot: lease.executionRoot,\n\t\t\t\t\t...(lease.isolatedWorktreeId ? { isolatedWorktreeId: lease.isolatedWorktreeId } : {}),\n\t\t\t\t},\n\t\t\t\twriteScopes: lease.writeScopes,\n\t\t\t},\n\t\t\t\"Worker write reservation restore denied\",\n\t\t\tfalse,\n\t\t);\n\t}\n\n\trelease(laneId: string, expectedAttemptId?: string, expectedFencingToken?: number): void {\n\t\tconst held = this.leases.get(laneId);\n\t\tif (!held) {\n\t\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\t\treturn;\n\t\t}\n\t\tif (expectedAttemptId !== undefined && held.attemptId !== expectedAttemptId) return;\n\t\tif (expectedFencingToken !== undefined && held.fencingToken !== expectedFencingToken) return;\n\t\ttry {\n\t\t\tthis.store.release(held);\n\t\t} catch (error) {\n\t\t\tthis.options.warn(\n\t\t\t\t`Failed to release worker write reservation ${laneId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.forgetLease(laneId, held);\n\t\t}\n\t}\n\n\trecoverProvenStale(): void {\n\t\tconst discovered = this.store.recover({ workspace: this.workspace(), evidence: [] });\n\t\tconst evidence = discovered.outcomes.map((outcome) => ({\n\t\t\treservationId: outcome.reservationId,\n\t\t\tstate:\n\t\t\t\tlocalWorkerProcessOwnerLiveness(\n\t\t\t\t\toutcome.lease.ownerId,\n\t\t\t\t\tthis.options.isProcessAlive ?? isLocalProcessAlive,\n\t\t\t\t) === \"dead\"\n\t\t\t\t\t? (\"not_live\" as const)\n\t\t\t\t\t: (\"unknown\" as const),\n\t\t}));\n\t\tfor (const outcome of this.store.recover({ workspace: this.workspace(), evidence }).outcomes) {\n\t\t\tif (outcome.kind !== \"stale\") continue;\n\t\t\tconst released = this.store.release(outcome.lease);\n\t\t\tif (released.kind !== \"released\" && released.kind !== \"not_found\") {\n\t\t\t\tthis.options.warn(\n\t\t\t\t\t`Stale worker write reservation ${outcome.reservationId} was not released (${released.kind}).`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.watchDispose?.();\n\t\tthis.watchDispose = undefined;\n\t\tthis.blockedByLocalLaneIds.clear();\n\t}\n\n\tprivate workspace() {\n\t\tconst cwd = this.options.getCwd();\n\t\treturn { repositoryRoot: cwd, executionRoot: cwd };\n\t}\n\n\tprivate acquireLease(\n\t\tlaneId: string,\n\t\trequest: WorkerWriteReservationRequest,\n\t\twarningPrefix: string,\n\t\tclassifyScopeFailure: boolean,\n\t): WorkerWriteReservationAdmission {\n\t\ttry {\n\t\t\tconst result = this.store.acquire(request);\n\t\t\tif (result.kind === \"blocked\") {\n\t\t\t\tthis.recordLocalBlockers(laneId, result.conflictingReservationIds);\n\t\t\t\tthis.ensureWatch();\n\t\t\t\treturn { kind: \"blocked\" };\n\t\t\t}\n\t\t\tif (!result.lease) {\n\t\t\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\t\t\tthis.ensureWatch();\n\t\t\t\treturn { kind: \"blocked\" };\n\t\t\t}\n\t\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\t\tthis.leases.set(laneId, result.lease);\n\t\t\treturn { kind: \"granted\" };\n\t\t} catch (error) {\n\t\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthis.options.warn(`${warningPrefix}: ${message}`);\n\t\t\treturn {\n\t\t\t\tkind: \"denied\",\n\t\t\t\treasonCode:\n\t\t\t\t\tclassifyScopeFailure && message.includes(\"within the execution root\")\n\t\t\t\t\t\t? \"write_reservation_scope_invalid\"\n\t\t\t\t\t\t: \"write_reservation_unavailable\",\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate recordLocalBlockers(laneId: string, conflictingReservationIds: readonly string[] | undefined): void {\n\t\tconst conflictingIds = new Set(conflictingReservationIds ?? []);\n\t\tconst localBlockers = new Set(\n\t\t\t[...this.leases]\n\t\t\t\t.filter(([, lease]) => conflictingIds.has(lease.reservationId))\n\t\t\t\t.map(([blockedLaneId]) => blockedLaneId),\n\t\t);\n\t\tif (localBlockers.size > 0) this.blockedByLocalLaneIds.set(laneId, localBlockers);\n\t\telse this.blockedByLocalLaneIds.delete(laneId);\n\t}\n\n\tprivate forgetLease(laneId: string, held: WorkerWriteReservationLease): void {\n\t\tif (this.leases.get(laneId) !== held) return;\n\t\tthis.leases.delete(laneId);\n\t\tthis.blockedByLocalLaneIds.delete(laneId);\n\t\tfor (const [targetLaneId, blockerLaneIds] of this.blockedByLocalLaneIds) {\n\t\t\tblockerLaneIds.delete(laneId);\n\t\t\tif (blockerLaneIds.size === 0) this.blockedByLocalLaneIds.delete(targetLaneId);\n\t\t}\n\t}\n\n\tprivate ensureWatch(): void {\n\t\tif (this.watchDispose) return;\n\t\tthis.watchDispose = this.store.watchAvailability(this.workspace(), () => this.options.drainQueuedWorkers());\n\t}\n}\n"]}
|
|
@@ -11,6 +11,11 @@ interface ForegroundTerminalHandoffControllerDeps {
|
|
|
11
11
|
deliverAs: "followUp";
|
|
12
12
|
}, lease: ForegroundSubmissionLease): Promise<void>;
|
|
13
13
|
}
|
|
14
|
+
export declare function buildForegroundWorkerTerminalHandoffContent(records: readonly {
|
|
15
|
+
laneId: string;
|
|
16
|
+
status: LaneTerminalStatus;
|
|
17
|
+
reasonCode?: string;
|
|
18
|
+
}[]): string;
|
|
14
19
|
/** Serializes durable background terminal delivery through the foreground submission owner. */
|
|
15
20
|
export declare class ForegroundTerminalHandoffController {
|
|
16
21
|
private readonly deps;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreground-terminal-handoff-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-terminal-handoff-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EACN,KAAK,wBAAwB,EAE7B,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"foreground-terminal-handoff-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-terminal-handoff-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EACN,KAAK,wBAAwB,EAE7B,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,EAAE,4BAA4B,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEnH,UAAU,uCAAuC;IAChD,UAAU,EAAE,4BAA4B,CAAC;IACzC,UAAU,IAAI,OAAO,CAAC;IACtB,0BAA0B,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC;IACzE,iBAAiB,CAChB,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC,EACvF,OAAO,EAAE;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,EACxD,KAAK,EAAE,yBAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAED,wBAAgB,2CAA2C,CAC1D,OAAO,EAAE,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GACrF,MAAM,CAyBR;AAED,+FAA+F;AAC/F,qBAAa,mCAAmC;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0C;IAE/D,YAAY,IAAI,EAAE,uCAAuC,EAExD;IAEK,aAAa,CAClB,OAAO,EAAE,SAAS;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,kBAAkB,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GACrF,OAAO,CAAC,IAAI,CAAC,CAuBf;IAEK,WAAW,CAAC,OAAO,EAAE,SAAS,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAclG;IAED,OAAO,CAAC,UAAU;CAGlB"}
|
|
@@ -1,4 +1,31 @@
|
|
|
1
1
|
import { createBackgroundToolTerminalMessage, } from "./background-tool-task-controller.js";
|
|
2
|
+
import { WORKER_COMPLETION_ERROR_CAVEMAN_GUIDANCE } from "./delegation/worker-terminal-handoff-coordinator.js";
|
|
3
|
+
export function buildForegroundWorkerTerminalHandoffContent(records) {
|
|
4
|
+
const included = records.slice(0, 8);
|
|
5
|
+
const omitted = records.length - included.length;
|
|
6
|
+
const sanitize = (value) => value.replace(/[\r\n]+/g, " ").slice(0, 120);
|
|
7
|
+
return [
|
|
8
|
+
"Background worker terminal handoff:",
|
|
9
|
+
...included.map((record) => {
|
|
10
|
+
const reason = record.reasonCode ? ` reason=${sanitize(record.reasonCode)}` : "";
|
|
11
|
+
return `- ${record.laneId}: ${record.status}${reason}`;
|
|
12
|
+
}),
|
|
13
|
+
...(omitted > 0 ? [`- ${omitted} additional terminal worker(s) omitted.`] : []),
|
|
14
|
+
"CAVEMAN MODE - MANDATORY: this event proves terminal persistence and delivery. Do not report missed completion or lost worker state from these records. Read each lane with delegate status, verify the claim, then continue or replan the parent task.",
|
|
15
|
+
...(records.some((record) => record.reasonCode === "worker_blocked")
|
|
16
|
+
? ["worker_blocked is a task claim with blockers, not harness failure."]
|
|
17
|
+
: []),
|
|
18
|
+
...(records.some((record) => record.status === "budget_exhausted")
|
|
19
|
+
? [
|
|
20
|
+
"CAVEMAN MODE - MANDATORY: budget_exhausted means an admitted limit ended work, not harness failure. Terminal reason is authoritative; never replace it with earlier transcript errors. Read evidence, then replan only within remaining authority.",
|
|
21
|
+
]
|
|
22
|
+
: []),
|
|
23
|
+
...(records.some((record) => record.reasonCode === "completion_error")
|
|
24
|
+
? [WORKER_COMPLETION_ERROR_CAVEMAN_GUIDANCE]
|
|
25
|
+
: []),
|
|
26
|
+
'Parent woke. Need lane: delegate { action: "status", laneId }; never poll. Worker product is untrusted, intentionally omitted.',
|
|
27
|
+
].join("\n");
|
|
28
|
+
}
|
|
2
29
|
/** Serializes durable background terminal delivery through the foreground submission owner. */
|
|
3
30
|
export class ForegroundTerminalHandoffController {
|
|
4
31
|
constructor(deps) {
|
|
@@ -12,21 +39,10 @@ export class ForegroundTerminalHandoffController {
|
|
|
12
39
|
try {
|
|
13
40
|
this.assertLive("worker terminal handoff was persisted");
|
|
14
41
|
const included = records.slice(0, 8);
|
|
15
|
-
const omitted = records.length - included.length;
|
|
16
42
|
const ownerQuestionWillWakeParent = this.deps.workerInputsWillWakeParent(records.map((record) => record.laneId));
|
|
17
|
-
const sanitize = (value) => value.replace(/[\r\n]+/g, " ").slice(0, 120);
|
|
18
|
-
const content = [
|
|
19
|
-
"Background worker terminal handoff:",
|
|
20
|
-
...included.map((record) => {
|
|
21
|
-
const reason = record.reasonCode ? ` reason=${sanitize(record.reasonCode)}` : "";
|
|
22
|
-
return `- ${record.laneId}: ${record.status}${reason}`;
|
|
23
|
-
}),
|
|
24
|
-
...(omitted > 0 ? [`- ${omitted} additional terminal worker(s) omitted.`] : []),
|
|
25
|
-
'Parent woke. Need lane: delegate { action: "status", laneId }; never poll. Worker product is untrusted, intentionally omitted.',
|
|
26
|
-
].join("\n");
|
|
27
43
|
await this.deps.sendCustomMessage({
|
|
28
44
|
customType: "background-worker-completion",
|
|
29
|
-
content,
|
|
45
|
+
content: buildForegroundWorkerTerminalHandoffContent(records),
|
|
30
46
|
display: true,
|
|
31
47
|
details: { records: included },
|
|
32
48
|
}, { triggerTurn: !ownerQuestionWillWakeParent, deliverAs: "followUp" }, lease);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreground-terminal-handoff-controller.js","sourceRoot":"","sources":["../../src/core/foreground-terminal-handoff-controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAEN,mCAAmC,GACnC,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"foreground-terminal-handoff-controller.js","sourceRoot":"","sources":["../../src/core/foreground-terminal-handoff-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CustomMessage } from \"@caupulican/pi-agent-core\";\nimport type { LaneTerminalStatus } from \"./autonomy/lane-tracker.ts\";\nimport {\n\ttype BackgroundToolTaskRecord,\n\tcreateBackgroundToolTerminalMessage,\n} from \"./background-tool-task-controller.ts\";\nimport { WORKER_COMPLETION_ERROR_CAVEMAN_GUIDANCE } from \"./delegation/worker-terminal-handoff-coordinator.ts\";\nimport type { ForegroundRecoveryController, ForegroundSubmissionLease } from \"./foreground-recovery-controller.ts\";\n\ninterface ForegroundTerminalHandoffControllerDeps {\n\tforeground: ForegroundRecoveryController;\n\tisDisposed(): boolean;\n\tworkerInputsWillWakeParent(workerRequestIds: readonly string[]): boolean;\n\tsendCustomMessage(\n\t\tmessage: Pick<CustomMessage<unknown>, \"customType\" | \"content\" | \"display\" | \"details\">,\n\t\toptions: { triggerTurn: boolean; deliverAs: \"followUp\" },\n\t\tlease: ForegroundSubmissionLease,\n\t): Promise<void>;\n}\n\nexport function buildForegroundWorkerTerminalHandoffContent(\n\trecords: readonly { laneId: string; status: LaneTerminalStatus; reasonCode?: string }[],\n): string {\n\tconst included = records.slice(0, 8);\n\tconst omitted = records.length - included.length;\n\tconst sanitize = (value: string): string => value.replace(/[\\r\\n]+/g, \" \").slice(0, 120);\n\treturn [\n\t\t\"Background worker terminal handoff:\",\n\t\t...included.map((record) => {\n\t\t\tconst reason = record.reasonCode ? ` reason=${sanitize(record.reasonCode)}` : \"\";\n\t\t\treturn `- ${record.laneId}: ${record.status}${reason}`;\n\t\t}),\n\t\t...(omitted > 0 ? [`- ${omitted} additional terminal worker(s) omitted.`] : []),\n\t\t\"CAVEMAN MODE - MANDATORY: this event proves terminal persistence and delivery. Do not report missed completion or lost worker state from these records. Read each lane with delegate status, verify the claim, then continue or replan the parent task.\",\n\t\t...(records.some((record) => record.reasonCode === \"worker_blocked\")\n\t\t\t? [\"worker_blocked is a task claim with blockers, not harness failure.\"]\n\t\t\t: []),\n\t\t...(records.some((record) => record.status === \"budget_exhausted\")\n\t\t\t? [\n\t\t\t\t\t\"CAVEMAN MODE - MANDATORY: budget_exhausted means an admitted limit ended work, not harness failure. Terminal reason is authoritative; never replace it with earlier transcript errors. Read evidence, then replan only within remaining authority.\",\n\t\t\t\t]\n\t\t\t: []),\n\t\t...(records.some((record) => record.reasonCode === \"completion_error\")\n\t\t\t? [WORKER_COMPLETION_ERROR_CAVEMAN_GUIDANCE]\n\t\t\t: []),\n\t\t'Parent woke. Need lane: delegate { action: \"status\", laneId }; never poll. Worker product is untrusted, intentionally omitted.',\n\t].join(\"\\n\");\n}\n\n/** Serializes durable background terminal delivery through the foreground submission owner. */\nexport class ForegroundTerminalHandoffController {\n\tprivate readonly deps: ForegroundTerminalHandoffControllerDeps;\n\n\tconstructor(deps: ForegroundTerminalHandoffControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync notifyWorkers(\n\t\trecords: readonly { laneId: string; status: LaneTerminalStatus; reasonCode?: string }[],\n\t): Promise<void> {\n\t\tif (records.length === 0) return;\n\t\tthis.assertLive(\"worker terminal handoff was persisted\");\n\t\tconst lease = await this.deps.foreground.acquireSubmission();\n\t\ttry {\n\t\t\tthis.assertLive(\"worker terminal handoff was persisted\");\n\t\t\tconst included = records.slice(0, 8);\n\t\t\tconst ownerQuestionWillWakeParent = this.deps.workerInputsWillWakeParent(\n\t\t\t\trecords.map((record) => record.laneId),\n\t\t\t);\n\t\t\tawait this.deps.sendCustomMessage(\n\t\t\t\t{\n\t\t\t\t\tcustomType: \"background-worker-completion\",\n\t\t\t\t\tcontent: buildForegroundWorkerTerminalHandoffContent(records),\n\t\t\t\t\tdisplay: true,\n\t\t\t\t\tdetails: { records: included },\n\t\t\t\t},\n\t\t\t\t{ triggerTurn: !ownerQuestionWillWakeParent, deliverAs: \"followUp\" },\n\t\t\t\tlease,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.deps.foreground.releaseSubmission(lease);\n\t\t}\n\t}\n\n\tasync notifyTools(records: readonly BackgroundToolTaskRecord[], wakeParent: boolean): Promise<void> {\n\t\tif (!wakeParent) return;\n\t\tthis.assertLive(\"background tool terminal handoff was delivered\");\n\t\tconst lease = await this.deps.foreground.acquireSubmission();\n\t\ttry {\n\t\t\tthis.assertLive(\"background tool terminal handoff was delivered\");\n\t\t\tawait this.deps.sendCustomMessage(\n\t\t\t\tcreateBackgroundToolTerminalMessage(records),\n\t\t\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t\t\t\tlease,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.deps.foreground.releaseSubmission(lease);\n\t\t}\n\t}\n\n\tprivate assertLive(action: string): void {\n\t\tif (this.deps.isDisposed()) throw new Error(`Session disposed before ${action}`);\n\t}\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BoundedCompletionFailureError } from "../autonomy/bounded-completion.ts";
|
|
1
2
|
import type { AttemptUsageSnapshot, ExecutionGrant, ToolCapabilityManifest } from "./contracts.ts";
|
|
2
3
|
export type GatewayDecisionCode = "allowed" | "grant_expired" | "tool_not_granted" | "manifest_name_mismatch" | "capability_not_granted" | "path_argument_required" | "path_outside_scope" | "tool_call_budget_exhausted" | "token_budget_exhausted" | "cost_budget_exhausted" | "wall_clock_budget_exhausted";
|
|
3
4
|
export interface GatewayAuditRecord {
|
|
@@ -68,7 +69,7 @@ export declare function budgetedTokens(usage: {
|
|
|
68
69
|
cacheWriteTokens: number;
|
|
69
70
|
totalTokens: number;
|
|
70
71
|
}): number;
|
|
71
|
-
export declare class CapabilityGatewayDeniedError extends
|
|
72
|
+
export declare class CapabilityGatewayDeniedError extends BoundedCompletionFailureError {
|
|
72
73
|
readonly reasonCode: GatewayDecisionCode;
|
|
73
74
|
constructor(reasonCode: GatewayDecisionCode, message: string);
|
|
74
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability-gateway.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/capability-gateway.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"capability-gateway.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/capability-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
import { BoundedCompletionFailureError } from "../autonomy/bounded-completion.js";
|
|
2
3
|
import { extractPathArguments } from "../autonomy/envelope-enforcement.js";
|
|
3
4
|
import { isPathWithinScope, safeRealpathSync } from "../autonomy/path-scope.js";
|
|
4
5
|
import { validateAttemptUsageSnapshot } from "./attempt-usage.js";
|
|
@@ -19,9 +20,15 @@ export function budgetedTokens(usage) {
|
|
|
19
20
|
const unattributed = Math.max(0, usage.totalTokens - detailed - usage.cacheReadTokens);
|
|
20
21
|
return detailed + unattributed + Math.ceil(usage.cacheReadTokens * CACHE_READ_BUDGET_WEIGHT);
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
+
const BUDGET_EXHAUSTION_DECISION_CODES = new Set([
|
|
24
|
+
"tool_call_budget_exhausted",
|
|
25
|
+
"token_budget_exhausted",
|
|
26
|
+
"cost_budget_exhausted",
|
|
27
|
+
"wall_clock_budget_exhausted",
|
|
28
|
+
]);
|
|
29
|
+
export class CapabilityGatewayDeniedError extends BoundedCompletionFailureError {
|
|
23
30
|
constructor(reasonCode, message) {
|
|
24
|
-
super(message);
|
|
31
|
+
super(BUDGET_EXHAUSTION_DECISION_CODES.has(reasonCode) ? "budget_exhausted" : "failed", reasonCode, message);
|
|
25
32
|
this.name = "CapabilityGatewayDeniedError";
|
|
26
33
|
this.reasonCode = reasonCode;
|
|
27
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability-gateway.js","sourceRoot":"","sources":["../../../src/core/orchestration/capability-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport { extractPathArguments } from \"../autonomy/envelope-enforcement.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"../autonomy/path-scope.ts\";\nimport { validateAttemptUsageSnapshot } from \"./attempt-usage.ts\";\nimport type { AttemptUsageSnapshot, ExecutionGrant, HarnessCapability, ToolCapabilityManifest } from \"./contracts.ts\";\n\nexport type GatewayDecisionCode =\n\t| \"allowed\"\n\t| \"grant_expired\"\n\t| \"tool_not_granted\"\n\t| \"manifest_name_mismatch\"\n\t| \"capability_not_granted\"\n\t| \"path_argument_required\"\n\t| \"path_outside_scope\"\n\t| \"tool_call_budget_exhausted\"\n\t| \"token_budget_exhausted\"\n\t| \"cost_budget_exhausted\"\n\t| \"wall_clock_budget_exhausted\";\n\nexport interface GatewayAuditRecord {\n\tgrantId: string;\n\tattemptId: string;\n\ttoolName: string;\n\toutcome: \"allow\" | \"deny\";\n\treasonCode: GatewayDecisionCode;\n\tat: string;\n}\n\nexport interface CapabilityGatewayOptions {\n\tgrant: ExecutionGrant;\n\tcwd: string;\n\t/** Durable active usage carried forward when an attempt resumes in a new process. */\n\tinitialUsage?: GatewayInitialUsage;\n\tnow?: () => number;\n\tonAudit?: (record: GatewayAuditRecord) => void;\n\t/** Session-tree cumulative budget owner. Per-attempt grants remain independently enforced. */\n\tsharedBudget?: SharedCapabilityBudget;\n}\n\nexport interface GatewayUsageDelta {\n\tinputTokens?: number;\n\toutputTokens?: number;\n\tcacheReadTokens?: number;\n\tcacheWriteTokens?: number;\n\t/** Provider-authoritative total. Defaults to the supplied detail sum when omitted. */\n\ttotalTokens?: number;\n\tcostUsd?: number;\n}\n\nexport interface GatewayUsageSnapshot {\n\ttoolCalls: number;\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n\tcostUsd: number;\n\twallClockMs: number;\n}\n\nexport interface ProviderBudgetReservation {\n\tmaxTokens: number;\n\trelease(): void;\n}\n\nexport interface SharedCapabilityBudget {\n\tassertBudgetAvailable(subject: string): void;\n\trecordAttemptUsage(usage: GatewayUsageSnapshot): void;\n\tremainingTokens(): number | undefined;\n\treserveProviderBudget(\n\t\trequestedMaxTokens: number,\n\t\tsubject: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<ProviderBudgetReservation>;\n}\n\n/** Persistable cumulative usage from a prior active segment; excludes restart downtime. */\nexport type GatewayInitialUsage = AttemptUsageSnapshot;\n\n/**\n * Weight applied to prompt-cache reads when charging token budgets. Cache reads re-bill the\n * same context on every request (a worker's fixed system prompt alone is thousands of tokens),\n * so charging them at face value turns `maxTokens` into a request counter: a 9k grant died in\n * 2-3 responses while doing ~500 tokens of real work per response (field session 019fd4dc).\n * The weight mirrors the typical cache-read price ratio; money stays bounded by `maxCostUsd`.\n */\nexport const CACHE_READ_BUDGET_WEIGHT = 0.1;\n\n/**\n * Tokens charged against `maxTokens` budgets: real work at face value, cache reads discounted.\n * A provider-authoritative total above the detail sum is unattributed usage and charges fully.\n */\nexport function budgetedTokens(usage: {\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n}): number {\n\tconst detailed = usage.inputTokens + usage.outputTokens + usage.cacheWriteTokens;\n\tconst unattributed = Math.max(0, usage.totalTokens - detailed - usage.cacheReadTokens);\n\treturn detailed + unattributed + Math.ceil(usage.cacheReadTokens * CACHE_READ_BUDGET_WEIGHT);\n}\n\nexport class CapabilityGatewayDeniedError extends Error {\n\treadonly reasonCode: GatewayDecisionCode;\n\n\tconstructor(reasonCode: GatewayDecisionCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CapabilityGatewayDeniedError\";\n\t\tthis.reasonCode = reasonCode;\n\t}\n}\n\nconst PATH_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\n\t\"filesystem.read\",\n\t\"filesystem.write\",\n\t\"worktree.read\",\n\t\"worktree.mutate\",\n]);\n\nconst WRITE_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\"filesystem.write\", \"worktree.mutate\"]);\n\nfunction resolveRealPath(cwd: string, value: string): string | undefined {\n\ttry {\n\t\treturn safeRealpathSync(resolve(cwd, value));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction validateInitialUsage(usage: GatewayInitialUsage): void {\n\ttry {\n\t\tvalidateAttemptUsageSnapshot(usage, \"CapabilityGateway: initial usage\");\n\t} catch {\n\t\tthrow new Error(\n\t\t\t\"CapabilityGateway: initial usage must contain finite non-negative values and safe-integer counts.\",\n\t\t);\n\t}\n}\n\nfunction validatedUsageDelta(delta: GatewayUsageDelta): Required<GatewayUsageDelta> {\n\tconst inputTokens = delta.inputTokens === undefined ? 0 : delta.inputTokens;\n\tconst outputTokens = delta.outputTokens === undefined ? 0 : delta.outputTokens;\n\tconst cacheReadTokens = delta.cacheReadTokens === undefined ? 0 : delta.cacheReadTokens;\n\tconst cacheWriteTokens = delta.cacheWriteTokens === undefined ? 0 : delta.cacheWriteTokens;\n\tconst totalTokens =\n\t\tdelta.totalTokens === undefined\n\t\t\t? inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens\n\t\t\t: delta.totalTokens;\n\tconst costUsd = delta.costUsd === undefined ? 0 : delta.costUsd;\n\tif (\n\t\t!Number.isSafeInteger(inputTokens) ||\n\t\tinputTokens < 0 ||\n\t\t!Number.isSafeInteger(outputTokens) ||\n\t\toutputTokens < 0 ||\n\t\t!Number.isSafeInteger(cacheReadTokens) ||\n\t\tcacheReadTokens < 0 ||\n\t\t!Number.isSafeInteger(cacheWriteTokens) ||\n\t\tcacheWriteTokens < 0 ||\n\t\t!Number.isSafeInteger(totalTokens) ||\n\t\ttotalTokens < 0 ||\n\t\t!Number.isFinite(costUsd) ||\n\t\tcostUsd < 0\n\t) {\n\t\tthrow new Error(\n\t\t\t\"CapabilityGateway: usage delta must contain finite non-negative values and safe-integer token counts.\",\n\t\t);\n\t}\n\treturn { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, totalTokens, costUsd };\n}\n\n/**\n * Per-invocation enforcement for an already-compiled grant. The compiler controls what may be\n * loaded; this gateway independently rechecks authority, path scope, expiry, and cumulative budget\n * immediately before execution.\n */\nexport class CapabilityGateway {\n\tprivate readonly grant: ExecutionGrant;\n\tprivate readonly cwd: string;\n\tprivate readonly now: () => number;\n\tprivate readonly onAudit?: (record: GatewayAuditRecord) => void;\n\tprivate readonly sharedBudget?: SharedCapabilityBudget;\n\tprivate readonly startedAt: number;\n\tprivate readonly initialWallClockMs: number;\n\tprivate toolCalls: number;\n\tprivate inputTokens: number;\n\tprivate outputTokens: number;\n\tprivate cacheReadTokens: number;\n\tprivate cacheWriteTokens: number;\n\tprivate totalTokens: number;\n\tprivate costUsd: number;\n\n\tconstructor(options: CapabilityGatewayOptions) {\n\t\tconst initialUsage: GatewayInitialUsage = options.initialUsage ?? {\n\t\t\ttoolCalls: 0,\n\t\t\tinputTokens: 0,\n\t\t\toutputTokens: 0,\n\t\t\tcacheReadTokens: 0,\n\t\t\tcacheWriteTokens: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcostUsd: 0,\n\t\t\tactiveWallClockMs: 0,\n\t\t};\n\t\tvalidateInitialUsage(initialUsage);\n\t\tthis.grant = options.grant;\n\t\tthis.cwd = options.cwd;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.onAudit = options.onAudit;\n\t\tthis.sharedBudget = options.sharedBudget;\n\t\tthis.startedAt = this.currentTime();\n\t\tthis.initialWallClockMs = initialUsage.activeWallClockMs;\n\t\tthis.toolCalls = initialUsage.toolCalls;\n\t\tthis.inputTokens = initialUsage.inputTokens;\n\t\tthis.outputTokens = initialUsage.outputTokens;\n\t\tthis.cacheReadTokens = initialUsage.cacheReadTokens;\n\t\tthis.cacheWriteTokens = initialUsage.cacheWriteTokens;\n\t\tthis.totalTokens = initialUsage.totalTokens;\n\t\tthis.costUsd = initialUsage.costUsd;\n\t\tthis.sharedBudget?.recordAttemptUsage(this.getUsage());\n\t}\n\n\tasync execute<T>(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tinvoke: () => Promise<T> | T,\n\t): Promise<T> {\n\t\tthis.authorizeToolCall(manifest, toolName, params);\n\t\treturn await invoke();\n\t}\n\n\t/** Immediate pre-execution authorization for runtimes that expose a before-tool-call hook. */\n\tauthorizeToolCall(manifest: ToolCapabilityManifest, toolName: string, params: unknown): void {\n\t\tconst now = this.currentTime();\n\t\tconst wallClockMs = this.wallClockMsAt(now);\n\t\tthis.authorize(manifest, toolName, params, now, wallClockMs);\n\t\tconst toolCalls = this.toolCalls + 1;\n\t\tif (!Number.isSafeInteger(toolCalls)) {\n\t\t\tthrow new Error(\"CapabilityGateway: tool-call count would exceed safe cumulative usage bounds.\");\n\t\t}\n\t\tthis.toolCalls = toolCalls;\n\t\tthis.sharedBudget?.recordAttemptUsage(this.getUsage());\n\t\tthis.audit(toolName, \"allow\", \"allowed\");\n\t}\n\n\trecordUsage(delta: GatewayUsageDelta): void {\n\t\tconst validated = validatedUsageDelta(delta);\n\t\tconst inputTokens = this.inputTokens + validated.inputTokens;\n\t\tconst outputTokens = this.outputTokens + validated.outputTokens;\n\t\tconst cacheReadTokens = this.cacheReadTokens + validated.cacheReadTokens;\n\t\tconst cacheWriteTokens = this.cacheWriteTokens + validated.cacheWriteTokens;\n\t\tconst totalTokens = this.totalTokens + validated.totalTokens;\n\t\tconst costUsd = this.costUsd + validated.costUsd;\n\t\tif (\n\t\t\t!Number.isSafeInteger(inputTokens) ||\n\t\t\t!Number.isSafeInteger(outputTokens) ||\n\t\t\t!Number.isSafeInteger(cacheReadTokens) ||\n\t\t\t!Number.isSafeInteger(cacheWriteTokens) ||\n\t\t\t!Number.isSafeInteger(totalTokens) ||\n\t\t\t!Number.isFinite(costUsd)\n\t\t) {\n\t\t\tthrow new Error(\"CapabilityGateway: usage delta would exceed safe cumulative usage bounds.\");\n\t\t}\n\t\tthis.inputTokens = inputTokens;\n\t\tthis.outputTokens = outputTokens;\n\t\tthis.cacheReadTokens = cacheReadTokens;\n\t\tthis.cacheWriteTokens = cacheWriteTokens;\n\t\tthis.totalTokens = totalTokens;\n\t\tthis.costUsd = costUsd;\n\t\tthis.sharedBudget?.recordAttemptUsage(this.getUsage());\n\t}\n\n\t/** Enforce resumed cumulative budgets before a provider request that has no tool-call boundary. */\n\tassertBudgetAvailable(subject = \"provider\"): void {\n\t\tthis.enforceBudget(subject, this.wallClockMsAt(this.currentTime()));\n\t\tthis.sharedBudget?.assertBudgetAvailable(subject);\n\t}\n\n\tasync reserveProviderBudget(\n\t\trequestedMaxTokens: number,\n\t\tsubject = \"provider\",\n\t\tsignal?: AbortSignal,\n\t): Promise<ProviderBudgetReservation> {\n\t\tif (!Number.isSafeInteger(requestedMaxTokens) || requestedMaxTokens <= 0) {\n\t\t\tthrow new Error(\"CapabilityGateway: provider token reservation must be a positive safe integer.\");\n\t\t}\n\t\tthis.assertBudgetAvailable(subject);\n\t\tconst localRemaining = this.remainingAttemptTokenBudget();\n\t\tconst maxTokens = Math.min(requestedMaxTokens, localRemaining ?? requestedMaxTokens);\n\t\tif (maxTokens <= 0) {\n\t\t\tthis.deny(subject, \"token_budget_exhausted\", \"Token budget exhausted.\");\n\t\t}\n\t\tif (this.sharedBudget) return this.sharedBudget.reserveProviderBudget(maxTokens, subject, signal);\n\t\tif (signal?.aborted) throw signal.reason;\n\t\treturn {\n\t\t\tmaxTokens,\n\t\t\trelease: () => undefined,\n\t\t};\n\t}\n\n\tremainingAttemptTokenBudget(): number | undefined {\n\t\tconst maximum = this.grant.budget.maxTokens;\n\t\treturn maximum === undefined ? undefined : Math.max(0, maximum - budgetedTokens(this.getUsage()));\n\t}\n\n\tremainingTokenBudget(): number | undefined {\n\t\tconst localRemaining = this.remainingAttemptTokenBudget();\n\t\tconst sharedRemaining = this.sharedBudget?.remainingTokens();\n\t\tif (localRemaining === undefined) return sharedRemaining;\n\t\tif (sharedRemaining === undefined) return localRemaining;\n\t\treturn Math.min(localRemaining, sharedRemaining);\n\t}\n\n\tgetUsage(): GatewayUsageSnapshot {\n\t\treturn {\n\t\t\ttoolCalls: this.toolCalls,\n\t\t\tinputTokens: this.inputTokens,\n\t\t\toutputTokens: this.outputTokens,\n\t\t\tcacheReadTokens: this.cacheReadTokens,\n\t\t\tcacheWriteTokens: this.cacheWriteTokens,\n\t\t\ttotalTokens: this.totalTokens,\n\t\t\tcostUsd: this.costUsd,\n\t\t\twallClockMs: this.wallClockMsAt(this.currentTime()),\n\t\t};\n\t}\n\n\tprivate authorize(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tnow: number,\n\t\twallClockMs: number,\n\t): void {\n\t\tif (this.grant.expiresAt && Date.parse(this.grant.expiresAt) <= now) {\n\t\t\tthis.deny(toolName, \"grant_expired\", `Execution grant '${this.grant.grantId}' expired.`);\n\t\t}\n\t\tif (!this.grant.allowedTools.includes(toolName)) {\n\t\t\tthis.deny(toolName, \"tool_not_granted\", `Tool '${toolName}' is not in grant '${this.grant.grantId}'.`);\n\t\t}\n\t\tif (manifest.toolName !== toolName) {\n\t\t\tthis.deny(toolName, \"manifest_name_mismatch\", `Tool '${toolName}' does not match its capability manifest.`);\n\t\t}\n\t\tif (!manifest.capabilities.every((capability) => this.grant.capabilities.includes(capability))) {\n\t\t\tthis.deny(toolName, \"capability_not_granted\", `Tool '${toolName}' requires an ungranted capability.`);\n\t\t}\n\t\tthis.enforceBudget(toolName, wallClockMs);\n\t\tthis.sharedBudget?.assertBudgetAvailable(toolName);\n\n\t\tif (manifest.capabilities.some((capability) => PATH_CAPABILITIES.has(capability))) {\n\t\t\tconst allowedPaths = manifest.capabilities.some((capability) => WRITE_CAPABILITIES.has(capability))\n\t\t\t\t? this.grant.writePaths\n\t\t\t\t: this.grant.readPaths;\n\t\t\tconst paths = extractPathArguments(params);\n\t\t\tif (paths.length === 0) {\n\t\t\t\tthis.deny(toolName, \"path_argument_required\", `Tool '${toolName}' requires an explicit path argument.`);\n\t\t\t}\n\t\t\tfor (const rawPath of paths) {\n\t\t\t\tif (!this.pathAllowed(rawPath, allowedPaths)) {\n\t\t\t\t\tthis.deny(toolName, \"path_outside_scope\", `Path '${rawPath}' is outside grant '${this.grant.grantId}'.`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate enforceBudget(toolName: string, wallClockMs: number): void {\n\t\tconst budget = this.grant.budget;\n\t\tif (budget.maxToolCalls !== undefined && this.toolCalls >= budget.maxToolCalls) {\n\t\t\tthis.deny(toolName, \"tool_call_budget_exhausted\", \"Tool-call budget exhausted.\");\n\t\t}\n\t\tif (budget.maxTokens !== undefined && budgetedTokens(this.getUsage()) >= budget.maxTokens) {\n\t\t\tthis.deny(toolName, \"token_budget_exhausted\", \"Token budget exhausted.\");\n\t\t}\n\t\tif (budget.maxCostUsd !== undefined && this.costUsd >= budget.maxCostUsd) {\n\t\t\tthis.deny(toolName, \"cost_budget_exhausted\", \"Cost budget exhausted.\");\n\t\t}\n\t\tif (budget.maxWallClockMs !== undefined && wallClockMs >= budget.maxWallClockMs) {\n\t\t\tthis.deny(toolName, \"wall_clock_budget_exhausted\", \"Wall-clock budget exhausted.\");\n\t\t}\n\t}\n\n\tprivate currentTime(): number {\n\t\tconst now = this.now();\n\t\tif (!Number.isFinite(now)) {\n\t\t\tthrow new Error(\"CapabilityGateway: clock source must return a finite time.\");\n\t\t}\n\t\treturn now;\n\t}\n\n\tprivate wallClockMsAt(now: number): number {\n\t\tconst elapsed = now - this.startedAt;\n\t\tif (!Number.isFinite(elapsed)) {\n\t\t\tthrow new Error(\"CapabilityGateway: active wall-clock elapsed time must be finite.\");\n\t\t}\n\t\tconst wallClockMs = this.initialWallClockMs + Math.max(0, elapsed);\n\t\tif (!Number.isFinite(wallClockMs) || wallClockMs < 0) {\n\t\t\tthrow new Error(\"CapabilityGateway: accumulated wall-clock usage must be finite and non-negative.\");\n\t\t}\n\t\treturn wallClockMs;\n\t}\n\n\tprivate pathAllowed(rawPath: string, allowedPaths: readonly string[]): boolean {\n\t\tconst target = resolveRealPath(this.cwd, rawPath);\n\t\tif (!target || allowedPaths.length === 0) return false;\n\t\tfor (const denied of this.grant.deniedPaths) {\n\t\t\tconst root = resolveRealPath(this.cwd, denied);\n\t\t\tif (root && isPathWithinScope(target, root)) return false;\n\t\t}\n\t\treturn allowedPaths.some((allowed) => {\n\t\t\tconst root = resolveRealPath(this.cwd, allowed);\n\t\t\treturn root !== undefined && isPathWithinScope(target, root);\n\t\t});\n\t}\n\n\tprivate deny(toolName: string, reasonCode: GatewayDecisionCode, message: string): never {\n\t\tthis.audit(toolName, \"deny\", reasonCode);\n\t\tthrow new CapabilityGatewayDeniedError(reasonCode, message);\n\t}\n\n\tprivate audit(toolName: string, outcome: \"allow\" | \"deny\", reasonCode: GatewayDecisionCode): void {\n\t\tthis.onAudit?.({\n\t\t\tgrantId: this.grant.grantId,\n\t\t\tattemptId: this.grant.attemptId,\n\t\t\ttoolName,\n\t\t\toutcome,\n\t\t\treasonCode,\n\t\t\tat: new Date(this.currentTime()).toISOString(),\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"capability-gateway.js","sourceRoot":"","sources":["../../../src/core/orchestration/capability-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport { BoundedCompletionFailureError } from \"../autonomy/bounded-completion.ts\";\nimport { extractPathArguments } from \"../autonomy/envelope-enforcement.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"../autonomy/path-scope.ts\";\nimport { validateAttemptUsageSnapshot } from \"./attempt-usage.ts\";\nimport type { AttemptUsageSnapshot, ExecutionGrant, HarnessCapability, ToolCapabilityManifest } from \"./contracts.ts\";\n\nexport type GatewayDecisionCode =\n\t| \"allowed\"\n\t| \"grant_expired\"\n\t| \"tool_not_granted\"\n\t| \"manifest_name_mismatch\"\n\t| \"capability_not_granted\"\n\t| \"path_argument_required\"\n\t| \"path_outside_scope\"\n\t| \"tool_call_budget_exhausted\"\n\t| \"token_budget_exhausted\"\n\t| \"cost_budget_exhausted\"\n\t| \"wall_clock_budget_exhausted\";\n\nexport interface GatewayAuditRecord {\n\tgrantId: string;\n\tattemptId: string;\n\ttoolName: string;\n\toutcome: \"allow\" | \"deny\";\n\treasonCode: GatewayDecisionCode;\n\tat: string;\n}\n\nexport interface CapabilityGatewayOptions {\n\tgrant: ExecutionGrant;\n\tcwd: string;\n\t/** Durable active usage carried forward when an attempt resumes in a new process. */\n\tinitialUsage?: GatewayInitialUsage;\n\tnow?: () => number;\n\tonAudit?: (record: GatewayAuditRecord) => void;\n\t/** Session-tree cumulative budget owner. Per-attempt grants remain independently enforced. */\n\tsharedBudget?: SharedCapabilityBudget;\n}\n\nexport interface GatewayUsageDelta {\n\tinputTokens?: number;\n\toutputTokens?: number;\n\tcacheReadTokens?: number;\n\tcacheWriteTokens?: number;\n\t/** Provider-authoritative total. Defaults to the supplied detail sum when omitted. */\n\ttotalTokens?: number;\n\tcostUsd?: number;\n}\n\nexport interface GatewayUsageSnapshot {\n\ttoolCalls: number;\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n\tcostUsd: number;\n\twallClockMs: number;\n}\n\nexport interface ProviderBudgetReservation {\n\tmaxTokens: number;\n\trelease(): void;\n}\n\nexport interface SharedCapabilityBudget {\n\tassertBudgetAvailable(subject: string): void;\n\trecordAttemptUsage(usage: GatewayUsageSnapshot): void;\n\tremainingTokens(): number | undefined;\n\treserveProviderBudget(\n\t\trequestedMaxTokens: number,\n\t\tsubject: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<ProviderBudgetReservation>;\n}\n\n/** Persistable cumulative usage from a prior active segment; excludes restart downtime. */\nexport type GatewayInitialUsage = AttemptUsageSnapshot;\n\n/**\n * Weight applied to prompt-cache reads when charging token budgets. Cache reads re-bill the\n * same context on every request (a worker's fixed system prompt alone is thousands of tokens),\n * so charging them at face value turns `maxTokens` into a request counter: a 9k grant died in\n * 2-3 responses while doing ~500 tokens of real work per response (field session 019fd4dc).\n * The weight mirrors the typical cache-read price ratio; money stays bounded by `maxCostUsd`.\n */\nexport const CACHE_READ_BUDGET_WEIGHT = 0.1;\n\n/**\n * Tokens charged against `maxTokens` budgets: real work at face value, cache reads discounted.\n * A provider-authoritative total above the detail sum is unattributed usage and charges fully.\n */\nexport function budgetedTokens(usage: {\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n}): number {\n\tconst detailed = usage.inputTokens + usage.outputTokens + usage.cacheWriteTokens;\n\tconst unattributed = Math.max(0, usage.totalTokens - detailed - usage.cacheReadTokens);\n\treturn detailed + unattributed + Math.ceil(usage.cacheReadTokens * CACHE_READ_BUDGET_WEIGHT);\n}\n\nconst BUDGET_EXHAUSTION_DECISION_CODES: ReadonlySet<GatewayDecisionCode> = new Set([\n\t\"tool_call_budget_exhausted\",\n\t\"token_budget_exhausted\",\n\t\"cost_budget_exhausted\",\n\t\"wall_clock_budget_exhausted\",\n]);\n\nexport class CapabilityGatewayDeniedError extends BoundedCompletionFailureError {\n\treadonly reasonCode: GatewayDecisionCode;\n\n\tconstructor(reasonCode: GatewayDecisionCode, message: string) {\n\t\tsuper(BUDGET_EXHAUSTION_DECISION_CODES.has(reasonCode) ? \"budget_exhausted\" : \"failed\", reasonCode, message);\n\t\tthis.name = \"CapabilityGatewayDeniedError\";\n\t\tthis.reasonCode = reasonCode;\n\t}\n}\n\nconst PATH_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\n\t\"filesystem.read\",\n\t\"filesystem.write\",\n\t\"worktree.read\",\n\t\"worktree.mutate\",\n]);\n\nconst WRITE_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\"filesystem.write\", \"worktree.mutate\"]);\n\nfunction resolveRealPath(cwd: string, value: string): string | undefined {\n\ttry {\n\t\treturn safeRealpathSync(resolve(cwd, value));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction validateInitialUsage(usage: GatewayInitialUsage): void {\n\ttry {\n\t\tvalidateAttemptUsageSnapshot(usage, \"CapabilityGateway: initial usage\");\n\t} catch {\n\t\tthrow new Error(\n\t\t\t\"CapabilityGateway: initial usage must contain finite non-negative values and safe-integer counts.\",\n\t\t);\n\t}\n}\n\nfunction validatedUsageDelta(delta: GatewayUsageDelta): Required<GatewayUsageDelta> {\n\tconst inputTokens = delta.inputTokens === undefined ? 0 : delta.inputTokens;\n\tconst outputTokens = delta.outputTokens === undefined ? 0 : delta.outputTokens;\n\tconst cacheReadTokens = delta.cacheReadTokens === undefined ? 0 : delta.cacheReadTokens;\n\tconst cacheWriteTokens = delta.cacheWriteTokens === undefined ? 0 : delta.cacheWriteTokens;\n\tconst totalTokens =\n\t\tdelta.totalTokens === undefined\n\t\t\t? inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens\n\t\t\t: delta.totalTokens;\n\tconst costUsd = delta.costUsd === undefined ? 0 : delta.costUsd;\n\tif (\n\t\t!Number.isSafeInteger(inputTokens) ||\n\t\tinputTokens < 0 ||\n\t\t!Number.isSafeInteger(outputTokens) ||\n\t\toutputTokens < 0 ||\n\t\t!Number.isSafeInteger(cacheReadTokens) ||\n\t\tcacheReadTokens < 0 ||\n\t\t!Number.isSafeInteger(cacheWriteTokens) ||\n\t\tcacheWriteTokens < 0 ||\n\t\t!Number.isSafeInteger(totalTokens) ||\n\t\ttotalTokens < 0 ||\n\t\t!Number.isFinite(costUsd) ||\n\t\tcostUsd < 0\n\t) {\n\t\tthrow new Error(\n\t\t\t\"CapabilityGateway: usage delta must contain finite non-negative values and safe-integer token counts.\",\n\t\t);\n\t}\n\treturn { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, totalTokens, costUsd };\n}\n\n/**\n * Per-invocation enforcement for an already-compiled grant. The compiler controls what may be\n * loaded; this gateway independently rechecks authority, path scope, expiry, and cumulative budget\n * immediately before execution.\n */\nexport class CapabilityGateway {\n\tprivate readonly grant: ExecutionGrant;\n\tprivate readonly cwd: string;\n\tprivate readonly now: () => number;\n\tprivate readonly onAudit?: (record: GatewayAuditRecord) => void;\n\tprivate readonly sharedBudget?: SharedCapabilityBudget;\n\tprivate readonly startedAt: number;\n\tprivate readonly initialWallClockMs: number;\n\tprivate toolCalls: number;\n\tprivate inputTokens: number;\n\tprivate outputTokens: number;\n\tprivate cacheReadTokens: number;\n\tprivate cacheWriteTokens: number;\n\tprivate totalTokens: number;\n\tprivate costUsd: number;\n\n\tconstructor(options: CapabilityGatewayOptions) {\n\t\tconst initialUsage: GatewayInitialUsage = options.initialUsage ?? {\n\t\t\ttoolCalls: 0,\n\t\t\tinputTokens: 0,\n\t\t\toutputTokens: 0,\n\t\t\tcacheReadTokens: 0,\n\t\t\tcacheWriteTokens: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcostUsd: 0,\n\t\t\tactiveWallClockMs: 0,\n\t\t};\n\t\tvalidateInitialUsage(initialUsage);\n\t\tthis.grant = options.grant;\n\t\tthis.cwd = options.cwd;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.onAudit = options.onAudit;\n\t\tthis.sharedBudget = options.sharedBudget;\n\t\tthis.startedAt = this.currentTime();\n\t\tthis.initialWallClockMs = initialUsage.activeWallClockMs;\n\t\tthis.toolCalls = initialUsage.toolCalls;\n\t\tthis.inputTokens = initialUsage.inputTokens;\n\t\tthis.outputTokens = initialUsage.outputTokens;\n\t\tthis.cacheReadTokens = initialUsage.cacheReadTokens;\n\t\tthis.cacheWriteTokens = initialUsage.cacheWriteTokens;\n\t\tthis.totalTokens = initialUsage.totalTokens;\n\t\tthis.costUsd = initialUsage.costUsd;\n\t\tthis.sharedBudget?.recordAttemptUsage(this.getUsage());\n\t}\n\n\tasync execute<T>(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tinvoke: () => Promise<T> | T,\n\t): Promise<T> {\n\t\tthis.authorizeToolCall(manifest, toolName, params);\n\t\treturn await invoke();\n\t}\n\n\t/** Immediate pre-execution authorization for runtimes that expose a before-tool-call hook. */\n\tauthorizeToolCall(manifest: ToolCapabilityManifest, toolName: string, params: unknown): void {\n\t\tconst now = this.currentTime();\n\t\tconst wallClockMs = this.wallClockMsAt(now);\n\t\tthis.authorize(manifest, toolName, params, now, wallClockMs);\n\t\tconst toolCalls = this.toolCalls + 1;\n\t\tif (!Number.isSafeInteger(toolCalls)) {\n\t\t\tthrow new Error(\"CapabilityGateway: tool-call count would exceed safe cumulative usage bounds.\");\n\t\t}\n\t\tthis.toolCalls = toolCalls;\n\t\tthis.sharedBudget?.recordAttemptUsage(this.getUsage());\n\t\tthis.audit(toolName, \"allow\", \"allowed\");\n\t}\n\n\trecordUsage(delta: GatewayUsageDelta): void {\n\t\tconst validated = validatedUsageDelta(delta);\n\t\tconst inputTokens = this.inputTokens + validated.inputTokens;\n\t\tconst outputTokens = this.outputTokens + validated.outputTokens;\n\t\tconst cacheReadTokens = this.cacheReadTokens + validated.cacheReadTokens;\n\t\tconst cacheWriteTokens = this.cacheWriteTokens + validated.cacheWriteTokens;\n\t\tconst totalTokens = this.totalTokens + validated.totalTokens;\n\t\tconst costUsd = this.costUsd + validated.costUsd;\n\t\tif (\n\t\t\t!Number.isSafeInteger(inputTokens) ||\n\t\t\t!Number.isSafeInteger(outputTokens) ||\n\t\t\t!Number.isSafeInteger(cacheReadTokens) ||\n\t\t\t!Number.isSafeInteger(cacheWriteTokens) ||\n\t\t\t!Number.isSafeInteger(totalTokens) ||\n\t\t\t!Number.isFinite(costUsd)\n\t\t) {\n\t\t\tthrow new Error(\"CapabilityGateway: usage delta would exceed safe cumulative usage bounds.\");\n\t\t}\n\t\tthis.inputTokens = inputTokens;\n\t\tthis.outputTokens = outputTokens;\n\t\tthis.cacheReadTokens = cacheReadTokens;\n\t\tthis.cacheWriteTokens = cacheWriteTokens;\n\t\tthis.totalTokens = totalTokens;\n\t\tthis.costUsd = costUsd;\n\t\tthis.sharedBudget?.recordAttemptUsage(this.getUsage());\n\t}\n\n\t/** Enforce resumed cumulative budgets before a provider request that has no tool-call boundary. */\n\tassertBudgetAvailable(subject = \"provider\"): void {\n\t\tthis.enforceBudget(subject, this.wallClockMsAt(this.currentTime()));\n\t\tthis.sharedBudget?.assertBudgetAvailable(subject);\n\t}\n\n\tasync reserveProviderBudget(\n\t\trequestedMaxTokens: number,\n\t\tsubject = \"provider\",\n\t\tsignal?: AbortSignal,\n\t): Promise<ProviderBudgetReservation> {\n\t\tif (!Number.isSafeInteger(requestedMaxTokens) || requestedMaxTokens <= 0) {\n\t\t\tthrow new Error(\"CapabilityGateway: provider token reservation must be a positive safe integer.\");\n\t\t}\n\t\tthis.assertBudgetAvailable(subject);\n\t\tconst localRemaining = this.remainingAttemptTokenBudget();\n\t\tconst maxTokens = Math.min(requestedMaxTokens, localRemaining ?? requestedMaxTokens);\n\t\tif (maxTokens <= 0) {\n\t\t\tthis.deny(subject, \"token_budget_exhausted\", \"Token budget exhausted.\");\n\t\t}\n\t\tif (this.sharedBudget) return this.sharedBudget.reserveProviderBudget(maxTokens, subject, signal);\n\t\tif (signal?.aborted) throw signal.reason;\n\t\treturn {\n\t\t\tmaxTokens,\n\t\t\trelease: () => undefined,\n\t\t};\n\t}\n\n\tremainingAttemptTokenBudget(): number | undefined {\n\t\tconst maximum = this.grant.budget.maxTokens;\n\t\treturn maximum === undefined ? undefined : Math.max(0, maximum - budgetedTokens(this.getUsage()));\n\t}\n\n\tremainingTokenBudget(): number | undefined {\n\t\tconst localRemaining = this.remainingAttemptTokenBudget();\n\t\tconst sharedRemaining = this.sharedBudget?.remainingTokens();\n\t\tif (localRemaining === undefined) return sharedRemaining;\n\t\tif (sharedRemaining === undefined) return localRemaining;\n\t\treturn Math.min(localRemaining, sharedRemaining);\n\t}\n\n\tgetUsage(): GatewayUsageSnapshot {\n\t\treturn {\n\t\t\ttoolCalls: this.toolCalls,\n\t\t\tinputTokens: this.inputTokens,\n\t\t\toutputTokens: this.outputTokens,\n\t\t\tcacheReadTokens: this.cacheReadTokens,\n\t\t\tcacheWriteTokens: this.cacheWriteTokens,\n\t\t\ttotalTokens: this.totalTokens,\n\t\t\tcostUsd: this.costUsd,\n\t\t\twallClockMs: this.wallClockMsAt(this.currentTime()),\n\t\t};\n\t}\n\n\tprivate authorize(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tnow: number,\n\t\twallClockMs: number,\n\t): void {\n\t\tif (this.grant.expiresAt && Date.parse(this.grant.expiresAt) <= now) {\n\t\t\tthis.deny(toolName, \"grant_expired\", `Execution grant '${this.grant.grantId}' expired.`);\n\t\t}\n\t\tif (!this.grant.allowedTools.includes(toolName)) {\n\t\t\tthis.deny(toolName, \"tool_not_granted\", `Tool '${toolName}' is not in grant '${this.grant.grantId}'.`);\n\t\t}\n\t\tif (manifest.toolName !== toolName) {\n\t\t\tthis.deny(toolName, \"manifest_name_mismatch\", `Tool '${toolName}' does not match its capability manifest.`);\n\t\t}\n\t\tif (!manifest.capabilities.every((capability) => this.grant.capabilities.includes(capability))) {\n\t\t\tthis.deny(toolName, \"capability_not_granted\", `Tool '${toolName}' requires an ungranted capability.`);\n\t\t}\n\t\tthis.enforceBudget(toolName, wallClockMs);\n\t\tthis.sharedBudget?.assertBudgetAvailable(toolName);\n\n\t\tif (manifest.capabilities.some((capability) => PATH_CAPABILITIES.has(capability))) {\n\t\t\tconst allowedPaths = manifest.capabilities.some((capability) => WRITE_CAPABILITIES.has(capability))\n\t\t\t\t? this.grant.writePaths\n\t\t\t\t: this.grant.readPaths;\n\t\t\tconst paths = extractPathArguments(params);\n\t\t\tif (paths.length === 0) {\n\t\t\t\tthis.deny(toolName, \"path_argument_required\", `Tool '${toolName}' requires an explicit path argument.`);\n\t\t\t}\n\t\t\tfor (const rawPath of paths) {\n\t\t\t\tif (!this.pathAllowed(rawPath, allowedPaths)) {\n\t\t\t\t\tthis.deny(toolName, \"path_outside_scope\", `Path '${rawPath}' is outside grant '${this.grant.grantId}'.`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate enforceBudget(toolName: string, wallClockMs: number): void {\n\t\tconst budget = this.grant.budget;\n\t\tif (budget.maxToolCalls !== undefined && this.toolCalls >= budget.maxToolCalls) {\n\t\t\tthis.deny(toolName, \"tool_call_budget_exhausted\", \"Tool-call budget exhausted.\");\n\t\t}\n\t\tif (budget.maxTokens !== undefined && budgetedTokens(this.getUsage()) >= budget.maxTokens) {\n\t\t\tthis.deny(toolName, \"token_budget_exhausted\", \"Token budget exhausted.\");\n\t\t}\n\t\tif (budget.maxCostUsd !== undefined && this.costUsd >= budget.maxCostUsd) {\n\t\t\tthis.deny(toolName, \"cost_budget_exhausted\", \"Cost budget exhausted.\");\n\t\t}\n\t\tif (budget.maxWallClockMs !== undefined && wallClockMs >= budget.maxWallClockMs) {\n\t\t\tthis.deny(toolName, \"wall_clock_budget_exhausted\", \"Wall-clock budget exhausted.\");\n\t\t}\n\t}\n\n\tprivate currentTime(): number {\n\t\tconst now = this.now();\n\t\tif (!Number.isFinite(now)) {\n\t\t\tthrow new Error(\"CapabilityGateway: clock source must return a finite time.\");\n\t\t}\n\t\treturn now;\n\t}\n\n\tprivate wallClockMsAt(now: number): number {\n\t\tconst elapsed = now - this.startedAt;\n\t\tif (!Number.isFinite(elapsed)) {\n\t\t\tthrow new Error(\"CapabilityGateway: active wall-clock elapsed time must be finite.\");\n\t\t}\n\t\tconst wallClockMs = this.initialWallClockMs + Math.max(0, elapsed);\n\t\tif (!Number.isFinite(wallClockMs) || wallClockMs < 0) {\n\t\t\tthrow new Error(\"CapabilityGateway: accumulated wall-clock usage must be finite and non-negative.\");\n\t\t}\n\t\treturn wallClockMs;\n\t}\n\n\tprivate pathAllowed(rawPath: string, allowedPaths: readonly string[]): boolean {\n\t\tconst target = resolveRealPath(this.cwd, rawPath);\n\t\tif (!target || allowedPaths.length === 0) return false;\n\t\tfor (const denied of this.grant.deniedPaths) {\n\t\t\tconst root = resolveRealPath(this.cwd, denied);\n\t\t\tif (root && isPathWithinScope(target, root)) return false;\n\t\t}\n\t\treturn allowedPaths.some((allowed) => {\n\t\t\tconst root = resolveRealPath(this.cwd, allowed);\n\t\t\treturn root !== undefined && isPathWithinScope(target, root);\n\t\t});\n\t}\n\n\tprivate deny(toolName: string, reasonCode: GatewayDecisionCode, message: string): never {\n\t\tthis.audit(toolName, \"deny\", reasonCode);\n\t\tthrow new CapabilityGatewayDeniedError(reasonCode, message);\n\t}\n\n\tprivate audit(toolName: string, outcome: \"allow\" | \"deny\", reasonCode: GatewayDecisionCode): void {\n\t\tthis.onAudit?.({\n\t\t\tgrantId: this.grant.grantId,\n\t\t\tattemptId: this.grant.attemptId,\n\t\t\ttoolName,\n\t\t\toutcome,\n\t\t\treasonCode,\n\t\t\tat: new Date(this.currentTime()).toISOString(),\n\t\t});\n\t}\n}\n"]}
|