@caupulican/pi-adaptative 0.81.41 → 0.81.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +5 -1
  3. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +3 -3
  4. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +8 -0
  5. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +26 -3
  6. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +3 -0
  7. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +4 -1
  8. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +2 -1
  9. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +2 -0
  10. package/dist/core/agent-paths.d.ts +32 -0
  11. package/dist/core/agent-paths.d.ts.map +1 -1
  12. package/dist/core/agent-paths.js +65 -0
  13. package/dist/core/agent-paths.js.map +1 -1
  14. package/dist/core/agent-session-contracts.d.ts +19 -1
  15. package/dist/core/agent-session-contracts.d.ts.map +1 -1
  16. package/dist/core/agent-session-contracts.js.map +1 -1
  17. package/dist/core/agent-session.d.ts.map +1 -1
  18. package/dist/core/agent-session.js +4 -9
  19. package/dist/core/agent-session.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  24. package/dist/core/autonomy/envelope-enforcement.js +4 -9
  25. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  26. package/dist/core/autonomy/lane-tool-surface.d.ts +3 -1
  27. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  28. package/dist/core/autonomy/lane-tool-surface.js +17 -4
  29. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  30. package/dist/core/autonomy/path-scope.d.ts +14 -0
  31. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  32. package/dist/core/autonomy/path-scope.js +37 -6
  33. package/dist/core/autonomy/path-scope.js.map +1 -1
  34. package/dist/core/background-lane-controller.d.ts +27 -1
  35. package/dist/core/background-lane-controller.d.ts.map +1 -1
  36. package/dist/core/background-lane-controller.js +32 -1
  37. package/dist/core/background-lane-controller.js.map +1 -1
  38. package/dist/core/delegation/managed-lane-controller.d.ts +11 -1
  39. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -1
  40. package/dist/core/delegation/managed-lane-controller.js +84 -41
  41. package/dist/core/delegation/managed-lane-controller.js.map +1 -1
  42. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -1
  43. package/dist/core/delegation/session-worker-claim.js +4 -3
  44. package/dist/core/delegation/session-worker-claim.js.map +1 -1
  45. package/dist/core/delegation/worker-action-journal.d.ts +73 -0
  46. package/dist/core/delegation/worker-action-journal.d.ts.map +1 -0
  47. package/dist/core/delegation/worker-action-journal.js +297 -0
  48. package/dist/core/delegation/worker-action-journal.js.map +1 -0
  49. package/dist/core/delegation/worker-actions.d.ts +36 -1
  50. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  51. package/dist/core/delegation/worker-actions.js +171 -46
  52. package/dist/core/delegation/worker-actions.js.map +1 -1
  53. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +75 -0
  54. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -0
  55. package/dist/core/delegation/worker-agent-control-coordinator.js +218 -0
  56. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -0
  57. package/dist/core/delegation/worker-agent-control.d.ts +66 -0
  58. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -0
  59. package/dist/core/delegation/worker-agent-control.js +163 -0
  60. package/dist/core/delegation/worker-agent-control.js.map +1 -0
  61. package/dist/core/delegation/worker-attempt-executor.d.ts +80 -0
  62. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -0
  63. package/dist/core/delegation/worker-attempt-executor.js +343 -0
  64. package/dist/core/delegation/worker-attempt-executor.js.map +1 -0
  65. package/dist/core/delegation/worker-claim.d.ts +30 -0
  66. package/dist/core/delegation/worker-claim.d.ts.map +1 -1
  67. package/dist/core/delegation/worker-claim.js +205 -53
  68. package/dist/core/delegation/worker-claim.js.map +1 -1
  69. package/dist/core/delegation/worker-conversation-store.d.ts +116 -0
  70. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -0
  71. package/dist/core/delegation/worker-conversation-store.js +442 -0
  72. package/dist/core/delegation/worker-conversation-store.js.map +1 -0
  73. package/dist/core/delegation/worker-delegation-controller.d.ts +21 -5
  74. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  75. package/dist/core/delegation/worker-delegation-controller.js +386 -251
  76. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  77. package/dist/core/delegation/worker-delegation-request.d.ts +14 -0
  78. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  79. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  80. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +4 -2
  81. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
  82. package/dist/core/delegation/worker-dispatch-scheduler.js +16 -6
  83. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
  84. package/dist/core/delegation/worker-execution-policy.d.ts +2 -1
  85. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  86. package/dist/core/delegation/worker-execution-policy.js +5 -7
  87. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  88. package/dist/core/delegation/worker-lifecycle.d.ts +44 -1
  89. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  90. package/dist/core/delegation/worker-lifecycle.js +120 -0
  91. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  92. package/dist/core/delegation/worker-process-owner.d.ts +18 -0
  93. package/dist/core/delegation/worker-process-owner.d.ts.map +1 -0
  94. package/dist/core/delegation/worker-process-owner.js +54 -0
  95. package/dist/core/delegation/worker-process-owner.js.map +1 -0
  96. package/dist/core/delegation/worker-profile-resolver.d.ts +5 -1
  97. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  98. package/dist/core/delegation/worker-profile-resolver.js +8 -0
  99. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  100. package/dist/core/delegation/worker-recovery-coordinator.d.ts +54 -0
  101. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -0
  102. package/dist/core/delegation/worker-recovery-coordinator.js +181 -0
  103. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -0
  104. package/dist/core/delegation/worker-resource-catalog.d.ts +32 -0
  105. package/dist/core/delegation/worker-resource-catalog.d.ts.map +1 -0
  106. package/dist/core/delegation/worker-resource-catalog.js +116 -0
  107. package/dist/core/delegation/worker-resource-catalog.js.map +1 -0
  108. package/dist/core/delegation/worker-resource-materializer.d.ts +47 -0
  109. package/dist/core/delegation/worker-resource-materializer.d.ts.map +1 -0
  110. package/dist/core/delegation/worker-resource-materializer.js +137 -0
  111. package/dist/core/delegation/worker-resource-materializer.js.map +1 -0
  112. package/dist/core/delegation/worker-runner.d.ts +3 -1
  113. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  114. package/dist/core/delegation/worker-runner.js +189 -54
  115. package/dist/core/delegation/worker-runner.js.map +1 -1
  116. package/dist/core/delegation/worker-terminal-finalizer.d.ts +23 -0
  117. package/dist/core/delegation/worker-terminal-finalizer.d.ts.map +1 -0
  118. package/dist/core/delegation/worker-terminal-finalizer.js +12 -0
  119. package/dist/core/delegation/worker-terminal-finalizer.js.map +1 -0
  120. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +38 -0
  121. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -0
  122. package/dist/core/delegation/worker-write-reservation-coordinator.js +107 -0
  123. package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -0
  124. package/dist/core/delegation/worker-write-reservation.d.ts +90 -0
  125. package/dist/core/delegation/worker-write-reservation.d.ts.map +1 -0
  126. package/dist/core/delegation/worker-write-reservation.js +348 -0
  127. package/dist/core/delegation/worker-write-reservation.js.map +1 -0
  128. package/dist/core/models/model-endpoint.d.ts +3 -0
  129. package/dist/core/models/model-endpoint.d.ts.map +1 -0
  130. package/dist/core/models/model-endpoint.js +11 -0
  131. package/dist/core/models/model-endpoint.js.map +1 -0
  132. package/dist/core/orchestration/agent-resume.d.ts +2 -0
  133. package/dist/core/orchestration/agent-resume.d.ts.map +1 -1
  134. package/dist/core/orchestration/agent-resume.js +7 -0
  135. package/dist/core/orchestration/agent-resume.js.map +1 -1
  136. package/dist/core/orchestration/attempt-usage.d.ts +28 -0
  137. package/dist/core/orchestration/attempt-usage.d.ts.map +1 -0
  138. package/dist/core/orchestration/attempt-usage.js +168 -0
  139. package/dist/core/orchestration/attempt-usage.js.map +1 -0
  140. package/dist/core/orchestration/capability-gateway.d.ts +19 -1
  141. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  142. package/dist/core/orchestration/capability-gateway.js +128 -28
  143. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  144. package/dist/core/orchestration/contracts.d.ts +35 -0
  145. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  146. package/dist/core/orchestration/contracts.js +17 -0
  147. package/dist/core/orchestration/contracts.js.map +1 -1
  148. package/dist/core/orchestration/delegation-ledger.d.ts +17 -2
  149. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  150. package/dist/core/orchestration/delegation-ledger.js +78 -3
  151. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  152. package/dist/core/orchestration/event-store.d.ts +1 -0
  153. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  154. package/dist/core/orchestration/event-store.js +78 -38
  155. package/dist/core/orchestration/event-store.js.map +1 -1
  156. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  157. package/dist/core/orchestration/profile-registry.js +81 -41
  158. package/dist/core/orchestration/profile-registry.js.map +1 -1
  159. package/dist/core/orchestration/profile-store.d.ts.map +1 -1
  160. package/dist/core/orchestration/profile-store.js +18 -4
  161. package/dist/core/orchestration/profile-store.js.map +1 -1
  162. package/dist/core/orchestration/risk-budget.d.ts +2 -0
  163. package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
  164. package/dist/core/orchestration/risk-budget.js +26 -0
  165. package/dist/core/orchestration/risk-budget.js.map +1 -1
  166. package/dist/core/orchestration/task-runtime.d.ts +13 -1
  167. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  168. package/dist/core/orchestration/task-runtime.js +127 -16
  169. package/dist/core/orchestration/task-runtime.js.map +1 -1
  170. package/dist/core/orchestration/worker-execution-contract.d.ts +3 -1
  171. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  172. package/dist/core/orchestration/worker-execution-contract.js +75 -23
  173. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  174. package/dist/core/orchestration/worker-result-adapter.d.ts +9 -3
  175. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -1
  176. package/dist/core/orchestration/worker-result-adapter.js +167 -14
  177. package/dist/core/orchestration/worker-result-adapter.js.map +1 -1
  178. package/dist/core/reflection-controller.d.ts +1 -1
  179. package/dist/core/reflection-controller.d.ts.map +1 -1
  180. package/dist/core/reflection-controller.js +90 -54
  181. package/dist/core/reflection-controller.js.map +1 -1
  182. package/dist/core/research/evidence-bundle.d.ts +20 -0
  183. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  184. package/dist/core/research/evidence-bundle.js +173 -69
  185. package/dist/core/research/evidence-bundle.js.map +1 -1
  186. package/dist/core/runtime-builder.d.ts +7 -0
  187. package/dist/core/runtime-builder.d.ts.map +1 -1
  188. package/dist/core/runtime-builder.js +15 -1
  189. package/dist/core/runtime-builder.js.map +1 -1
  190. package/dist/core/session-artifact-bundle.d.ts +35 -0
  191. package/dist/core/session-artifact-bundle.d.ts.map +1 -0
  192. package/dist/core/session-artifact-bundle.js +61 -0
  193. package/dist/core/session-artifact-bundle.js.map +1 -0
  194. package/dist/core/tools/delegate-status.d.ts +6 -1
  195. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  196. package/dist/core/tools/delegate-status.js +100 -18
  197. package/dist/core/tools/delegate-status.js.map +1 -1
  198. package/dist/core/tools/delegate.d.ts +9 -1
  199. package/dist/core/tools/delegate.d.ts.map +1 -1
  200. package/dist/core/tools/delegate.js +323 -61
  201. package/dist/core/tools/delegate.js.map +1 -1
  202. package/dist/core/tools/run-process.d.ts +1 -1
  203. package/dist/core/tools/run-process.d.ts.map +1 -1
  204. package/dist/core/tools/run-process.js +69 -9
  205. package/dist/core/tools/run-process.js.map +1 -1
  206. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  207. package/dist/core/tools/shell-contract-router.js +12 -5
  208. package/dist/core/tools/shell-contract-router.js.map +1 -1
  209. package/dist/core/util/bounded-file.d.ts +15 -0
  210. package/dist/core/util/bounded-file.d.ts.map +1 -0
  211. package/dist/core/util/bounded-file.js +120 -0
  212. package/dist/core/util/bounded-file.js.map +1 -0
  213. package/dist/core/util/bounded-jsonl.d.ts.map +1 -1
  214. package/dist/core/util/bounded-jsonl.js +119 -9
  215. package/dist/core/util/bounded-jsonl.js.map +1 -1
  216. package/dist/core/util/bounded-value.d.ts +3 -0
  217. package/dist/core/util/bounded-value.d.ts.map +1 -0
  218. package/dist/core/util/bounded-value.js +12 -0
  219. package/dist/core/util/bounded-value.js.map +1 -0
  220. package/dist/core/util/value-guards.d.ts +2 -0
  221. package/dist/core/util/value-guards.d.ts.map +1 -1
  222. package/dist/core/util/value-guards.js +5 -0
  223. package/dist/core/util/value-guards.js.map +1 -1
  224. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  225. package/dist/modes/interactive/components/session-selector.js +27 -44
  226. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  227. package/docs/tool-repair.md +3 -0
  228. package/docs/windows.md +4 -2
  229. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  230. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  231. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  232. package/examples/extensions/sandbox/package-lock.json +2 -2
  233. package/examples/extensions/sandbox/package.json +1 -1
  234. package/examples/extensions/with-deps/package-lock.json +2 -2
  235. package/examples/extensions/with-deps/package.json +1 -1
  236. package/npm-shrinkwrap.json +12 -12
  237. package/package.json +4 -4
@@ -0,0 +1,348 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { existsSync, mkdirSync, unlinkSync, watch } from "node:fs";
3
+ import { basename, dirname } from "node:path";
4
+ import { stateFile } from "../agent-paths.js";
5
+ import { canonicalPathScopeIdentity, isPathWithinScope, pathScopesOverlap, safeRealpathSync, } from "../autonomy/path-scope.js";
6
+ import { withFileLockSync, writeFileAtomicSync } from "../util/atomic-file.js";
7
+ import { readBoundedTextFileSync } from "../util/bounded-file.js";
8
+ import { requireBoundedTrimmedText } from "../util/bounded-value.js";
9
+ import { parseLocalWorkerProcessOwnerId } from "./worker-process-owner.js";
10
+ const SCHEMA_VERSION = 1;
11
+ const MAX_IDENTIFIER_CHARS = 256;
12
+ const MAX_PATH_CHARS = 4096;
13
+ const MAX_SCOPES = 32;
14
+ const MAX_RESERVATIONS = 64;
15
+ const MAX_FILE_BYTES = 128 * 1024;
16
+ const availabilityListeners = new Map();
17
+ function positiveFencingToken(value) {
18
+ if (!Number.isSafeInteger(value) || value < 1)
19
+ throw new TypeError("Fencing token must be a positive safe integer.");
20
+ return value;
21
+ }
22
+ /**
23
+ * Prefer a real path for filesystem-backed scopes, but preserve a foreign lexical path family
24
+ * (for example a Windows drive path inspected from WSL) so shared path-scope helpers retain
25
+ * their documented Windows drive/UNC comparison semantics.
26
+ */
27
+ function canonicalRealOrLexicalPath(value, label) {
28
+ const lexical = canonicalPathScopeIdentity(requireBoundedTrimmedText(value, MAX_PATH_CHARS, label));
29
+ const resolved = safeRealpathSync(lexical);
30
+ return pathScopesOverlap(lexical, resolved) ? canonicalPathScopeIdentity(resolved) : lexical;
31
+ }
32
+ function canonicalWorkspace(workspace) {
33
+ const repositoryRoot = canonicalRealOrLexicalPath(workspace.repositoryRoot, "Repository root");
34
+ const executionRoot = canonicalRealOrLexicalPath(workspace.executionRoot, "Execution root");
35
+ const isolatedWorktreeId = workspace.isolatedWorktreeId
36
+ ? requireBoundedTrimmedText(workspace.isolatedWorktreeId, MAX_IDENTIFIER_CHARS, "Isolated worktree id")
37
+ : undefined;
38
+ const isPrimaryExecutionRoot = isPathWithinScope(executionRoot, repositoryRoot) && isPathWithinScope(repositoryRoot, executionRoot);
39
+ if (isPrimaryExecutionRoot && isolatedWorktreeId) {
40
+ throw new TypeError("An isolated worktree execution root must differ from the shared repository root.");
41
+ }
42
+ if (!isPrimaryExecutionRoot && !isolatedWorktreeId) {
43
+ throw new TypeError("A non-primary execution root requires an explicit isolated worktree identity.");
44
+ }
45
+ return {
46
+ repositoryRoot,
47
+ executionRoot,
48
+ executionIdentity: isolatedWorktreeId ? `isolated:${executionRoot}` : `shared:${repositoryRoot}`,
49
+ ...(isolatedWorktreeId ? { isolatedWorktreeId } : {}),
50
+ };
51
+ }
52
+ function reservationFile(agentDir, repositoryRoot) {
53
+ const digest = createHash("sha256")
54
+ .update("pi-worker-write-reservations-v1")
55
+ .update("\0")
56
+ .update(repositoryRoot)
57
+ .digest("hex");
58
+ return stateFile(agentDir, "orchestration", "worker-write-reservations", `${digest}.json`);
59
+ }
60
+ function notifyAvailability(filePath) {
61
+ for (const listener of availabilityListeners.get(filePath) ?? [])
62
+ queueMicrotask(listener);
63
+ }
64
+ function leaseFromRecord(filePath, record) {
65
+ return {
66
+ filePath,
67
+ reservationId: record.reservationId,
68
+ parentSessionId: record.parentSessionId,
69
+ ownerId: record.ownerId,
70
+ taskId: record.taskId,
71
+ attemptId: record.attemptId,
72
+ fencingToken: record.fencingToken,
73
+ repositoryRoot: record.repositoryRoot,
74
+ executionRoot: record.executionRoot,
75
+ executionIdentity: record.executionIdentity,
76
+ ...(record.isolatedWorktreeId ? { isolatedWorktreeId: record.isolatedWorktreeId } : {}),
77
+ writeScopes: [...record.writeScopes],
78
+ };
79
+ }
80
+ function stringField(value, label, maximum) {
81
+ if (typeof value !== "string")
82
+ throw new Error(`Worker write reservation ${label} is invalid.`);
83
+ return requireBoundedTrimmedText(value, maximum, `Worker write reservation ${label}`);
84
+ }
85
+ function localOwnerId(value) {
86
+ const ownerId = stringField(value, "owner id", MAX_IDENTIFIER_CHARS);
87
+ if (!parseLocalWorkerProcessOwnerId(ownerId))
88
+ throw new Error("Worker write reservation owner id is invalid.");
89
+ return ownerId;
90
+ }
91
+ function parseReservation(value, repositoryRoot) {
92
+ if (!value || typeof value !== "object" || Array.isArray(value))
93
+ throw new Error("Worker write reservation entry is invalid.");
94
+ const entry = value;
95
+ const fencingToken = entry.fencingToken;
96
+ if (typeof fencingToken !== "number")
97
+ throw new Error("Worker write reservation fencing token is invalid.");
98
+ const parsedRepositoryRoot = stringField(entry.repositoryRoot, "repository root", MAX_PATH_CHARS);
99
+ if (parsedRepositoryRoot !== repositoryRoot)
100
+ throw new Error("Worker write reservation repository root conflicts with its store.");
101
+ if (!Array.isArray(entry.writeScopes) || entry.writeScopes.length === 0 || entry.writeScopes.length > MAX_SCOPES) {
102
+ throw new Error("Worker write reservation scopes are invalid.");
103
+ }
104
+ const writeScopes = entry.writeScopes.map((scope) => stringField(scope, "write scope", MAX_PATH_CHARS));
105
+ const isolatedWorktreeId = entry.isolatedWorktreeId === undefined
106
+ ? undefined
107
+ : stringField(entry.isolatedWorktreeId, "isolated worktree id", MAX_IDENTIFIER_CHARS);
108
+ return {
109
+ reservationId: stringField(entry.reservationId, "id", MAX_IDENTIFIER_CHARS),
110
+ parentSessionId: stringField(entry.parentSessionId, "parent session", MAX_IDENTIFIER_CHARS),
111
+ ownerId: localOwnerId(entry.ownerId),
112
+ taskId: stringField(entry.taskId, "task", MAX_IDENTIFIER_CHARS),
113
+ attemptId: stringField(entry.attemptId, "attempt", MAX_IDENTIFIER_CHARS),
114
+ fencingToken: positiveFencingToken(fencingToken),
115
+ repositoryRoot: parsedRepositoryRoot,
116
+ executionRoot: stringField(entry.executionRoot, "execution root", MAX_PATH_CHARS),
117
+ executionIdentity: stringField(entry.executionIdentity, "execution identity", MAX_PATH_CHARS),
118
+ ...(isolatedWorktreeId ? { isolatedWorktreeId } : {}),
119
+ writeScopes,
120
+ createdAt: stringField(entry.createdAt, "created time", MAX_IDENTIFIER_CHARS),
121
+ };
122
+ }
123
+ function parseStore(raw, repositoryRoot) {
124
+ if (Buffer.byteLength(raw, "utf-8") > MAX_FILE_BYTES)
125
+ throw new Error("Worker write reservation store exceeds its byte limit.");
126
+ let value;
127
+ try {
128
+ value = JSON.parse(raw);
129
+ }
130
+ catch {
131
+ throw new Error("Worker write reservation store is unreadable; refusing concurrent write admission.");
132
+ }
133
+ if (!value || typeof value !== "object" || Array.isArray(value))
134
+ throw new Error("Worker write reservation store is invalid.");
135
+ const store = value;
136
+ if (store.schemaVersion !== SCHEMA_VERSION)
137
+ throw new Error("Worker write reservation store schema is unsupported.");
138
+ if (stringField(store.repositoryRoot, "store repository root", MAX_PATH_CHARS) !== repositoryRoot) {
139
+ throw new Error("Worker write reservation store root conflicts with the requested workspace.");
140
+ }
141
+ if (!Array.isArray(store.reservations) || store.reservations.length > MAX_RESERVATIONS) {
142
+ throw new Error("Worker write reservation store entries are invalid.");
143
+ }
144
+ const reservations = store.reservations.map((entry) => parseReservation(entry, repositoryRoot));
145
+ if (new Set(reservations.map((entry) => entry.reservationId)).size !== reservations.length) {
146
+ throw new Error("Worker write reservation store contains duplicate reservation identities.");
147
+ }
148
+ return { schemaVersion: SCHEMA_VERSION, repositoryRoot, reservations };
149
+ }
150
+ function writeStore(filePath, store) {
151
+ const serialized = `${JSON.stringify(store)}\n`;
152
+ if (Buffer.byteLength(serialized, "utf-8") > MAX_FILE_BYTES) {
153
+ throw new Error("Worker write reservation store would exceed its byte limit.");
154
+ }
155
+ writeFileAtomicSync(filePath, serialized, { mode: 0o600 });
156
+ }
157
+ function overlappingReservationIds(reservations, executionIdentity, writeScopes) {
158
+ const matches = [];
159
+ for (const reservation of reservations) {
160
+ if (reservation.executionIdentity !== executionIdentity)
161
+ continue;
162
+ if (reservation.writeScopes.some((reservedScope) => writeScopes.some((requestedScope) => pathScopesOverlap(reservedScope, requestedScope)))) {
163
+ matches.push(reservation.reservationId);
164
+ }
165
+ }
166
+ return matches;
167
+ }
168
+ /**
169
+ * Cross-process, agent-owned admission seam for concurrent worker mutations. It owns only
170
+ * reservations; lifecycle, process liveness, and terminal status remain controller concerns.
171
+ */
172
+ export class WorkerWriteReservationStore {
173
+ agentDir;
174
+ watchDirectory;
175
+ constructor(options) {
176
+ this.agentDir = requireBoundedTrimmedText(options.agentDir, MAX_PATH_CHARS, "Agent directory");
177
+ this.watchDirectory = options.watchDirectory ?? watch;
178
+ }
179
+ acquire(request) {
180
+ if (request.access === "read")
181
+ return { kind: "granted" };
182
+ if (request.access !== "write")
183
+ throw new TypeError("Worker write reservation access must be read or write.");
184
+ if (request.writeScopes.length === 0 || request.writeScopes.length > MAX_SCOPES) {
185
+ throw new TypeError(`Write reservations require from 1 through ${MAX_SCOPES} write scopes.`);
186
+ }
187
+ const workspace = canonicalWorkspace(request.workspace);
188
+ const writeScopes = [
189
+ ...new Set(request.writeScopes.map((scope) => canonicalRealOrLexicalPath(scope, "Write scope"))),
190
+ ].sort();
191
+ if (writeScopes.some((scope) => !isPathWithinScope(scope, workspace.executionRoot))) {
192
+ throw new TypeError("Write reservation scopes must be within the execution root.");
193
+ }
194
+ const parentSessionId = requireBoundedTrimmedText(request.parentSessionId, MAX_IDENTIFIER_CHARS, "Parent session id");
195
+ const ownerId = localOwnerId(request.ownerId);
196
+ const taskId = requireBoundedTrimmedText(request.taskId, MAX_IDENTIFIER_CHARS, "Task id");
197
+ const attemptId = requireBoundedTrimmedText(request.attemptId, MAX_IDENTIFIER_CHARS, "Attempt id");
198
+ const fencingToken = positiveFencingToken(request.fencingToken);
199
+ const filePath = reservationFile(this.agentDir, workspace.repositoryRoot);
200
+ return withFileLockSync(filePath, () => {
201
+ const store = this.readLocked(filePath, workspace.repositoryRoot);
202
+ const matchingAttempt = store.reservations.find((reservation) => reservation.parentSessionId === parentSessionId &&
203
+ reservation.ownerId === ownerId &&
204
+ reservation.taskId === taskId &&
205
+ reservation.attemptId === attemptId &&
206
+ reservation.fencingToken === fencingToken);
207
+ if (matchingAttempt) {
208
+ if (matchingAttempt.executionIdentity !== workspace.executionIdentity ||
209
+ matchingAttempt.isolatedWorktreeId !== workspace.isolatedWorktreeId ||
210
+ matchingAttempt.writeScopes.length !== writeScopes.length ||
211
+ matchingAttempt.writeScopes.some((scope, index) => scope !== writeScopes[index])) {
212
+ return { kind: "blocked", reasonCode: "attempt_reservation_conflict" };
213
+ }
214
+ return { kind: "granted", lease: leaseFromRecord(filePath, matchingAttempt) };
215
+ }
216
+ const conflicts = overlappingReservationIds(store.reservations, workspace.executionIdentity, writeScopes);
217
+ if (conflicts.length > 0) {
218
+ return { kind: "blocked", reasonCode: "overlapping_write_scope", conflictingReservationIds: conflicts };
219
+ }
220
+ if (store.reservations.length >= MAX_RESERVATIONS)
221
+ return { kind: "blocked", reasonCode: "reservation_capacity_reached" };
222
+ const record = {
223
+ reservationId: randomUUID(),
224
+ parentSessionId,
225
+ ownerId,
226
+ taskId,
227
+ attemptId,
228
+ fencingToken,
229
+ repositoryRoot: workspace.repositoryRoot,
230
+ executionRoot: workspace.executionRoot,
231
+ executionIdentity: workspace.executionIdentity,
232
+ ...(workspace.isolatedWorktreeId ? { isolatedWorktreeId: workspace.isolatedWorktreeId } : {}),
233
+ writeScopes,
234
+ createdAt: new Date().toISOString(),
235
+ };
236
+ store.reservations.push(record);
237
+ writeStore(filePath, store);
238
+ return { kind: "granted", lease: leaseFromRecord(filePath, record) };
239
+ });
240
+ }
241
+ release(lease) {
242
+ const repositoryRoot = requireBoundedTrimmedText(lease.repositoryRoot, MAX_PATH_CHARS, "Lease repository root");
243
+ const ownerId = localOwnerId(lease.ownerId);
244
+ const expectedFilePath = reservationFile(this.agentDir, repositoryRoot);
245
+ if (lease.filePath !== expectedFilePath)
246
+ throw new TypeError("Worker write reservation lease does not belong to this agent state.");
247
+ return withFileLockSync(expectedFilePath, () => {
248
+ const store = this.readLocked(expectedFilePath, repositoryRoot);
249
+ const index = store.reservations.findIndex((reservation) => reservation.reservationId === lease.reservationId);
250
+ if (index < 0) {
251
+ const supersedingReservation = store.reservations.some((reservation) => reservation.parentSessionId === lease.parentSessionId &&
252
+ reservation.ownerId === ownerId &&
253
+ reservation.taskId === lease.taskId &&
254
+ reservation.fencingToken > lease.fencingToken);
255
+ return { kind: supersedingReservation ? "stale_fence" : "not_found" };
256
+ }
257
+ const record = store.reservations[index];
258
+ if (record.parentSessionId !== lease.parentSessionId ||
259
+ record.ownerId !== ownerId ||
260
+ record.taskId !== lease.taskId ||
261
+ record.attemptId !== lease.attemptId ||
262
+ record.fencingToken !== lease.fencingToken ||
263
+ record.executionIdentity !== lease.executionIdentity ||
264
+ record.isolatedWorktreeId !== lease.isolatedWorktreeId) {
265
+ return { kind: "stale_fence" };
266
+ }
267
+ store.reservations.splice(index, 1);
268
+ if (store.reservations.length === 0)
269
+ unlinkSync(expectedFilePath);
270
+ else
271
+ writeStore(expectedFilePath, store);
272
+ notifyAvailability(expectedFilePath);
273
+ return { kind: "released" };
274
+ });
275
+ }
276
+ /** Event-driven cross-process wakeup for queued write admission; caller owns disposal. */
277
+ watchAvailability(workspace, listener) {
278
+ const canonical = canonicalWorkspace(workspace);
279
+ const filePath = reservationFile(this.agentDir, canonical.repositoryRoot);
280
+ const listeners = availabilityListeners.get(filePath) ?? new Set();
281
+ availabilityListeners.set(filePath, listeners);
282
+ listeners.add(listener);
283
+ const directory = dirname(filePath);
284
+ mkdirSync(directory, { recursive: true });
285
+ let scheduled = false;
286
+ const schedule = () => {
287
+ if (scheduled)
288
+ return;
289
+ scheduled = true;
290
+ queueMicrotask(() => {
291
+ scheduled = false;
292
+ listener();
293
+ });
294
+ };
295
+ const watcher = this.watchDirectory(directory, { persistent: false }, (_eventType, fileName) => {
296
+ // Node may omit a filename (notably on Windows), or provide a Buffer when the watcher is
297
+ // configured that way. An unknown filename is conservatively treated as a wake signal: the
298
+ // scheduler re-evaluates durable admission, so this cannot grant access by itself.
299
+ if (fileName === null) {
300
+ schedule();
301
+ return;
302
+ }
303
+ const normalizedName = Buffer.isBuffer(fileName) ? fileName.toString("utf-8") : fileName;
304
+ if (normalizedName === basename(filePath))
305
+ schedule();
306
+ });
307
+ return () => {
308
+ listeners.delete(listener);
309
+ if (listeners.size === 0)
310
+ availabilityListeners.delete(filePath);
311
+ watcher.close();
312
+ };
313
+ }
314
+ recover(request) {
315
+ const workspace = canonicalWorkspace(request.workspace);
316
+ const filePath = reservationFile(this.agentDir, workspace.repositoryRoot);
317
+ const evidence = new Map();
318
+ for (const observation of request.evidence) {
319
+ const reservationId = requireBoundedTrimmedText(observation.reservationId, MAX_IDENTIFIER_CHARS, "Reservation evidence id");
320
+ if (observation.state !== "live" && observation.state !== "not_live" && observation.state !== "unknown") {
321
+ throw new TypeError("Reservation evidence state must be live, not_live, or unknown.");
322
+ }
323
+ if (evidence.has(reservationId))
324
+ throw new TypeError("Reservation evidence must not repeat an identity.");
325
+ evidence.set(reservationId, observation.state);
326
+ }
327
+ return withFileLockSync(filePath, () => {
328
+ const store = this.readLocked(filePath, workspace.repositoryRoot);
329
+ const outcomes = store.reservations
330
+ .filter((reservation) => reservation.executionIdentity === workspace.executionIdentity)
331
+ .map((reservation) => {
332
+ const state = evidence.get(reservation.reservationId);
333
+ return {
334
+ kind: state === "live" ? "active" : state === "not_live" ? "stale" : "inspection_required",
335
+ reservationId: reservation.reservationId,
336
+ lease: leaseFromRecord(filePath, reservation),
337
+ };
338
+ });
339
+ return { outcomes };
340
+ });
341
+ }
342
+ readLocked(filePath, repositoryRoot) {
343
+ if (!existsSync(filePath))
344
+ return { schemaVersion: SCHEMA_VERSION, repositoryRoot, reservations: [] };
345
+ return parseStore(readBoundedTextFileSync(filePath, MAX_FILE_BYTES, "Worker write reservation store"), repositoryRoot);
346
+ }
347
+ }
348
+ //# sourceMappingURL=worker-write-reservation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-write-reservation.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-write-reservation.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { existsSync, type FSWatcher, mkdirSync, unlinkSync, watch } from \"node:fs\";\nimport { basename, dirname } from \"node:path\";\nimport { stateFile } from \"../agent-paths.ts\";\nimport {\n\tcanonicalPathScopeIdentity,\n\tisPathWithinScope,\n\tpathScopesOverlap,\n\tsafeRealpathSync,\n} from \"../autonomy/path-scope.ts\";\nimport { withFileLockSync, writeFileAtomicSync } from \"../util/atomic-file.ts\";\nimport { readBoundedTextFileSync } from \"../util/bounded-file.ts\";\nimport { requireBoundedTrimmedText } from \"../util/bounded-value.ts\";\nimport { parseLocalWorkerProcessOwnerId } from \"./worker-process-owner.ts\";\n\nconst SCHEMA_VERSION = 1;\nconst MAX_IDENTIFIER_CHARS = 256;\nconst MAX_PATH_CHARS = 4096;\nconst MAX_SCOPES = 32;\nconst MAX_RESERVATIONS = 64;\nconst MAX_FILE_BYTES = 128 * 1024;\nconst availabilityListeners = new Map<string, Set<() => void>>();\n\ntype WatchDirectory = (\n\tdirectory: string,\n\toptions: { persistent: boolean },\n\tlistener: (eventType: string, fileName: string | Buffer | null) => void,\n) => FSWatcher;\n\nexport interface WorkerWriteReservationWorkspace {\n\t/** Canonical repository/workspace root shared by primary and isolated worktrees. */\n\trepositoryRoot: string;\n\t/** Actual checkout used by this attempt. A non-primary checkout requires an isolation identity. */\n\texecutionRoot: string;\n\t/** Durable worktree identity supplied by the worktree owner, never a model-provided label. */\n\tisolatedWorktreeId?: string;\n}\n\nexport interface WorkerWriteReservationRequest {\n\tparentSessionId: string;\n\t/** Exact pi-owned process instance that acquired this reservation. */\n\townerId: string;\n\ttaskId: string;\n\tattemptId: string;\n\tfencingToken: number;\n\taccess: \"read\" | \"write\";\n\tworkspace: WorkerWriteReservationWorkspace;\n\t/** Absolute authorized write roots. They are resolved before persistence. */\n\twriteScopes: readonly string[];\n}\n\nexport interface WorkerWriteReservationLease {\n\tfilePath: string;\n\treservationId: string;\n\tparentSessionId: string;\n\townerId: string;\n\ttaskId: string;\n\tattemptId: string;\n\tfencingToken: number;\n\trepositoryRoot: string;\n\texecutionRoot: string;\n\texecutionIdentity: string;\n\tisolatedWorktreeId?: string;\n\twriteScopes: readonly string[];\n}\n\nexport type WorkerWriteReservationAcquireResult =\n\t| { kind: \"granted\"; lease?: WorkerWriteReservationLease }\n\t| {\n\t\t\tkind: \"blocked\";\n\t\t\treasonCode: \"overlapping_write_scope\" | \"attempt_reservation_conflict\" | \"reservation_capacity_reached\";\n\t\t\tconflictingReservationIds?: readonly string[];\n\t };\n\nexport type WorkerWriteReservationReleaseResult =\n\t| { kind: \"released\" }\n\t| { kind: \"stale_fence\" }\n\t| { kind: \"not_found\" };\n\nexport type WorkerWriteReservationEvidenceState = \"live\" | \"not_live\" | \"unknown\";\n\nexport interface WorkerWriteReservationRecoveryEvidence {\n\treservationId: string;\n\tstate: WorkerWriteReservationEvidenceState;\n}\n\nexport interface WorkerWriteReservationRecoveryRequest {\n\tworkspace: WorkerWriteReservationWorkspace;\n\t/** Caller-owned lease observations. This store never polls process state or worker output. */\n\tevidence: readonly WorkerWriteReservationRecoveryEvidence[];\n}\n\nexport interface WorkerWriteReservationRecoveryOutcome {\n\tkind: \"active\" | \"stale\" | \"inspection_required\";\n\treservationId: string;\n\tlease: WorkerWriteReservationLease;\n}\n\ninterface PersistedReservation {\n\treservationId: string;\n\tparentSessionId: string;\n\townerId: string;\n\ttaskId: string;\n\tattemptId: string;\n\tfencingToken: number;\n\trepositoryRoot: string;\n\texecutionRoot: string;\n\texecutionIdentity: string;\n\tisolatedWorktreeId?: string;\n\twriteScopes: string[];\n\tcreatedAt: string;\n}\n\ninterface PersistedReservationStore {\n\tschemaVersion: number;\n\trepositoryRoot: string;\n\treservations: PersistedReservation[];\n}\n\ninterface CanonicalWorkspace {\n\trepositoryRoot: string;\n\texecutionRoot: string;\n\texecutionIdentity: string;\n\tisolatedWorktreeId?: string;\n}\n\nfunction positiveFencingToken(value: number): number {\n\tif (!Number.isSafeInteger(value) || value < 1) throw new TypeError(\"Fencing token must be a positive safe integer.\");\n\treturn value;\n}\n\n/**\n * Prefer a real path for filesystem-backed scopes, but preserve a foreign lexical path family\n * (for example a Windows drive path inspected from WSL) so shared path-scope helpers retain\n * their documented Windows drive/UNC comparison semantics.\n */\nfunction canonicalRealOrLexicalPath(value: string, label: string): string {\n\tconst lexical = canonicalPathScopeIdentity(requireBoundedTrimmedText(value, MAX_PATH_CHARS, label));\n\tconst resolved = safeRealpathSync(lexical);\n\treturn pathScopesOverlap(lexical, resolved) ? canonicalPathScopeIdentity(resolved) : lexical;\n}\n\nfunction canonicalWorkspace(workspace: WorkerWriteReservationWorkspace): CanonicalWorkspace {\n\tconst repositoryRoot = canonicalRealOrLexicalPath(workspace.repositoryRoot, \"Repository root\");\n\tconst executionRoot = canonicalRealOrLexicalPath(workspace.executionRoot, \"Execution root\");\n\tconst isolatedWorktreeId = workspace.isolatedWorktreeId\n\t\t? requireBoundedTrimmedText(workspace.isolatedWorktreeId, MAX_IDENTIFIER_CHARS, \"Isolated worktree id\")\n\t\t: undefined;\n\tconst isPrimaryExecutionRoot =\n\t\tisPathWithinScope(executionRoot, repositoryRoot) && isPathWithinScope(repositoryRoot, executionRoot);\n\tif (isPrimaryExecutionRoot && isolatedWorktreeId) {\n\t\tthrow new TypeError(\"An isolated worktree execution root must differ from the shared repository root.\");\n\t}\n\tif (!isPrimaryExecutionRoot && !isolatedWorktreeId) {\n\t\tthrow new TypeError(\"A non-primary execution root requires an explicit isolated worktree identity.\");\n\t}\n\treturn {\n\t\trepositoryRoot,\n\t\texecutionRoot,\n\t\texecutionIdentity: isolatedWorktreeId ? `isolated:${executionRoot}` : `shared:${repositoryRoot}`,\n\t\t...(isolatedWorktreeId ? { isolatedWorktreeId } : {}),\n\t};\n}\n\nfunction reservationFile(agentDir: string, repositoryRoot: string): string {\n\tconst digest = createHash(\"sha256\")\n\t\t.update(\"pi-worker-write-reservations-v1\")\n\t\t.update(\"\\0\")\n\t\t.update(repositoryRoot)\n\t\t.digest(\"hex\");\n\treturn stateFile(agentDir, \"orchestration\", \"worker-write-reservations\", `${digest}.json`);\n}\n\nfunction notifyAvailability(filePath: string): void {\n\tfor (const listener of availabilityListeners.get(filePath) ?? []) queueMicrotask(listener);\n}\n\nfunction leaseFromRecord(filePath: string, record: PersistedReservation): WorkerWriteReservationLease {\n\treturn {\n\t\tfilePath,\n\t\treservationId: record.reservationId,\n\t\tparentSessionId: record.parentSessionId,\n\t\townerId: record.ownerId,\n\t\ttaskId: record.taskId,\n\t\tattemptId: record.attemptId,\n\t\tfencingToken: record.fencingToken,\n\t\trepositoryRoot: record.repositoryRoot,\n\t\texecutionRoot: record.executionRoot,\n\t\texecutionIdentity: record.executionIdentity,\n\t\t...(record.isolatedWorktreeId ? { isolatedWorktreeId: record.isolatedWorktreeId } : {}),\n\t\twriteScopes: [...record.writeScopes],\n\t};\n}\n\nfunction stringField(value: unknown, label: string, maximum: number): string {\n\tif (typeof value !== \"string\") throw new Error(`Worker write reservation ${label} is invalid.`);\n\treturn requireBoundedTrimmedText(value, maximum, `Worker write reservation ${label}`);\n}\n\nfunction localOwnerId(value: unknown): string {\n\tconst ownerId = stringField(value, \"owner id\", MAX_IDENTIFIER_CHARS);\n\tif (!parseLocalWorkerProcessOwnerId(ownerId)) throw new Error(\"Worker write reservation owner id is invalid.\");\n\treturn ownerId;\n}\n\nfunction parseReservation(value: unknown, repositoryRoot: string): PersistedReservation {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new Error(\"Worker write reservation entry is invalid.\");\n\tconst entry = value as Record<string, unknown>;\n\tconst fencingToken = entry.fencingToken;\n\tif (typeof fencingToken !== \"number\") throw new Error(\"Worker write reservation fencing token is invalid.\");\n\tconst parsedRepositoryRoot = stringField(entry.repositoryRoot, \"repository root\", MAX_PATH_CHARS);\n\tif (parsedRepositoryRoot !== repositoryRoot)\n\t\tthrow new Error(\"Worker write reservation repository root conflicts with its store.\");\n\tif (!Array.isArray(entry.writeScopes) || entry.writeScopes.length === 0 || entry.writeScopes.length > MAX_SCOPES) {\n\t\tthrow new Error(\"Worker write reservation scopes are invalid.\");\n\t}\n\tconst writeScopes = entry.writeScopes.map((scope) => stringField(scope, \"write scope\", MAX_PATH_CHARS));\n\tconst isolatedWorktreeId =\n\t\tentry.isolatedWorktreeId === undefined\n\t\t\t? undefined\n\t\t\t: stringField(entry.isolatedWorktreeId, \"isolated worktree id\", MAX_IDENTIFIER_CHARS);\n\treturn {\n\t\treservationId: stringField(entry.reservationId, \"id\", MAX_IDENTIFIER_CHARS),\n\t\tparentSessionId: stringField(entry.parentSessionId, \"parent session\", MAX_IDENTIFIER_CHARS),\n\t\townerId: localOwnerId(entry.ownerId),\n\t\ttaskId: stringField(entry.taskId, \"task\", MAX_IDENTIFIER_CHARS),\n\t\tattemptId: stringField(entry.attemptId, \"attempt\", MAX_IDENTIFIER_CHARS),\n\t\tfencingToken: positiveFencingToken(fencingToken),\n\t\trepositoryRoot: parsedRepositoryRoot,\n\t\texecutionRoot: stringField(entry.executionRoot, \"execution root\", MAX_PATH_CHARS),\n\t\texecutionIdentity: stringField(entry.executionIdentity, \"execution identity\", MAX_PATH_CHARS),\n\t\t...(isolatedWorktreeId ? { isolatedWorktreeId } : {}),\n\t\twriteScopes,\n\t\tcreatedAt: stringField(entry.createdAt, \"created time\", MAX_IDENTIFIER_CHARS),\n\t};\n}\n\nfunction parseStore(raw: string, repositoryRoot: string): PersistedReservationStore {\n\tif (Buffer.byteLength(raw, \"utf-8\") > MAX_FILE_BYTES)\n\t\tthrow new Error(\"Worker write reservation store exceeds its byte limit.\");\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(raw);\n\t} catch {\n\t\tthrow new Error(\"Worker write reservation store is unreadable; refusing concurrent write admission.\");\n\t}\n\tif (!value || typeof value !== \"object\" || Array.isArray(value))\n\t\tthrow new Error(\"Worker write reservation store is invalid.\");\n\tconst store = value as Record<string, unknown>;\n\tif (store.schemaVersion !== SCHEMA_VERSION) throw new Error(\"Worker write reservation store schema is unsupported.\");\n\tif (stringField(store.repositoryRoot, \"store repository root\", MAX_PATH_CHARS) !== repositoryRoot) {\n\t\tthrow new Error(\"Worker write reservation store root conflicts with the requested workspace.\");\n\t}\n\tif (!Array.isArray(store.reservations) || store.reservations.length > MAX_RESERVATIONS) {\n\t\tthrow new Error(\"Worker write reservation store entries are invalid.\");\n\t}\n\tconst reservations = store.reservations.map((entry) => parseReservation(entry, repositoryRoot));\n\tif (new Set(reservations.map((entry) => entry.reservationId)).size !== reservations.length) {\n\t\tthrow new Error(\"Worker write reservation store contains duplicate reservation identities.\");\n\t}\n\treturn { schemaVersion: SCHEMA_VERSION, repositoryRoot, reservations };\n}\n\nfunction writeStore(filePath: string, store: PersistedReservationStore): void {\n\tconst serialized = `${JSON.stringify(store)}\\n`;\n\tif (Buffer.byteLength(serialized, \"utf-8\") > MAX_FILE_BYTES) {\n\t\tthrow new Error(\"Worker write reservation store would exceed its byte limit.\");\n\t}\n\twriteFileAtomicSync(filePath, serialized, { mode: 0o600 });\n}\n\nfunction overlappingReservationIds(\n\treservations: readonly PersistedReservation[],\n\texecutionIdentity: string,\n\twriteScopes: readonly string[],\n): string[] {\n\tconst matches: string[] = [];\n\tfor (const reservation of reservations) {\n\t\tif (reservation.executionIdentity !== executionIdentity) continue;\n\t\tif (\n\t\t\treservation.writeScopes.some((reservedScope) =>\n\t\t\t\twriteScopes.some((requestedScope) => pathScopesOverlap(reservedScope, requestedScope)),\n\t\t\t)\n\t\t) {\n\t\t\tmatches.push(reservation.reservationId);\n\t\t}\n\t}\n\treturn matches;\n}\n\n/**\n * Cross-process, agent-owned admission seam for concurrent worker mutations. It owns only\n * reservations; lifecycle, process liveness, and terminal status remain controller concerns.\n */\nexport class WorkerWriteReservationStore {\n\tprivate readonly agentDir: string;\n\tprivate readonly watchDirectory: WatchDirectory;\n\n\tconstructor(options: { agentDir: string; watchDirectory?: WatchDirectory }) {\n\t\tthis.agentDir = requireBoundedTrimmedText(options.agentDir, MAX_PATH_CHARS, \"Agent directory\");\n\t\tthis.watchDirectory = options.watchDirectory ?? watch;\n\t}\n\n\tacquire(request: WorkerWriteReservationRequest): WorkerWriteReservationAcquireResult {\n\t\tif (request.access === \"read\") return { kind: \"granted\" };\n\t\tif (request.access !== \"write\") throw new TypeError(\"Worker write reservation access must be read or write.\");\n\t\tif (request.writeScopes.length === 0 || request.writeScopes.length > MAX_SCOPES) {\n\t\t\tthrow new TypeError(`Write reservations require from 1 through ${MAX_SCOPES} write scopes.`);\n\t\t}\n\t\tconst workspace = canonicalWorkspace(request.workspace);\n\t\tconst writeScopes = [\n\t\t\t...new Set(request.writeScopes.map((scope) => canonicalRealOrLexicalPath(scope, \"Write scope\"))),\n\t\t].sort();\n\t\tif (writeScopes.some((scope) => !isPathWithinScope(scope, workspace.executionRoot))) {\n\t\t\tthrow new TypeError(\"Write reservation scopes must be within the execution root.\");\n\t\t}\n\t\tconst parentSessionId = requireBoundedTrimmedText(\n\t\t\trequest.parentSessionId,\n\t\t\tMAX_IDENTIFIER_CHARS,\n\t\t\t\"Parent session id\",\n\t\t);\n\t\tconst ownerId = localOwnerId(request.ownerId);\n\t\tconst taskId = requireBoundedTrimmedText(request.taskId, MAX_IDENTIFIER_CHARS, \"Task id\");\n\t\tconst attemptId = requireBoundedTrimmedText(request.attemptId, MAX_IDENTIFIER_CHARS, \"Attempt id\");\n\t\tconst fencingToken = positiveFencingToken(request.fencingToken);\n\t\tconst filePath = reservationFile(this.agentDir, workspace.repositoryRoot);\n\n\t\treturn withFileLockSync(filePath, () => {\n\t\t\tconst store = this.readLocked(filePath, workspace.repositoryRoot);\n\t\t\tconst matchingAttempt = store.reservations.find(\n\t\t\t\t(reservation) =>\n\t\t\t\t\treservation.parentSessionId === parentSessionId &&\n\t\t\t\t\treservation.ownerId === ownerId &&\n\t\t\t\t\treservation.taskId === taskId &&\n\t\t\t\t\treservation.attemptId === attemptId &&\n\t\t\t\t\treservation.fencingToken === fencingToken,\n\t\t\t);\n\t\t\tif (matchingAttempt) {\n\t\t\t\tif (\n\t\t\t\t\tmatchingAttempt.executionIdentity !== workspace.executionIdentity ||\n\t\t\t\t\tmatchingAttempt.isolatedWorktreeId !== workspace.isolatedWorktreeId ||\n\t\t\t\t\tmatchingAttempt.writeScopes.length !== writeScopes.length ||\n\t\t\t\t\tmatchingAttempt.writeScopes.some((scope, index) => scope !== writeScopes[index])\n\t\t\t\t) {\n\t\t\t\t\treturn { kind: \"blocked\", reasonCode: \"attempt_reservation_conflict\" };\n\t\t\t\t}\n\t\t\t\treturn { kind: \"granted\", lease: leaseFromRecord(filePath, matchingAttempt) };\n\t\t\t}\n\t\t\tconst conflicts = overlappingReservationIds(store.reservations, workspace.executionIdentity, writeScopes);\n\t\t\tif (conflicts.length > 0) {\n\t\t\t\treturn { kind: \"blocked\", reasonCode: \"overlapping_write_scope\", conflictingReservationIds: conflicts };\n\t\t\t}\n\t\t\tif (store.reservations.length >= MAX_RESERVATIONS)\n\t\t\t\treturn { kind: \"blocked\", reasonCode: \"reservation_capacity_reached\" };\n\t\t\tconst record: PersistedReservation = {\n\t\t\t\treservationId: randomUUID(),\n\t\t\t\tparentSessionId,\n\t\t\t\townerId,\n\t\t\t\ttaskId,\n\t\t\t\tattemptId,\n\t\t\t\tfencingToken,\n\t\t\t\trepositoryRoot: workspace.repositoryRoot,\n\t\t\t\texecutionRoot: workspace.executionRoot,\n\t\t\t\texecutionIdentity: workspace.executionIdentity,\n\t\t\t\t...(workspace.isolatedWorktreeId ? { isolatedWorktreeId: workspace.isolatedWorktreeId } : {}),\n\t\t\t\twriteScopes,\n\t\t\t\tcreatedAt: new Date().toISOString(),\n\t\t\t};\n\t\t\tstore.reservations.push(record);\n\t\t\twriteStore(filePath, store);\n\t\t\treturn { kind: \"granted\", lease: leaseFromRecord(filePath, record) };\n\t\t});\n\t}\n\n\trelease(lease: WorkerWriteReservationLease): WorkerWriteReservationReleaseResult {\n\t\tconst repositoryRoot = requireBoundedTrimmedText(lease.repositoryRoot, MAX_PATH_CHARS, \"Lease repository root\");\n\t\tconst ownerId = localOwnerId(lease.ownerId);\n\t\tconst expectedFilePath = reservationFile(this.agentDir, repositoryRoot);\n\t\tif (lease.filePath !== expectedFilePath)\n\t\t\tthrow new TypeError(\"Worker write reservation lease does not belong to this agent state.\");\n\t\treturn withFileLockSync(expectedFilePath, () => {\n\t\t\tconst store = this.readLocked(expectedFilePath, repositoryRoot);\n\t\t\tconst index = store.reservations.findIndex((reservation) => reservation.reservationId === lease.reservationId);\n\t\t\tif (index < 0) {\n\t\t\t\tconst supersedingReservation = store.reservations.some(\n\t\t\t\t\t(reservation) =>\n\t\t\t\t\t\treservation.parentSessionId === lease.parentSessionId &&\n\t\t\t\t\t\treservation.ownerId === ownerId &&\n\t\t\t\t\t\treservation.taskId === lease.taskId &&\n\t\t\t\t\t\treservation.fencingToken > lease.fencingToken,\n\t\t\t\t);\n\t\t\t\treturn { kind: supersedingReservation ? \"stale_fence\" : \"not_found\" };\n\t\t\t}\n\t\t\tconst record = store.reservations[index];\n\t\t\tif (\n\t\t\t\trecord.parentSessionId !== lease.parentSessionId ||\n\t\t\t\trecord.ownerId !== ownerId ||\n\t\t\t\trecord.taskId !== lease.taskId ||\n\t\t\t\trecord.attemptId !== lease.attemptId ||\n\t\t\t\trecord.fencingToken !== lease.fencingToken ||\n\t\t\t\trecord.executionIdentity !== lease.executionIdentity ||\n\t\t\t\trecord.isolatedWorktreeId !== lease.isolatedWorktreeId\n\t\t\t) {\n\t\t\t\treturn { kind: \"stale_fence\" };\n\t\t\t}\n\t\t\tstore.reservations.splice(index, 1);\n\t\t\tif (store.reservations.length === 0) unlinkSync(expectedFilePath);\n\t\t\telse writeStore(expectedFilePath, store);\n\t\t\tnotifyAvailability(expectedFilePath);\n\t\t\treturn { kind: \"released\" };\n\t\t});\n\t}\n\n\t/** Event-driven cross-process wakeup for queued write admission; caller owns disposal. */\n\twatchAvailability(workspace: WorkerWriteReservationWorkspace, listener: () => void): () => void {\n\t\tconst canonical = canonicalWorkspace(workspace);\n\t\tconst filePath = reservationFile(this.agentDir, canonical.repositoryRoot);\n\t\tconst listeners = availabilityListeners.get(filePath) ?? new Set<() => void>();\n\t\tavailabilityListeners.set(filePath, listeners);\n\t\tlisteners.add(listener);\n\t\tconst directory = dirname(filePath);\n\t\tmkdirSync(directory, { recursive: true });\n\t\tlet scheduled = false;\n\t\tconst schedule = () => {\n\t\t\tif (scheduled) return;\n\t\t\tscheduled = true;\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tscheduled = false;\n\t\t\t\tlistener();\n\t\t\t});\n\t\t};\n\t\tconst watcher = this.watchDirectory(directory, { persistent: false }, (_eventType, fileName) => {\n\t\t\t// Node may omit a filename (notably on Windows), or provide a Buffer when the watcher is\n\t\t\t// configured that way. An unknown filename is conservatively treated as a wake signal: the\n\t\t\t// scheduler re-evaluates durable admission, so this cannot grant access by itself.\n\t\t\tif (fileName === null) {\n\t\t\t\tschedule();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst normalizedName = Buffer.isBuffer(fileName) ? fileName.toString(\"utf-8\") : fileName;\n\t\t\tif (normalizedName === basename(filePath)) schedule();\n\t\t});\n\t\treturn () => {\n\t\t\tlisteners.delete(listener);\n\t\t\tif (listeners.size === 0) availabilityListeners.delete(filePath);\n\t\t\twatcher.close();\n\t\t};\n\t}\n\n\trecover(request: WorkerWriteReservationRecoveryRequest): { outcomes: WorkerWriteReservationRecoveryOutcome[] } {\n\t\tconst workspace = canonicalWorkspace(request.workspace);\n\t\tconst filePath = reservationFile(this.agentDir, workspace.repositoryRoot);\n\t\tconst evidence = new Map<string, WorkerWriteReservationEvidenceState>();\n\t\tfor (const observation of request.evidence) {\n\t\t\tconst reservationId = requireBoundedTrimmedText(\n\t\t\t\tobservation.reservationId,\n\t\t\t\tMAX_IDENTIFIER_CHARS,\n\t\t\t\t\"Reservation evidence id\",\n\t\t\t);\n\t\t\tif (observation.state !== \"live\" && observation.state !== \"not_live\" && observation.state !== \"unknown\") {\n\t\t\t\tthrow new TypeError(\"Reservation evidence state must be live, not_live, or unknown.\");\n\t\t\t}\n\t\t\tif (evidence.has(reservationId)) throw new TypeError(\"Reservation evidence must not repeat an identity.\");\n\t\t\tevidence.set(reservationId, observation.state);\n\t\t}\n\t\treturn withFileLockSync(filePath, () => {\n\t\t\tconst store = this.readLocked(filePath, workspace.repositoryRoot);\n\t\t\tconst outcomes = store.reservations\n\t\t\t\t.filter((reservation) => reservation.executionIdentity === workspace.executionIdentity)\n\t\t\t\t.map((reservation): WorkerWriteReservationRecoveryOutcome => {\n\t\t\t\t\tconst state = evidence.get(reservation.reservationId);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: state === \"live\" ? \"active\" : state === \"not_live\" ? \"stale\" : \"inspection_required\",\n\t\t\t\t\t\treservationId: reservation.reservationId,\n\t\t\t\t\t\tlease: leaseFromRecord(filePath, reservation),\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t\treturn { outcomes };\n\t\t});\n\t}\n\n\tprivate readLocked(filePath: string, repositoryRoot: string): PersistedReservationStore {\n\t\tif (!existsSync(filePath)) return { schemaVersion: SCHEMA_VERSION, repositoryRoot, reservations: [] };\n\t\treturn parseStore(\n\t\t\treadBoundedTextFileSync(filePath, MAX_FILE_BYTES, \"Worker write reservation store\"),\n\t\t\trepositoryRoot,\n\t\t);\n\t}\n}\n"]}
@@ -0,0 +1,3 @@
1
+ /** Shared loopback classification for local execution, routing, and cache-warm decisions. */
2
+ export declare function isLoopbackModelEndpoint(baseUrl: string): boolean;
3
+ //# sourceMappingURL=model-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-endpoint.d.ts","sourceRoot":"","sources":["../../../src/core/models/model-endpoint.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMhE","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\n\n/** Shared loopback classification for local execution, routing, and cache-warm decisions. */\nexport function isLoopbackModelEndpoint(baseUrl: string): boolean {\n\ttry {\n\t\treturn LOOPBACK_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -0,0 +1,11 @@
1
+ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
2
+ /** Shared loopback classification for local execution, routing, and cache-warm decisions. */
3
+ export function isLoopbackModelEndpoint(baseUrl) {
4
+ try {
5
+ return LOOPBACK_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ //# sourceMappingURL=model-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-endpoint.js","sourceRoot":"","sources":["../../../src/core/models/model-endpoint.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3E,6FAA6F;AAC7F,MAAM,UAAU,uBAAuB,CAAC,OAAe,EAAW;IACjE,IAAI,CAAC;QACJ,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\n\n/** Shared loopback classification for local execution, routing, and cache-warm decisions. */\nexport function isLoopbackModelEndpoint(baseUrl: string): boolean {\n\ttry {\n\t\treturn LOOPBACK_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -16,6 +16,8 @@ export interface BuildPiResumeLaunchSpecOptions {
16
16
  export declare function isAgentIdentity(value: unknown): value is AgentIdentityContract;
17
17
  /** Build and validate the one logical identity shared by orchestration and process supervision. */
18
18
  export declare function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract;
19
+ /** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */
20
+ export declare function sameAgentResumeIdentity(left: AgentResumeContext, right: AgentResumeContext): boolean;
19
21
  /**
20
22
  * Build an argv-safe launch specification for the exact persisted Pi session behind a logical
21
23
  * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-resume.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAyB,MAAM,gBAAgB,CAAC;AAE5G,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,8BAA8B;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAiC9E;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,GAAG,qBAAqB,CAQ7G;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,8BAA8B,GACrC,kBAAkB,CA2BpB","sourcesContent":["import { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
1
+ {"version":3,"file":"agent-resume.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAyB,MAAM,gBAAgB,CAAC;AAE5G,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,8BAA8B;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAiC9E;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,GAAG,qBAAqB,CAQ7G;AAED,oGAAoG;AACpG,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAIpG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,8BAA8B,GACrC,kBAAkB,CA2BpB","sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */\nexport function sameAgentResumeIdentity(left: AgentResumeContext, right: AgentResumeContext): boolean {\n\tconst { latestCheckpointId: _leftCheckpoint, ...leftIdentity } = left;\n\tconst { latestCheckpointId: _rightCheckpoint, ...rightIdentity } = right;\n\treturn isDeepStrictEqual(leftIdentity, rightIdentity);\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { isDeepStrictEqual } from "node:util";
1
2
  import { PI_ORCHESTRATION_AGENT_ID_ENV } from "../process-identity.js";
2
3
  import { isPlainRecord } from "../util/value-guards.js";
3
4
  import { isResourcePointerKind } from "./contracts.js";
@@ -48,6 +49,12 @@ export function createAgentIdentity(agentId, resumeContext) {
48
49
  throw new TypeError("Agent resume context is invalid.");
49
50
  return identity;
50
51
  }
52
+ /** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */
53
+ export function sameAgentResumeIdentity(left, right) {
54
+ const { latestCheckpointId: _leftCheckpoint, ...leftIdentity } = left;
55
+ const { latestCheckpointId: _rightCheckpoint, ...rightIdentity } = right;
56
+ return isDeepStrictEqual(leftIdentity, rightIdentity);
57
+ }
51
58
  /**
52
59
  * Build an argv-safe launch specification for the exact persisted Pi session behind a logical
53
60
  * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-resume.js","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"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","sourcesContent":["import { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
1
+ {"version":3,"file":"agent-resume.js","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"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","sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */\nexport function sameAgentResumeIdentity(left: AgentResumeContext, right: AgentResumeContext): boolean {\n\tconst { latestCheckpointId: _leftCheckpoint, ...leftIdentity } = left;\n\tconst { latestCheckpointId: _rightCheckpoint, ...rightIdentity } = right;\n\treturn isDeepStrictEqual(leftIdentity, rightIdentity);\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { Usage } from "@caupulican/pi-ai";
2
+ import type { AttemptUsageSnapshot } from "./contracts.ts";
3
+ export declare const EMPTY_ATTEMPT_USAGE: AttemptUsageSnapshot;
4
+ /** Parse an untrusted provider-neutral usage report into a closed, known-field value. */
5
+ export declare function validateProviderUsage(value: unknown, label?: string): Usage;
6
+ /** Validate the one durable cumulative usage shape shared by checkpoints, recovery, and results. */
7
+ export declare function validateAttemptUsageSnapshot(usage: AttemptUsageSnapshot, label?: string): AttemptUsageSnapshot;
8
+ export declare function attemptUsageFromGatewayUsage(usage: {
9
+ toolCalls: number;
10
+ inputTokens: number;
11
+ outputTokens: number;
12
+ cacheReadTokens: number;
13
+ cacheWriteTokens: number;
14
+ totalTokens: number;
15
+ costUsd: number;
16
+ wallClockMs: number;
17
+ }): AttemptUsageSnapshot;
18
+ export declare function addAttemptUsage(base: AttemptUsageSnapshot, delta: AttemptUsageSnapshot, label?: string): AttemptUsageSnapshot;
19
+ /**
20
+ * Recover the strongest cumulative evidence across an atomic checkpoint and its durable transcript.
21
+ * Both inputs are cumulative views of the same attempt, so summing would double-count; component
22
+ * maxima close append-before-checkpoint crash windows conservatively.
23
+ */
24
+ export declare function reconcileAttemptUsage(checkpoint: AttemptUsageSnapshot, transcript: AttemptUsageSnapshot): AttemptUsageSnapshot;
25
+ export declare function remainingTokenBudget(maxTokens: number | undefined, usage: AttemptUsageSnapshot): number | undefined;
26
+ /** Convert durable cumulative accounting into the provider-neutral report shape exactly once. */
27
+ export declare function providerUsageFromAttemptUsage(usage: AttemptUsageSnapshot): Usage;
28
+ //# sourceMappingURL=attempt-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-usage.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/attempt-usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,eAAO,MAAM,mBAAmB,EAAE,oBASjC,CAAC;AAwBF,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAmB,GAAG,KAAK,CAyCrF;AAED,oGAAoG;AACpG,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,KAAK,SAAkB,GACrB,oBAAoB,CA0BtB;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACpB,GAAG,oBAAoB,CAcvB;AAED,wBAAgB,eAAe,CAC9B,IAAI,EAAE,oBAAoB,EAC1B,KAAK,EAAE,oBAAoB,EAC3B,KAAK,SAAkB,GACrB,oBAAoB,CAgBtB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,oBAAoB,EAChC,UAAU,EAAE,oBAAoB,GAC9B,oBAAoB,CAatB;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAInH;AAED,iGAAiG;AACjG,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,KAAK,CAUhF","sourcesContent":["import type { Usage } from \"@caupulican/pi-ai\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport type { AttemptUsageSnapshot } from \"./contracts.ts\";\n\nexport const EMPTY_ATTEMPT_USAGE: AttemptUsageSnapshot = {\n\ttoolCalls: 0,\n\tinputTokens: 0,\n\toutputTokens: 0,\n\tcacheReadTokens: 0,\n\tcacheWriteTokens: 0,\n\ttotalTokens: 0,\n\tcostUsd: 0,\n\tactiveWallClockMs: 0,\n};\n\n/**\n * Validate an untrusted provider-neutral usage report before it reaches durable accounting.\n * Provider totals are authoritative and therefore need not equal the detail field sum.\n */\nconst PROVIDER_USAGE_FIELDS = [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"totalTokens\", \"cost\"] as const;\nconst PROVIDER_USAGE_COST_FIELDS = [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"total\"] as const;\n\nfunction parseExactDataRecord(value: unknown, fields: readonly string[], label: string): Record<string, unknown> {\n\tif (!isPlainRecord(value)) throw new TypeError(`${label} must be a plain object.`);\n\tfor (const key of Reflect.ownKeys(value)) {\n\t\tif (typeof key !== \"string\" || !fields.includes(key))\n\t\t\tthrow new TypeError(`${label} contains an unsupported field.`);\n\t}\n\tconst parsed: Record<string, unknown> = {};\n\tfor (const field of fields) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, field);\n\t\tif (!descriptor || !(\"value\" in descriptor)) throw new TypeError(`${label}.${field} must be a data field.`);\n\t\tparsed[field] = descriptor.value;\n\t}\n\treturn parsed;\n}\n\n/** Parse an untrusted provider-neutral usage report into a closed, known-field value. */\nexport function validateProviderUsage(value: unknown, label = \"provider usage\"): Usage {\n\tconst usage = parseExactDataRecord(value, PROVIDER_USAGE_FIELDS, label);\n\tconst cost = parseExactDataRecord(usage.cost, PROVIDER_USAGE_COST_FIELDS, `${label}.cost`);\n\tconst tokenFields: Array<[string, number]> = [\n\t\t[\"input\", usage.input as number],\n\t\t[\"output\", usage.output as number],\n\t\t[\"cacheRead\", usage.cacheRead as number],\n\t\t[\"cacheWrite\", usage.cacheWrite as number],\n\t\t[\"totalTokens\", usage.totalTokens as number],\n\t];\n\tfor (const [field, value] of tokenFields) {\n\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\tthrow new Error(`${label}.${field} must be a non-negative safe integer.`);\n\t\t}\n\t}\n\tconst costFields: Array<[string, number]> = [\n\t\t[\"input\", cost.input as number],\n\t\t[\"output\", cost.output as number],\n\t\t[\"cacheRead\", cost.cacheRead as number],\n\t\t[\"cacheWrite\", cost.cacheWrite as number],\n\t\t[\"total\", cost.total as number],\n\t];\n\tfor (const [field, value] of costFields) {\n\t\tif (!Number.isFinite(value) || value < 0) {\n\t\t\tthrow new Error(`${label}.cost.${field} must be finite and non-negative.`);\n\t\t}\n\t}\n\treturn {\n\t\tinput: usage.input as number,\n\t\toutput: usage.output as number,\n\t\tcacheRead: usage.cacheRead as number,\n\t\tcacheWrite: usage.cacheWrite as number,\n\t\ttotalTokens: usage.totalTokens as number,\n\t\tcost: {\n\t\t\tinput: cost.input as number,\n\t\t\toutput: cost.output as number,\n\t\t\tcacheRead: cost.cacheRead as number,\n\t\t\tcacheWrite: cost.cacheWrite as number,\n\t\t\ttotal: cost.total as number,\n\t\t},\n\t};\n}\n\n/** Validate the one durable cumulative usage shape shared by checkpoints, recovery, and results. */\nexport function validateAttemptUsageSnapshot(\n\tusage: AttemptUsageSnapshot,\n\tlabel = \"attempt usage\",\n): AttemptUsageSnapshot {\n\tif (!Number.isSafeInteger(usage.toolCalls) || usage.toolCalls < 0) {\n\t\tthrow new Error(`${label}.toolCalls must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.inputTokens) || usage.inputTokens < 0) {\n\t\tthrow new Error(`${label}.inputTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.outputTokens) || usage.outputTokens < 0) {\n\t\tthrow new Error(`${label}.outputTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.cacheReadTokens) || usage.cacheReadTokens < 0) {\n\t\tthrow new Error(`${label}.cacheReadTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.cacheWriteTokens) || usage.cacheWriteTokens < 0) {\n\t\tthrow new Error(`${label}.cacheWriteTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.totalTokens) || usage.totalTokens < 0) {\n\t\tthrow new Error(`${label}.totalTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isFinite(usage.costUsd) || usage.costUsd < 0) {\n\t\tthrow new Error(`${label}.costUsd must be finite and non-negative.`);\n\t}\n\tif (!Number.isFinite(usage.activeWallClockMs) || usage.activeWallClockMs < 0) {\n\t\tthrow new Error(`${label}.activeWallClockMs must be finite and non-negative.`);\n\t}\n\treturn structuredClone(usage);\n}\n\nexport function attemptUsageFromGatewayUsage(usage: {\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}): AttemptUsageSnapshot {\n\treturn validateAttemptUsageSnapshot(\n\t\t{\n\t\t\ttoolCalls: usage.toolCalls,\n\t\t\tinputTokens: usage.inputTokens,\n\t\t\toutputTokens: usage.outputTokens,\n\t\t\tcacheReadTokens: usage.cacheReadTokens,\n\t\t\tcacheWriteTokens: usage.cacheWriteTokens,\n\t\t\ttotalTokens: usage.totalTokens,\n\t\t\tcostUsd: usage.costUsd,\n\t\t\tactiveWallClockMs: usage.wallClockMs,\n\t\t},\n\t\t\"gateway usage\",\n\t);\n}\n\nexport function addAttemptUsage(\n\tbase: AttemptUsageSnapshot,\n\tdelta: AttemptUsageSnapshot,\n\tlabel = \"attempt usage\",\n): AttemptUsageSnapshot {\n\tvalidateAttemptUsageSnapshot(base, `${label} base`);\n\tvalidateAttemptUsageSnapshot(delta, `${label} delta`);\n\treturn validateAttemptUsageSnapshot(\n\t\t{\n\t\t\ttoolCalls: base.toolCalls + delta.toolCalls,\n\t\t\tinputTokens: base.inputTokens + delta.inputTokens,\n\t\t\toutputTokens: base.outputTokens + delta.outputTokens,\n\t\t\tcacheReadTokens: base.cacheReadTokens + delta.cacheReadTokens,\n\t\t\tcacheWriteTokens: base.cacheWriteTokens + delta.cacheWriteTokens,\n\t\t\ttotalTokens: base.totalTokens + delta.totalTokens,\n\t\t\tcostUsd: base.costUsd + delta.costUsd,\n\t\t\tactiveWallClockMs: base.activeWallClockMs + delta.activeWallClockMs,\n\t\t},\n\t\tlabel,\n\t);\n}\n\n/**\n * Recover the strongest cumulative evidence across an atomic checkpoint and its durable transcript.\n * Both inputs are cumulative views of the same attempt, so summing would double-count; component\n * maxima close append-before-checkpoint crash windows conservatively.\n */\nexport function reconcileAttemptUsage(\n\tcheckpoint: AttemptUsageSnapshot,\n\ttranscript: AttemptUsageSnapshot,\n): AttemptUsageSnapshot {\n\tvalidateAttemptUsageSnapshot(checkpoint, \"attempt checkpoint usage\");\n\tvalidateAttemptUsageSnapshot(transcript, \"attempt transcript usage\");\n\treturn validateAttemptUsageSnapshot({\n\t\ttoolCalls: Math.max(checkpoint.toolCalls, transcript.toolCalls),\n\t\tinputTokens: Math.max(checkpoint.inputTokens, transcript.inputTokens),\n\t\toutputTokens: Math.max(checkpoint.outputTokens, transcript.outputTokens),\n\t\tcacheReadTokens: Math.max(checkpoint.cacheReadTokens, transcript.cacheReadTokens),\n\t\tcacheWriteTokens: Math.max(checkpoint.cacheWriteTokens, transcript.cacheWriteTokens),\n\t\ttotalTokens: Math.max(checkpoint.totalTokens, transcript.totalTokens),\n\t\tcostUsd: Math.max(checkpoint.costUsd, transcript.costUsd),\n\t\tactiveWallClockMs: Math.max(checkpoint.activeWallClockMs, transcript.activeWallClockMs),\n\t});\n}\n\nexport function remainingTokenBudget(maxTokens: number | undefined, usage: AttemptUsageSnapshot): number | undefined {\n\tif (maxTokens === undefined) return undefined;\n\tconst remaining = maxTokens - usage.totalTokens;\n\treturn Math.max(0, remaining);\n}\n\n/** Convert durable cumulative accounting into the provider-neutral report shape exactly once. */\nexport function providerUsageFromAttemptUsage(usage: AttemptUsageSnapshot): Usage {\n\tconst validated = validateAttemptUsageSnapshot(usage);\n\treturn {\n\t\tinput: validated.inputTokens,\n\t\toutput: validated.outputTokens,\n\t\tcacheRead: validated.cacheReadTokens,\n\t\tcacheWrite: validated.cacheWriteTokens,\n\t\ttotalTokens: validated.totalTokens,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: validated.costUsd },\n\t};\n}\n"]}