@caupulican/pi-adaptative 0.85.3 → 0.85.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/core/agent-session-services.d.ts.map +1 -1
  3. package/dist/core/agent-session-services.js +2 -0
  4. package/dist/core/agent-session-services.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +6 -5
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +76 -87
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auth-guidance.d.ts.map +1 -1
  10. package/dist/core/auth-guidance.js +3 -0
  11. package/dist/core/auth-guidance.js.map +1 -1
  12. package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
  13. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  14. package/dist/core/autonomy/lane-tool-surface.js +4 -3
  15. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  16. package/dist/core/background-lane-controller.d.ts +4 -0
  17. package/dist/core/background-lane-controller.d.ts.map +1 -1
  18. package/dist/core/background-lane-controller.js +2 -0
  19. package/dist/core/background-lane-controller.js.map +1 -1
  20. package/dist/core/background-tool-task-controller.d.ts +12 -7
  21. package/dist/core/background-tool-task-controller.d.ts.map +1 -1
  22. package/dist/core/background-tool-task-controller.js +55 -19
  23. package/dist/core/background-tool-task-controller.js.map +1 -1
  24. package/dist/core/bedrock-scope.d.ts +26 -0
  25. package/dist/core/bedrock-scope.d.ts.map +1 -0
  26. package/dist/core/bedrock-scope.js +178 -0
  27. package/dist/core/bedrock-scope.js.map +1 -0
  28. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  29. package/dist/core/delegation/worker-delegation-controller.js +6 -0
  30. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  31. package/dist/core/extensions/loader.d.ts.map +1 -1
  32. package/dist/core/extensions/loader.js +3 -0
  33. package/dist/core/extensions/loader.js.map +1 -1
  34. package/dist/core/foreground-recovery-controller.d.ts +25 -2
  35. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  36. package/dist/core/foreground-recovery-controller.js +111 -2
  37. package/dist/core/foreground-recovery-controller.js.map +1 -1
  38. package/dist/core/foreground-terminal-handoff-controller.d.ts +27 -0
  39. package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -0
  40. package/dist/core/foreground-terminal-handoff-controller.js +56 -0
  41. package/dist/core/foreground-terminal-handoff-controller.js.map +1 -0
  42. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  43. package/dist/core/goal-loop-controller.js +5 -0
  44. package/dist/core/goal-loop-controller.js.map +1 -1
  45. package/dist/core/goals/goal-auto-continue-controller.d.ts +3 -0
  46. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  47. package/dist/core/goals/goal-auto-continue-controller.js +28 -7
  48. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  49. package/dist/core/human-input-controller.d.ts +9 -0
  50. package/dist/core/human-input-controller.d.ts.map +1 -1
  51. package/dist/core/human-input-controller.js +60 -5
  52. package/dist/core/human-input-controller.js.map +1 -1
  53. package/dist/core/human-input.d.ts +12 -0
  54. package/dist/core/human-input.d.ts.map +1 -1
  55. package/dist/core/human-input.js +59 -12
  56. package/dist/core/human-input.js.map +1 -1
  57. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  58. package/dist/core/local-runtime-controller.js +15 -9
  59. package/dist/core/local-runtime-controller.js.map +1 -1
  60. package/dist/core/memory/providers/file-store.d.ts +1 -0
  61. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  62. package/dist/core/memory/providers/file-store.js +252 -125
  63. package/dist/core/memory/providers/file-store.js.map +1 -1
  64. package/dist/core/model-capability.d.ts +15 -1
  65. package/dist/core/model-capability.d.ts.map +1 -1
  66. package/dist/core/model-capability.js +24 -1
  67. package/dist/core/model-capability.js.map +1 -1
  68. package/dist/core/model-registry.d.ts +5 -0
  69. package/dist/core/model-registry.d.ts.map +1 -1
  70. package/dist/core/model-registry.js +18 -0
  71. package/dist/core/model-registry.js.map +1 -1
  72. package/dist/core/model-router-controller.d.ts +6 -6
  73. package/dist/core/model-router-controller.d.ts.map +1 -1
  74. package/dist/core/model-router-controller.js +5 -5
  75. package/dist/core/model-router-controller.js.map +1 -1
  76. package/dist/core/models/context-sizing.d.ts +1 -0
  77. package/dist/core/models/context-sizing.d.ts.map +1 -1
  78. package/dist/core/models/context-sizing.js +4 -1
  79. package/dist/core/models/context-sizing.js.map +1 -1
  80. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  81. package/dist/core/models/default-model-suggestions.js +20 -10
  82. package/dist/core/models/default-model-suggestions.js.map +1 -1
  83. package/dist/core/models/llamacpp-runtime.d.ts +32 -11
  84. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -1
  85. package/dist/core/models/llamacpp-runtime.js +79 -28
  86. package/dist/core/models/llamacpp-runtime.js.map +1 -1
  87. package/dist/core/models/local-execution-planner.d.ts +24 -0
  88. package/dist/core/models/local-execution-planner.d.ts.map +1 -0
  89. package/dist/core/models/local-execution-planner.js +119 -0
  90. package/dist/core/models/local-execution-planner.js.map +1 -0
  91. package/dist/core/models/local-inference-profile.d.ts +34 -0
  92. package/dist/core/models/local-inference-profile.d.ts.map +1 -0
  93. package/dist/core/models/local-inference-profile.js +74 -0
  94. package/dist/core/models/local-inference-profile.js.map +1 -0
  95. package/dist/core/models/local-runtime.d.ts +23 -4
  96. package/dist/core/models/local-runtime.d.ts.map +1 -1
  97. package/dist/core/models/local-runtime.js +11 -8
  98. package/dist/core/models/local-runtime.js.map +1 -1
  99. package/dist/core/models/managed-ollama-model.d.ts +26 -0
  100. package/dist/core/models/managed-ollama-model.d.ts.map +1 -0
  101. package/dist/core/models/managed-ollama-model.js +59 -0
  102. package/dist/core/models/managed-ollama-model.js.map +1 -0
  103. package/dist/core/models/model-ref.js +11 -11
  104. package/dist/core/models/model-ref.js.map +1 -1
  105. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +10 -20
  106. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -1
  107. package/dist/core/models/prism-llamacpp-lifecycle.js +11 -31
  108. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -1
  109. package/dist/core/models/prism-llamacpp-server-probe.d.ts +10 -0
  110. package/dist/core/models/prism-llamacpp-server-probe.d.ts.map +1 -0
  111. package/dist/core/models/prism-llamacpp-server-probe.js +36 -0
  112. package/dist/core/models/prism-llamacpp-server-probe.js.map +1 -0
  113. package/dist/core/models/runtime-arbiter.d.ts +3 -0
  114. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  115. package/dist/core/models/runtime-arbiter.js +2 -1
  116. package/dist/core/models/runtime-arbiter.js.map +1 -1
  117. package/dist/core/research/research-lane-controller.d.ts.map +1 -1
  118. package/dist/core/research/research-lane-controller.js +14 -5
  119. package/dist/core/research/research-lane-controller.js.map +1 -1
  120. package/dist/core/runtime-builder.d.ts +5 -1
  121. package/dist/core/runtime-builder.d.ts.map +1 -1
  122. package/dist/core/runtime-builder.js +37 -4
  123. package/dist/core/runtime-builder.js.map +1 -1
  124. package/dist/core/scout-controller.d.ts +3 -2
  125. package/dist/core/scout-controller.d.ts.map +1 -1
  126. package/dist/core/scout-controller.js +1 -0
  127. package/dist/core/scout-controller.js.map +1 -1
  128. package/dist/core/sdk.d.ts.map +1 -1
  129. package/dist/core/sdk.js +10 -8
  130. package/dist/core/sdk.js.map +1 -1
  131. package/dist/core/settings-manager.d.ts +13 -0
  132. package/dist/core/settings-manager.d.ts.map +1 -1
  133. package/dist/core/settings-manager.js +59 -0
  134. package/dist/core/settings-manager.js.map +1 -1
  135. package/dist/core/system-prompt-builder.d.ts +5 -2
  136. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  137. package/dist/core/system-prompt-builder.js +22 -10
  138. package/dist/core/system-prompt-builder.js.map +1 -1
  139. package/dist/core/system-prompt.d.ts +3 -0
  140. package/dist/core/system-prompt.d.ts.map +1 -1
  141. package/dist/core/system-prompt.js +181 -55
  142. package/dist/core/system-prompt.js.map +1 -1
  143. package/dist/core/tool-protocol-controller.d.ts.map +1 -1
  144. package/dist/core/tool-protocol-controller.js +5 -3
  145. package/dist/core/tool-protocol-controller.js.map +1 -1
  146. package/dist/core/tools/edit-diff.d.ts +2 -0
  147. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  148. package/dist/core/tools/edit-diff.js +9 -1
  149. package/dist/core/tools/edit-diff.js.map +1 -1
  150. package/dist/core/tools/edit.d.ts +5 -8
  151. package/dist/core/tools/edit.d.ts.map +1 -1
  152. package/dist/core/tools/edit.js +88 -55
  153. package/dist/core/tools/edit.js.map +1 -1
  154. package/dist/core/tools/file-mutation-intent.d.ts +42 -15
  155. package/dist/core/tools/file-mutation-intent.d.ts.map +1 -1
  156. package/dist/core/tools/file-mutation-intent.js +271 -77
  157. package/dist/core/tools/file-mutation-intent.js.map +1 -1
  158. package/dist/core/tools/index.d.ts +1 -1
  159. package/dist/core/tools/index.d.ts.map +1 -1
  160. package/dist/core/tools/index.js +1 -1
  161. package/dist/core/tools/index.js.map +1 -1
  162. package/dist/core/tools/write.d.ts +5 -10
  163. package/dist/core/tools/write.d.ts.map +1 -1
  164. package/dist/core/tools/write.js +92 -73
  165. package/dist/core/tools/write.js.map +1 -1
  166. package/dist/index.d.ts +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +1 -1
  169. package/dist/index.js.map +1 -1
  170. package/dist/modes/interactive/auth-dialogs-controller.d.ts +6 -0
  171. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
  172. package/dist/modes/interactive/auth-dialogs-controller.js +59 -12
  173. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
  174. package/dist/modes/interactive/local-model-commands.d.ts +1 -1
  175. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  176. package/dist/modes/interactive/local-model-commands.js +51 -8
  177. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  178. package/docs/providers.md +34 -10
  179. package/docs/tool-boundary-performance-roadmap-2026-08-02.md +21 -18
  180. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  181. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  182. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  183. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  184. package/examples/extensions/minimal-mode.ts +1 -1
  185. package/examples/extensions/prompt-customizer.ts +2 -2
  186. package/examples/extensions/sandbox/package-lock.json +2 -2
  187. package/examples/extensions/sandbox/package.json +1 -1
  188. package/examples/extensions/ssh.ts +17 -0
  189. package/examples/extensions/with-deps/package-lock.json +2 -2
  190. package/examples/extensions/with-deps/package.json +1 -1
  191. package/npm-shrinkwrap.json +240 -260
  192. package/package.json +6 -6
@@ -1,13 +1,38 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { constants } from "node:fs";
3
- import { access, copyFile, lstat, open, stat, unlink } from "node:fs/promises";
4
- import { dirname, resolve } from "node:path";
5
- const DEFAULT_INTENT_LIMIT = 64;
2
+ import { constants, rmSync } from "node:fs";
3
+ import { access, copyFile, lstat, mkdtemp, open, readFile, stat, unlink, writeFile } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
6
  const DEFAULT_CONTENT_REFERENCE_LIMIT = 64;
7
- const DEFAULT_INTENT_TTL_MS = 10 * 60 * 1000;
8
7
  const DEFAULT_CONTENT_REFERENCE_TTL_MS = 60 * 60 * 1000;
8
+ const DEFAULT_MUTATION_PAYLOAD_LIMIT = 8;
9
+ const DEFAULT_MUTATION_PAYLOAD_BYTE_LIMIT = 16 * 1024 * 1024;
10
+ const DEFAULT_MUTATION_PAYLOAD_TTL_MS = 10 * 60 * 1000;
9
11
  const HASH_BUFFER_BYTES = 64 * 1024;
10
12
  const CONTENT_REFERENCE_PREFIX = "file-content:";
13
+ const MUTATION_PAYLOAD_REFERENCE_PREFIX = "file-mutation:";
14
+ const LOCAL_MUTATION_PAYLOAD_DIRECTORY_PREFIX = "pi-file-mutation-payloads-";
15
+ const LOCAL_MUTATION_PAYLOAD_PROCESS_BYTE_LIMIT = 64 * 1024 * 1024;
16
+ const LOCAL_MUTATION_PAYLOAD_PROCESS_FILE_LIMIT = 64;
17
+ let localMutationPayloadDirectoryPromise;
18
+ let localMutationPayloadDirectory;
19
+ let localMutationPayloadCleanupRegistered = false;
20
+ let localMutationPayloadOperationTail = Promise.resolve();
21
+ let localMutationPayloadBytes = 0;
22
+ const localMutationPayloadFiles = new Map();
23
+ class MutationPayloadCapacityError extends Error {
24
+ constructor() {
25
+ super("Process mutation payload cache is full.");
26
+ this.name = "MutationPayloadCapacityError";
27
+ }
28
+ }
29
+ export class FileMutationPreflightError extends Error {
30
+ constructor(reason, message) {
31
+ super(message);
32
+ this.name = "FileMutationPreflightError";
33
+ this.reason = reason;
34
+ }
35
+ }
11
36
  function isMissingPathError(error) {
12
37
  return (typeof error === "object" &&
13
38
  error !== null &&
@@ -19,10 +44,6 @@ function errorCode(error) {
19
44
  ? error.code
20
45
  : undefined;
21
46
  }
22
- function normalizedPath(path) {
23
- const absolutePath = resolve(path);
24
- return process.platform === "win32" ? absolutePath.toLocaleLowerCase("en-US") : absolutePath;
25
- }
26
47
  function positiveBound(value, fallback, label) {
27
48
  const resolvedValue = value ?? fallback;
28
49
  if (!Number.isSafeInteger(resolvedValue) || resolvedValue < 1) {
@@ -85,27 +106,95 @@ async function hashLocalFile(path, signal) {
85
106
  await handle.close();
86
107
  }
87
108
  }
109
+ async function getLocalMutationPayloadDirectory() {
110
+ localMutationPayloadDirectoryPromise ??= mkdtemp(join(tmpdir(), LOCAL_MUTATION_PAYLOAD_DIRECTORY_PREFIX)).then((path) => {
111
+ localMutationPayloadDirectory = path;
112
+ if (!localMutationPayloadCleanupRegistered) {
113
+ localMutationPayloadCleanupRegistered = true;
114
+ process.once("exit", () => {
115
+ if (localMutationPayloadDirectory) {
116
+ try {
117
+ rmSync(localMutationPayloadDirectory, { recursive: true, force: true });
118
+ }
119
+ catch {
120
+ // Process shutdown cannot safely retry filesystem cleanup.
121
+ }
122
+ }
123
+ });
124
+ }
125
+ return path;
126
+ });
127
+ return localMutationPayloadDirectoryPromise;
128
+ }
129
+ async function stageLocalMutationPayload(content, ttlMs) {
130
+ const byteLength = Buffer.byteLength(content, "utf8");
131
+ let stagedPath = "";
132
+ const operation = localMutationPayloadOperationTail.then(async () => {
133
+ for (const [path, record] of localMutationPayloadFiles) {
134
+ if (record.expiresAt > Date.now())
135
+ continue;
136
+ localMutationPayloadFiles.delete(path);
137
+ localMutationPayloadBytes -= record.byteLength;
138
+ try {
139
+ await unlink(path);
140
+ }
141
+ catch (error) {
142
+ if (!isMissingPathError(error))
143
+ throw error;
144
+ }
145
+ }
146
+ if (localMutationPayloadFiles.size >= LOCAL_MUTATION_PAYLOAD_PROCESS_FILE_LIMIT ||
147
+ localMutationPayloadBytes + byteLength > LOCAL_MUTATION_PAYLOAD_PROCESS_BYTE_LIMIT) {
148
+ throw new MutationPayloadCapacityError();
149
+ }
150
+ stagedPath = join(await getLocalMutationPayloadDirectory(), randomUUID());
151
+ await writeFile(stagedPath, content, { encoding: "utf8", flag: "wx", mode: 0o600 });
152
+ localMutationPayloadFiles.set(stagedPath, { byteLength, expiresAt: Date.now() + ttlMs });
153
+ localMutationPayloadBytes += byteLength;
154
+ });
155
+ localMutationPayloadOperationTail = operation.catch(() => { });
156
+ await operation;
157
+ return stagedPath;
158
+ }
159
+ async function removeLocalMutationPayloadOrFile(path) {
160
+ const operation = localMutationPayloadOperationTail.then(async () => {
161
+ const record = localMutationPayloadFiles.get(path);
162
+ if (record !== undefined) {
163
+ localMutationPayloadFiles.delete(path);
164
+ localMutationPayloadBytes -= record.byteLength;
165
+ }
166
+ await unlink(path);
167
+ });
168
+ localMutationPayloadOperationTail = operation.catch(() => { });
169
+ await operation;
170
+ }
88
171
  export const localFileMutationIntentOperations = {
89
172
  inspect: inspectLocalPath,
90
173
  access,
91
174
  copyFileExclusive: (sourcePath, targetPath) => copyFile(sourcePath, targetPath, constants.COPYFILE_EXCL),
92
175
  hashFile: hashLocalFile,
93
- removeFile: unlink,
176
+ readPayload: (path) => readFile(path, "utf8"),
177
+ removeFile: removeLocalMutationPayloadOrFile,
178
+ stagePayload: stageLocalMutationPayload,
94
179
  };
95
180
  /**
96
- * Session-owned authority for two-phase file mutation intents and bounded exact-content references.
97
- * It retains only fixed-size metadata; file bytes remain owned by the filesystem.
181
+ * Session-owned harness authority for file-mutation preflight, stale-target checks,
182
+ * and bounded exact-content references. It retains only fixed-size metadata; file
183
+ * bytes remain owned by the filesystem.
98
184
  */
99
185
  export class FileMutationIntentController {
100
186
  constructor(options = {}) {
101
- this.intents = new Map();
102
- this.intentByPath = new Map();
103
187
  this.contentReferences = new Map();
188
+ this.mutationPayloads = new Map();
189
+ this.mutationPayloadBytes = 0;
190
+ this.lastMutationPayloadExpiresAt = 0;
191
+ this.mutationPayloadOperationTail = Promise.resolve();
104
192
  this.operations = options.operations ?? localFileMutationIntentOperations;
105
- this.intentLimit = positiveBound(options.intentLimit, DEFAULT_INTENT_LIMIT, "Intent limit");
106
193
  this.contentReferenceLimit = positiveBound(options.contentReferenceLimit, DEFAULT_CONTENT_REFERENCE_LIMIT, "Content reference limit");
107
- this.intentTtlMs = positiveBound(options.intentTtlMs, DEFAULT_INTENT_TTL_MS, "Intent TTL");
108
194
  this.contentReferenceTtlMs = positiveBound(options.contentReferenceTtlMs, DEFAULT_CONTENT_REFERENCE_TTL_MS, "Content reference TTL");
195
+ this.mutationPayloadLimit = positiveBound(options.mutationPayloadLimit, DEFAULT_MUTATION_PAYLOAD_LIMIT, "Mutation payload limit");
196
+ this.mutationPayloadByteLimit = positiveBound(options.mutationPayloadByteLimit, DEFAULT_MUTATION_PAYLOAD_BYTE_LIMIT, "Mutation payload byte limit");
197
+ this.mutationPayloadTtlMs = positiveBound(options.mutationPayloadTtlMs, DEFAULT_MUTATION_PAYLOAD_TTL_MS, "Mutation payload TTL");
109
198
  this.now = options.now ?? Date.now;
110
199
  }
111
200
  async prepare(kind, path, signal, displayPath = path) {
@@ -116,16 +205,19 @@ export class FileMutationIntentController {
116
205
  let identity;
117
206
  if (kind === "write") {
118
207
  const existing = await this.operations.inspect(absolutePath, false);
119
- if (existing)
120
- throw new Error(`Write collision: ${displayPath} already exists.`);
208
+ if (existing) {
209
+ throw new FileMutationPreflightError("write_collision", `Write collision: ${displayPath} already exists.`);
210
+ }
121
211
  await this.assertCreatableParent(absolutePath);
122
212
  }
123
213
  else {
124
214
  const existing = await this.operations.inspect(absolutePath, true);
125
- if (!existing)
126
- throw new Error(`Could not edit file: ${displayPath}. Error code: ENOENT.`);
127
- if (existing.kind !== "file")
128
- throw new Error(`Could not edit file: ${displayPath}. Target is not a regular file.`);
215
+ if (!existing) {
216
+ throw new FileMutationPreflightError("edit_missing", `Could not edit file: ${displayPath}. Error code: ENOENT.`);
217
+ }
218
+ if (existing.kind !== "file") {
219
+ throw new FileMutationPreflightError("edit_not_file", `Could not edit file: ${displayPath}. Target is not a regular file.`);
220
+ }
129
221
  try {
130
222
  await this.operations.access(absolutePath, constants.R_OK | constants.W_OK);
131
223
  }
@@ -137,46 +229,11 @@ export class FileMutationIntentController {
137
229
  }
138
230
  if (signal?.aborted)
139
231
  throw new Error("Operation aborted");
140
- const key = `${kind}\0${normalizedPath(absolutePath)}`;
141
- const priorIntentId = this.intentByPath.get(key);
142
- if (priorIntentId)
143
- this.deleteIntent(priorIntentId);
144
- while (this.intents.size >= this.intentLimit) {
145
- const oldestIntentId = this.intents.keys().next().value;
146
- if (typeof oldestIntentId !== "string")
147
- break;
148
- this.deleteIntent(oldestIntentId);
149
- }
150
- const intentId = randomUUID();
151
- const record = {
152
- intentId,
232
+ return {
153
233
  kind,
154
234
  path: absolutePath,
155
235
  displayPath,
156
236
  ...(identity ? { identity } : {}),
157
- expiresAt: this.now() + this.intentTtlMs,
158
- };
159
- this.intents.set(intentId, record);
160
- this.intentByPath.set(key, intentId);
161
- return { intentId, kind, path: absolutePath, displayPath };
162
- }
163
- consume(intentId, kind, path) {
164
- this.pruneExpired();
165
- const record = this.intents.get(intentId);
166
- if (!record)
167
- throw new Error("File mutation intent is invalid, expired, or belongs to another session.");
168
- this.deleteIntent(intentId);
169
- if (record.kind !== kind)
170
- throw new Error(`File mutation intent is for ${record.kind}, not ${kind}.`);
171
- if (normalizedPath(record.path) !== normalizedPath(path)) {
172
- throw new Error("File mutation intent path does not match the commit path.");
173
- }
174
- return {
175
- intentId: record.intentId,
176
- kind: record.kind,
177
- path: record.path,
178
- displayPath: record.displayPath,
179
- ...(record.identity ? { identity: record.identity } : {}),
180
237
  };
181
238
  }
182
239
  async assertCurrent(lease, signal) {
@@ -184,16 +241,16 @@ export class FileMutationIntentController {
184
241
  throw new Error("Operation aborted");
185
242
  if (lease.kind === "write") {
186
243
  if (await this.operations.inspect(lease.path, false)) {
187
- throw new Error(`Write collision: ${lease.displayPath} already exists; the prepared intent is stale.`);
244
+ throw new FileMutationPreflightError("write_collision", `Write collision: ${lease.displayPath} became occupied after preflight; no content was overwritten.`);
188
245
  }
189
246
  return;
190
247
  }
191
248
  const current = await this.operations.inspect(lease.path, true);
192
249
  if (!current || current.kind !== "file") {
193
- throw new Error(`Could not edit file: ${lease.displayPath}. The prepared target no longer exists.`);
250
+ throw new Error(`Could not edit file: ${lease.displayPath}. The target no longer exists after preflight.`);
194
251
  }
195
252
  if (!identitiesMatch(lease.identity, current.identity)) {
196
- throw new Error(`Could not edit file: ${lease.displayPath}. The file changed after preparation; prepare again.`);
253
+ throw new Error(`Could not edit file: ${lease.displayPath}. The file changed during edit execution; read it again before retrying.`);
197
254
  }
198
255
  try {
199
256
  await this.operations.access(lease.path, constants.R_OK | constants.W_OK);
@@ -207,7 +264,97 @@ export class FileMutationIntentController {
207
264
  const digest = createHash("sha256").update(content, "utf8").digest("hex");
208
265
  return this.rememberContentDigest(sourcePath, digest, Buffer.byteLength(content, "utf8"));
209
266
  }
267
+ async retainMutationPayload(kind, payload) {
268
+ return this.runMutationPayloadOperation(async () => {
269
+ await this.pruneExpiredMutationPayloads();
270
+ const byteLength = Buffer.byteLength(payload, "utf8");
271
+ if (byteLength > this.mutationPayloadByteLimit)
272
+ return undefined;
273
+ while (this.mutationPayloads.size >= this.mutationPayloadLimit ||
274
+ this.mutationPayloadBytes + byteLength > this.mutationPayloadByteLimit) {
275
+ const oldestPayloadRef = this.mutationPayloads.keys().next().value;
276
+ if (typeof oldestPayloadRef !== "string")
277
+ return undefined;
278
+ await this.deleteMutationPayload(oldestPayloadRef, true);
279
+ }
280
+ // Keep insertion and expiry order identical, even if the wall clock moves backward. Expiry
281
+ // pruning can then stop at the first live record instead of rescanning every retained payload.
282
+ const expiresAt = Math.max(this.lastMutationPayloadExpiresAt, this.now() + this.mutationPayloadTtlMs);
283
+ this.lastMutationPayloadExpiresAt = expiresAt;
284
+ let sourcePath;
285
+ try {
286
+ sourcePath = await this.operations.stagePayload(payload, this.mutationPayloadTtlMs);
287
+ }
288
+ catch (error) {
289
+ if (error instanceof MutationPayloadCapacityError)
290
+ return undefined;
291
+ throw error;
292
+ }
293
+ const payloadRef = `${MUTATION_PAYLOAD_REFERENCE_PREFIX}${randomUUID()}`;
294
+ this.mutationPayloads.set(payloadRef, {
295
+ payloadRef,
296
+ kind,
297
+ sourcePath,
298
+ digest: createHash("sha256").update(payload, "utf8").digest("hex"),
299
+ byteLength,
300
+ expiresAt,
301
+ });
302
+ this.mutationPayloadBytes += byteLength;
303
+ return { payloadRef, byteLength };
304
+ });
305
+ }
306
+ async readMutationPayload(payloadRef, kind, signal) {
307
+ return this.runMutationPayloadOperation(async () => {
308
+ const record = await this.requireMutationPayload(payloadRef, kind, signal);
309
+ const payload = await this.operations.readPayload(record.sourcePath);
310
+ if (createHash("sha256").update(payload, "utf8").digest("hex") !== record.digest) {
311
+ await this.deleteMutationPayload(payloadRef);
312
+ throw new Error("Retained file mutation payload changed after it was cached.");
313
+ }
314
+ return payload;
315
+ });
316
+ }
317
+ async assertMutationPayload(payloadRef, kind, signal) {
318
+ await this.runMutationPayloadOperation(async () => {
319
+ await this.requireVerifiedMutationPayload(payloadRef, kind, signal);
320
+ });
321
+ }
322
+ async assertContentReference(contentRef, signal) {
323
+ await this.requireVerifiedContentReference(contentRef, signal);
324
+ }
325
+ async copyMutationPayload(payloadRef, targetPath, signal) {
326
+ return this.runMutationPayloadOperation(async () => {
327
+ const record = await this.requireVerifiedMutationPayload(payloadRef, "write", signal);
328
+ await this.copyVerifiedSource(record, targetPath, signal);
329
+ await this.deleteMutationPayload(payloadRef);
330
+ return this.rememberContentDigest(targetPath, record.digest, record.byteLength);
331
+ });
332
+ }
333
+ async discardMutationPayload(payloadRef) {
334
+ await this.runMutationPayloadOperation(() => this.deleteMutationPayload(payloadRef));
335
+ }
336
+ /** Release every payload owned by this controller without touching another controller's files. */
337
+ async dispose() {
338
+ await this.runMutationPayloadOperation(async () => {
339
+ let firstError;
340
+ for (const payloadRef of [...this.mutationPayloads.keys()]) {
341
+ try {
342
+ await this.deleteMutationPayload(payloadRef, true);
343
+ }
344
+ catch (error) {
345
+ firstError ??= error;
346
+ }
347
+ }
348
+ if (firstError)
349
+ throw firstError;
350
+ });
351
+ }
210
352
  async copyReferencedContent(contentRef, targetPath, signal) {
353
+ const record = await this.requireVerifiedContentReference(contentRef, signal);
354
+ await this.copyVerifiedSource(record, targetPath, signal);
355
+ return this.rememberContentDigest(targetPath, record.digest, record.byteLength);
356
+ }
357
+ async requireVerifiedContentReference(contentRef, signal) {
211
358
  this.pruneExpired();
212
359
  const record = this.contentReferences.get(contentRef);
213
360
  if (!record)
@@ -215,17 +362,51 @@ export class FileMutationIntentController {
215
362
  this.touchContentReference(record);
216
363
  if (signal?.aborted)
217
364
  throw new Error("Operation aborted");
365
+ try {
366
+ await this.verifySource(record, signal, "File content reference source");
367
+ }
368
+ catch (error) {
369
+ this.contentReferences.delete(contentRef);
370
+ throw error;
371
+ }
372
+ return record;
373
+ }
374
+ async requireMutationPayload(payloadRef, kind, signal) {
375
+ await this.pruneExpiredMutationPayloads();
376
+ if (signal?.aborted)
377
+ throw new Error("Operation aborted");
378
+ const record = this.mutationPayloads.get(payloadRef);
379
+ if (!record) {
380
+ throw new Error("File mutation payload reference is invalid, expired, or belongs to another session.");
381
+ }
382
+ if (record.kind !== kind) {
383
+ throw new Error(`File mutation payload reference is for ${record.kind}, not ${kind}.`);
384
+ }
385
+ return record;
386
+ }
387
+ async requireVerifiedMutationPayload(payloadRef, kind, signal) {
388
+ const record = await this.requireMutationPayload(payloadRef, kind, signal);
389
+ try {
390
+ await this.verifySource(record, signal, "Retained file mutation payload");
391
+ }
392
+ catch (error) {
393
+ await this.deleteMutationPayload(payloadRef);
394
+ throw error;
395
+ }
396
+ return record;
397
+ }
398
+ async verifySource(record, signal, label) {
218
399
  let sourceDigest;
219
400
  try {
220
401
  sourceDigest = await this.operations.hashFile(record.sourcePath, signal);
221
402
  }
222
403
  catch (error) {
223
- throw new Error(`File content reference source is unavailable: ${String(error)}.`);
224
- }
225
- if (sourceDigest !== record.digest) {
226
- this.contentReferences.delete(contentRef);
227
- throw new Error("File content reference source changed after it was cached.");
404
+ throw new Error(`${label} is unavailable: ${String(error)}.`);
228
405
  }
406
+ if (sourceDigest !== record.digest)
407
+ throw new Error(`${label} changed after it was cached.`);
408
+ }
409
+ async copyVerifiedSource(record, targetPath, signal) {
229
410
  await this.operations.copyFileExclusive(record.sourcePath, targetPath);
230
411
  let targetDigest;
231
412
  try {
@@ -239,7 +420,6 @@ export class FileMutationIntentController {
239
420
  await this.removeFailedCopy(targetPath, new Error("copied bytes did not match the cached digest"));
240
421
  throw new Error("File content copy changed while it was being created; the target was removed.");
241
422
  }
242
- return this.rememberContentDigest(targetPath, record.digest, record.byteLength);
243
423
  }
244
424
  async assertCreatableParent(path) {
245
425
  let candidate = dirname(path);
@@ -301,21 +481,35 @@ export class FileMutationIntentController {
301
481
  throw new Error(`Copied content verification failed (${String(cause)}), and the newly created target could not be removed: ${String(cleanupError)}.`);
302
482
  }
303
483
  }
304
- deleteIntent(intentId) {
305
- const record = this.intents.get(intentId);
484
+ async deleteMutationPayload(payloadRef, strict = false) {
485
+ const record = this.mutationPayloads.get(payloadRef);
306
486
  if (!record)
307
487
  return;
308
- this.intents.delete(intentId);
309
- const key = `${record.kind}\0${normalizedPath(record.path)}`;
310
- if (this.intentByPath.get(key) === intentId)
311
- this.intentByPath.delete(key);
488
+ this.mutationPayloads.delete(payloadRef);
489
+ this.mutationPayloadBytes -= record.byteLength;
490
+ try {
491
+ await this.operations.removeFile(record.sourcePath);
492
+ }
493
+ catch (error) {
494
+ if (strict && !isMissingPathError(error))
495
+ throw error;
496
+ }
312
497
  }
313
- pruneExpired() {
498
+ runMutationPayloadOperation(operation) {
499
+ const run = this.mutationPayloadOperationTail.then(operation, operation);
500
+ this.mutationPayloadOperationTail = run.then(() => undefined, () => undefined);
501
+ return run;
502
+ }
503
+ async pruneExpiredMutationPayloads() {
314
504
  const now = this.now();
315
- for (const [intentId, record] of this.intents) {
316
- if (record.expiresAt <= now)
317
- this.deleteIntent(intentId);
505
+ for (const [payloadRef, record] of this.mutationPayloads) {
506
+ if (record.expiresAt > now)
507
+ break;
508
+ await this.deleteMutationPayload(payloadRef, true);
318
509
  }
510
+ }
511
+ pruneExpired() {
512
+ const now = this.now();
319
513
  for (const [contentRef, record] of this.contentReferences) {
320
514
  if (record.expiresAt <= now)
321
515
  this.contentReferences.delete(contentRef);
@@ -1 +1 @@
1
- {"version":3,"file":"file-mutation-intent.js","sourceRoot":"","sources":["../../../src/core/tools/file-mutation-intent.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { constants } from \"node:fs\";\nimport { access, copyFile, lstat, open, stat, unlink } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\n\nconst DEFAULT_INTENT_LIMIT = 64;\nconst DEFAULT_CONTENT_REFERENCE_LIMIT = 64;\nconst DEFAULT_INTENT_TTL_MS = 10 * 60 * 1000;\nconst DEFAULT_CONTENT_REFERENCE_TTL_MS = 60 * 60 * 1000;\nconst HASH_BUFFER_BYTES = 64 * 1024;\nconst CONTENT_REFERENCE_PREFIX = \"file-content:\";\n\nexport type FileMutationKind = \"write\" | \"edit\";\n\nexport interface FilePathIdentity {\n\tdev: string;\n\tino: string;\n\tmode: string;\n\tsize: string;\n\tmtimeMs: string;\n\tctimeMs: string;\n}\n\nexport interface FilePathInspection {\n\tkind: \"file\" | \"directory\" | \"other\";\n\tidentity: FilePathIdentity;\n}\n\nexport interface FileMutationIntentOperations {\n\tinspect(path: string, followSymlinks: boolean): Promise<FilePathInspection | undefined>;\n\taccess(path: string, mode: number): Promise<void>;\n\tcopyFileExclusive(sourcePath: string, targetPath: string): Promise<void>;\n\thashFile(path: string, signal?: AbortSignal): Promise<string>;\n\tremoveFile(path: string): Promise<void>;\n}\n\nexport interface PreparedFileMutation {\n\tintentId: string;\n\tkind: FileMutationKind;\n\tpath: string;\n\tdisplayPath: string;\n}\n\nexport interface FileMutationLease extends PreparedFileMutation {\n\tidentity?: FilePathIdentity;\n}\n\nexport interface FileContentReference {\n\tcontentRef: string;\n\tbyteLength: number;\n}\n\ninterface MutationIntentRecord extends FileMutationLease {\n\texpiresAt: number;\n}\n\ninterface ContentReferenceRecord {\n\tcontentRef: string;\n\tsourcePath: string;\n\tdigest: string;\n\tbyteLength: number;\n\texpiresAt: number;\n}\n\nexport interface FileMutationIntentControllerOptions {\n\toperations?: FileMutationIntentOperations;\n\tintentLimit?: number;\n\tcontentReferenceLimit?: number;\n\tintentTtlMs?: number;\n\tcontentReferenceTtlMs?: number;\n\tnow?: () => number;\n}\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error && typeof error.code === \"string\"\n\t\t? error.code\n\t\t: undefined;\n}\n\nfunction normalizedPath(path: string): string {\n\tconst absolutePath = resolve(path);\n\treturn process.platform === \"win32\" ? absolutePath.toLocaleLowerCase(\"en-US\") : absolutePath;\n}\n\nfunction positiveBound(value: number | undefined, fallback: number, label: string): number {\n\tconst resolvedValue = value ?? fallback;\n\tif (!Number.isSafeInteger(resolvedValue) || resolvedValue < 1) {\n\t\tthrow new TypeError(`${label} must be a positive safe integer.`);\n\t}\n\treturn resolvedValue;\n}\n\nfunction normalizedIdentity(value: {\n\tdev: number | bigint;\n\tino: number | bigint;\n\tmode: number | bigint;\n\tsize: number | bigint;\n\tmtimeMs: number | bigint;\n\tctimeMs: number | bigint;\n}): FilePathIdentity {\n\treturn {\n\t\tdev: String(value.dev),\n\t\tino: String(value.ino),\n\t\tmode: String(value.mode),\n\t\tsize: String(value.size),\n\t\tmtimeMs: String(value.mtimeMs),\n\t\tctimeMs: String(value.ctimeMs),\n\t};\n}\n\nfunction identitiesMatch(left: FilePathIdentity | undefined, right: FilePathIdentity | undefined): boolean {\n\treturn (\n\t\tleft !== undefined &&\n\t\tright !== undefined &&\n\t\tleft.dev === right.dev &&\n\t\tleft.ino === right.ino &&\n\t\tleft.mode === right.mode &&\n\t\tleft.size === right.size &&\n\t\tleft.mtimeMs === right.mtimeMs &&\n\t\tleft.ctimeMs === right.ctimeMs\n\t);\n}\n\nasync function inspectLocalPath(path: string, followSymlinks: boolean): Promise<FilePathInspection | undefined> {\n\ttry {\n\t\tconst result = followSymlinks ? await stat(path, { bigint: true }) : await lstat(path, { bigint: true });\n\t\treturn {\n\t\t\tkind: result.isFile() ? \"file\" : result.isDirectory() ? \"directory\" : \"other\",\n\t\t\tidentity: normalizedIdentity(result),\n\t\t};\n\t} catch (error) {\n\t\tif (isMissingPathError(error)) return undefined;\n\t\tthrow error;\n\t}\n}\n\nasync function hashLocalFile(path: string, signal?: AbortSignal): Promise<string> {\n\tconst handle = await open(path, \"r\");\n\tconst hash = createHash(\"sha256\");\n\tconst buffer = Buffer.allocUnsafe(HASH_BUFFER_BYTES);\n\ttry {\n\t\tlet position = 0;\n\t\twhile (true) {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n\t\t\tif (bytesRead === 0) break;\n\t\t\thash.update(buffer.subarray(0, bytesRead));\n\t\t\tposition += bytesRead;\n\t\t}\n\t\treturn hash.digest(\"hex\");\n\t} finally {\n\t\tawait handle.close();\n\t}\n}\n\nexport const localFileMutationIntentOperations: FileMutationIntentOperations = {\n\tinspect: inspectLocalPath,\n\taccess,\n\tcopyFileExclusive: (sourcePath, targetPath) => copyFile(sourcePath, targetPath, constants.COPYFILE_EXCL),\n\thashFile: hashLocalFile,\n\tremoveFile: unlink,\n};\n\n/**\n * Session-owned authority for two-phase file mutation intents and bounded exact-content references.\n * It retains only fixed-size metadata; file bytes remain owned by the filesystem.\n */\nexport class FileMutationIntentController {\n\tprivate readonly operations: FileMutationIntentOperations;\n\tprivate readonly intentLimit: number;\n\tprivate readonly contentReferenceLimit: number;\n\tprivate readonly intentTtlMs: number;\n\tprivate readonly contentReferenceTtlMs: number;\n\tprivate readonly now: () => number;\n\tprivate readonly intents = new Map<string, MutationIntentRecord>();\n\tprivate readonly intentByPath = new Map<string, string>();\n\tprivate readonly contentReferences = new Map<string, ContentReferenceRecord>();\n\n\tconstructor(options: FileMutationIntentControllerOptions = {}) {\n\t\tthis.operations = options.operations ?? localFileMutationIntentOperations;\n\t\tthis.intentLimit = positiveBound(options.intentLimit, DEFAULT_INTENT_LIMIT, \"Intent limit\");\n\t\tthis.contentReferenceLimit = positiveBound(\n\t\t\toptions.contentReferenceLimit,\n\t\t\tDEFAULT_CONTENT_REFERENCE_LIMIT,\n\t\t\t\"Content reference limit\",\n\t\t);\n\t\tthis.intentTtlMs = positiveBound(options.intentTtlMs, DEFAULT_INTENT_TTL_MS, \"Intent TTL\");\n\t\tthis.contentReferenceTtlMs = positiveBound(\n\t\t\toptions.contentReferenceTtlMs,\n\t\t\tDEFAULT_CONTENT_REFERENCE_TTL_MS,\n\t\t\t\"Content reference TTL\",\n\t\t);\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tasync prepare(\n\t\tkind: FileMutationKind,\n\t\tpath: string,\n\t\tsignal?: AbortSignal,\n\t\tdisplayPath: string = path,\n\t): Promise<PreparedFileMutation> {\n\t\tthis.pruneExpired();\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tconst absolutePath = resolve(path);\n\t\tlet identity: FilePathIdentity | undefined;\n\t\tif (kind === \"write\") {\n\t\t\tconst existing = await this.operations.inspect(absolutePath, false);\n\t\t\tif (existing) throw new Error(`Write collision: ${displayPath} already exists.`);\n\t\t\tawait this.assertCreatableParent(absolutePath);\n\t\t} else {\n\t\t\tconst existing = await this.operations.inspect(absolutePath, true);\n\t\t\tif (!existing) throw new Error(`Could not edit file: ${displayPath}. Error code: ENOENT.`);\n\t\t\tif (existing.kind !== \"file\")\n\t\t\t\tthrow new Error(`Could not edit file: ${displayPath}. Target is not a regular file.`);\n\t\t\ttry {\n\t\t\t\tawait this.operations.access(absolutePath, constants.R_OK | constants.W_OK);\n\t\t\t} catch (error) {\n\t\t\t\tconst code = errorCode(error);\n\t\t\t\tthrow new Error(`Could not edit file: ${displayPath}. ${code ? `Error code: ${code}` : String(error)}.`);\n\t\t\t}\n\t\t\tidentity = existing.identity;\n\t\t}\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\tconst key = `${kind}\\0${normalizedPath(absolutePath)}`;\n\t\tconst priorIntentId = this.intentByPath.get(key);\n\t\tif (priorIntentId) this.deleteIntent(priorIntentId);\n\t\twhile (this.intents.size >= this.intentLimit) {\n\t\t\tconst oldestIntentId = this.intents.keys().next().value;\n\t\t\tif (typeof oldestIntentId !== \"string\") break;\n\t\t\tthis.deleteIntent(oldestIntentId);\n\t\t}\n\t\tconst intentId = randomUUID();\n\t\tconst record: MutationIntentRecord = {\n\t\t\tintentId,\n\t\t\tkind,\n\t\t\tpath: absolutePath,\n\t\t\tdisplayPath,\n\t\t\t...(identity ? { identity } : {}),\n\t\t\texpiresAt: this.now() + this.intentTtlMs,\n\t\t};\n\t\tthis.intents.set(intentId, record);\n\t\tthis.intentByPath.set(key, intentId);\n\t\treturn { intentId, kind, path: absolutePath, displayPath };\n\t}\n\n\tconsume(intentId: string, kind: FileMutationKind, path: string): FileMutationLease {\n\t\tthis.pruneExpired();\n\t\tconst record = this.intents.get(intentId);\n\t\tif (!record) throw new Error(\"File mutation intent is invalid, expired, or belongs to another session.\");\n\t\tthis.deleteIntent(intentId);\n\t\tif (record.kind !== kind) throw new Error(`File mutation intent is for ${record.kind}, not ${kind}.`);\n\t\tif (normalizedPath(record.path) !== normalizedPath(path)) {\n\t\t\tthrow new Error(\"File mutation intent path does not match the commit path.\");\n\t\t}\n\t\treturn {\n\t\t\tintentId: record.intentId,\n\t\t\tkind: record.kind,\n\t\t\tpath: record.path,\n\t\t\tdisplayPath: record.displayPath,\n\t\t\t...(record.identity ? { identity: record.identity } : {}),\n\t\t};\n\t}\n\n\tasync assertCurrent(lease: FileMutationLease, signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tif (lease.kind === \"write\") {\n\t\t\tif (await this.operations.inspect(lease.path, false)) {\n\t\t\t\tthrow new Error(`Write collision: ${lease.displayPath} already exists; the prepared intent is stale.`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst current = await this.operations.inspect(lease.path, true);\n\t\tif (!current || current.kind !== \"file\") {\n\t\t\tthrow new Error(`Could not edit file: ${lease.displayPath}. The prepared target no longer exists.`);\n\t\t}\n\t\tif (!identitiesMatch(lease.identity, current.identity)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not edit file: ${lease.displayPath}. The file changed after preparation; prepare again.`,\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tawait this.operations.access(lease.path, constants.R_OK | constants.W_OK);\n\t\t} catch (error) {\n\t\t\tconst code = errorCode(error);\n\t\t\tthrow new Error(`Could not edit file: ${lease.displayPath}. ${code ? `Error code: ${code}` : String(error)}.`);\n\t\t}\n\t}\n\n\trememberContent(sourcePath: string, content: string): FileContentReference {\n\t\tconst digest = createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n\t\treturn this.rememberContentDigest(sourcePath, digest, Buffer.byteLength(content, \"utf8\"));\n\t}\n\n\tasync copyReferencedContent(\n\t\tcontentRef: string,\n\t\ttargetPath: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<FileContentReference> {\n\t\tthis.pruneExpired();\n\t\tconst record = this.contentReferences.get(contentRef);\n\t\tif (!record) throw new Error(\"File content reference is invalid, expired, or belongs to another session.\");\n\t\tthis.touchContentReference(record);\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tlet sourceDigest: string;\n\t\ttry {\n\t\t\tsourceDigest = await this.operations.hashFile(record.sourcePath, signal);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`File content reference source is unavailable: ${String(error)}.`);\n\t\t}\n\t\tif (sourceDigest !== record.digest) {\n\t\t\tthis.contentReferences.delete(contentRef);\n\t\t\tthrow new Error(\"File content reference source changed after it was cached.\");\n\t\t}\n\n\t\tawait this.operations.copyFileExclusive(record.sourcePath, targetPath);\n\t\tlet targetDigest: string;\n\t\ttry {\n\t\t\ttargetDigest = await this.operations.hashFile(targetPath, signal);\n\t\t} catch (error) {\n\t\t\tawait this.removeFailedCopy(targetPath, error);\n\t\t\tthrow error;\n\t\t}\n\t\tif (targetDigest !== record.digest) {\n\t\t\tawait this.removeFailedCopy(targetPath, new Error(\"copied bytes did not match the cached digest\"));\n\t\t\tthrow new Error(\"File content copy changed while it was being created; the target was removed.\");\n\t\t}\n\t\treturn this.rememberContentDigest(targetPath, record.digest, record.byteLength);\n\t}\n\n\tprivate async assertCreatableParent(path: string): Promise<void> {\n\t\tlet candidate = dirname(path);\n\t\twhile (true) {\n\t\t\tconst inspection = await this.operations.inspect(candidate, true);\n\t\t\tif (inspection) {\n\t\t\t\tif (inspection.kind !== \"directory\") {\n\t\t\t\t\tthrow new Error(`Write parent path is not a directory: ${candidate}.`);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.operations.access(candidate, constants.W_OK);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst code = errorCode(error);\n\t\t\t\t\tthrow new Error(`Write parent is not writable: ${candidate}${code ? ` (${code})` : \"\"}.`);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst parent = dirname(candidate);\n\t\t\tif (parent === candidate) throw new Error(`No writable parent exists for ${path}.`);\n\t\t\tcandidate = parent;\n\t\t}\n\t}\n\n\tprivate rememberContentDigest(sourcePath: string, digest: string, byteLength: number): FileContentReference {\n\t\tthis.pruneExpired();\n\t\tfor (const existing of this.contentReferences.values()) {\n\t\t\tif (existing.sourcePath === sourcePath && existing.digest === digest) {\n\t\t\t\tthis.touchContentReference(existing);\n\t\t\t\treturn { contentRef: existing.contentRef, byteLength: existing.byteLength };\n\t\t\t}\n\t\t}\n\t\twhile (this.contentReferences.size >= this.contentReferenceLimit) {\n\t\t\tconst oldestContentRef = this.contentReferences.keys().next().value;\n\t\t\tif (typeof oldestContentRef !== \"string\") break;\n\t\t\tthis.contentReferences.delete(oldestContentRef);\n\t\t}\n\t\tconst contentRef = `${CONTENT_REFERENCE_PREFIX}${randomUUID()}`;\n\t\tthis.contentReferences.set(contentRef, {\n\t\t\tcontentRef,\n\t\t\tsourcePath,\n\t\t\tdigest,\n\t\t\tbyteLength,\n\t\t\texpiresAt: this.now() + this.contentReferenceTtlMs,\n\t\t});\n\t\treturn { contentRef, byteLength };\n\t}\n\n\tprivate touchContentReference(record: ContentReferenceRecord): void {\n\t\trecord.expiresAt = this.now() + this.contentReferenceTtlMs;\n\t\tthis.contentReferences.delete(record.contentRef);\n\t\tthis.contentReferences.set(record.contentRef, record);\n\t}\n\n\tprivate async removeFailedCopy(path: string, cause: unknown): Promise<void> {\n\t\ttry {\n\t\t\tawait this.operations.removeFile(path);\n\t\t} catch (cleanupError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Copied content verification failed (${String(cause)}), and the newly created target could not be removed: ${String(cleanupError)}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate deleteIntent(intentId: string): void {\n\t\tconst record = this.intents.get(intentId);\n\t\tif (!record) return;\n\t\tthis.intents.delete(intentId);\n\t\tconst key = `${record.kind}\\0${normalizedPath(record.path)}`;\n\t\tif (this.intentByPath.get(key) === intentId) this.intentByPath.delete(key);\n\t}\n\n\tprivate pruneExpired(): void {\n\t\tconst now = this.now();\n\t\tfor (const [intentId, record] of this.intents) {\n\t\t\tif (record.expiresAt <= now) this.deleteIntent(intentId);\n\t\t}\n\t\tfor (const [contentRef, record] of this.contentReferences) {\n\t\t\tif (record.expiresAt <= now) this.contentReferences.delete(contentRef);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"file-mutation-intent.js","sourceRoot":"","sources":["../../../src/core/tools/file-mutation-intent.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport { constants, rmSync } from \"node:fs\";\nimport { access, copyFile, lstat, mkdtemp, open, readFile, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\n\nconst DEFAULT_CONTENT_REFERENCE_LIMIT = 64;\nconst DEFAULT_CONTENT_REFERENCE_TTL_MS = 60 * 60 * 1000;\nconst DEFAULT_MUTATION_PAYLOAD_LIMIT = 8;\nconst DEFAULT_MUTATION_PAYLOAD_BYTE_LIMIT = 16 * 1024 * 1024;\nconst DEFAULT_MUTATION_PAYLOAD_TTL_MS = 10 * 60 * 1000;\nconst HASH_BUFFER_BYTES = 64 * 1024;\nconst CONTENT_REFERENCE_PREFIX = \"file-content:\";\nconst MUTATION_PAYLOAD_REFERENCE_PREFIX = \"file-mutation:\";\nconst LOCAL_MUTATION_PAYLOAD_DIRECTORY_PREFIX = \"pi-file-mutation-payloads-\";\nconst LOCAL_MUTATION_PAYLOAD_PROCESS_BYTE_LIMIT = 64 * 1024 * 1024;\nconst LOCAL_MUTATION_PAYLOAD_PROCESS_FILE_LIMIT = 64;\n\nlet localMutationPayloadDirectoryPromise: Promise<string> | undefined;\nlet localMutationPayloadDirectory: string | undefined;\nlet localMutationPayloadCleanupRegistered = false;\nlet localMutationPayloadOperationTail: Promise<void> = Promise.resolve();\nlet localMutationPayloadBytes = 0;\nconst localMutationPayloadFiles = new Map<string, { byteLength: number; expiresAt: number }>();\n\nclass MutationPayloadCapacityError extends Error {\n\tconstructor() {\n\t\tsuper(\"Process mutation payload cache is full.\");\n\t\tthis.name = \"MutationPayloadCapacityError\";\n\t}\n}\n\nexport type FileMutationKind = \"write\" | \"edit\";\n\nexport interface FilePathIdentity {\n\tdev: string;\n\tino: string;\n\tmode: string;\n\tsize: string;\n\tmtimeMs: string;\n\tctimeMs: string;\n}\n\nexport interface FilePathInspection {\n\tkind: \"file\" | \"directory\" | \"other\";\n\tidentity: FilePathIdentity;\n}\n\nexport interface FileMutationIntentOperations {\n\tinspect(path: string, followSymlinks: boolean): Promise<FilePathInspection | undefined>;\n\taccess(path: string, mode: number): Promise<void>;\n\tcopyFileExclusive(sourcePath: string, targetPath: string): Promise<void>;\n\thashFile(path: string, signal?: AbortSignal): Promise<string>;\n\treadPayload(path: string): Promise<string>;\n\tremoveFile(path: string): Promise<void>;\n\tstagePayload(content: string, ttlMs: number): Promise<string>;\n}\n\nexport interface FileMutationLease {\n\tkind: FileMutationKind;\n\tpath: string;\n\tdisplayPath: string;\n\tidentity?: FilePathIdentity;\n}\n\nexport interface FileContentReference {\n\tcontentRef: string;\n\tbyteLength: number;\n}\n\nexport interface FileMutationPayloadReference {\n\tpayloadRef: string;\n\tbyteLength: number;\n}\n\nexport type FileMutationPreflightFailureReason = \"write_collision\" | \"edit_missing\" | \"edit_not_file\";\n\nexport class FileMutationPreflightError extends Error {\n\treadonly reason: FileMutationPreflightFailureReason;\n\n\tconstructor(reason: FileMutationPreflightFailureReason, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"FileMutationPreflightError\";\n\t\tthis.reason = reason;\n\t}\n}\n\ninterface ContentReferenceRecord {\n\tcontentRef: string;\n\tsourcePath: string;\n\tdigest: string;\n\tbyteLength: number;\n\texpiresAt: number;\n}\n\ninterface MutationPayloadRecord {\n\tpayloadRef: string;\n\tkind: FileMutationKind;\n\tsourcePath: string;\n\tdigest: string;\n\tbyteLength: number;\n\texpiresAt: number;\n}\n\nexport interface FileMutationIntentControllerOptions {\n\toperations?: FileMutationIntentOperations;\n\tcontentReferenceLimit?: number;\n\tcontentReferenceTtlMs?: number;\n\tmutationPayloadLimit?: number;\n\tmutationPayloadByteLimit?: number;\n\tmutationPayloadTtlMs?: number;\n\tnow?: () => number;\n}\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error && typeof error.code === \"string\"\n\t\t? error.code\n\t\t: undefined;\n}\n\nfunction positiveBound(value: number | undefined, fallback: number, label: string): number {\n\tconst resolvedValue = value ?? fallback;\n\tif (!Number.isSafeInteger(resolvedValue) || resolvedValue < 1) {\n\t\tthrow new TypeError(`${label} must be a positive safe integer.`);\n\t}\n\treturn resolvedValue;\n}\n\nfunction normalizedIdentity(value: {\n\tdev: number | bigint;\n\tino: number | bigint;\n\tmode: number | bigint;\n\tsize: number | bigint;\n\tmtimeMs: number | bigint;\n\tctimeMs: number | bigint;\n}): FilePathIdentity {\n\treturn {\n\t\tdev: String(value.dev),\n\t\tino: String(value.ino),\n\t\tmode: String(value.mode),\n\t\tsize: String(value.size),\n\t\tmtimeMs: String(value.mtimeMs),\n\t\tctimeMs: String(value.ctimeMs),\n\t};\n}\n\nfunction identitiesMatch(left: FilePathIdentity | undefined, right: FilePathIdentity | undefined): boolean {\n\treturn (\n\t\tleft !== undefined &&\n\t\tright !== undefined &&\n\t\tleft.dev === right.dev &&\n\t\tleft.ino === right.ino &&\n\t\tleft.mode === right.mode &&\n\t\tleft.size === right.size &&\n\t\tleft.mtimeMs === right.mtimeMs &&\n\t\tleft.ctimeMs === right.ctimeMs\n\t);\n}\n\nasync function inspectLocalPath(path: string, followSymlinks: boolean): Promise<FilePathInspection | undefined> {\n\ttry {\n\t\tconst result = followSymlinks ? await stat(path, { bigint: true }) : await lstat(path, { bigint: true });\n\t\treturn {\n\t\t\tkind: result.isFile() ? \"file\" : result.isDirectory() ? \"directory\" : \"other\",\n\t\t\tidentity: normalizedIdentity(result),\n\t\t};\n\t} catch (error) {\n\t\tif (isMissingPathError(error)) return undefined;\n\t\tthrow error;\n\t}\n}\n\nasync function hashLocalFile(path: string, signal?: AbortSignal): Promise<string> {\n\tconst handle = await open(path, \"r\");\n\tconst hash = createHash(\"sha256\");\n\tconst buffer = Buffer.allocUnsafe(HASH_BUFFER_BYTES);\n\ttry {\n\t\tlet position = 0;\n\t\twhile (true) {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n\t\t\tif (bytesRead === 0) break;\n\t\t\thash.update(buffer.subarray(0, bytesRead));\n\t\t\tposition += bytesRead;\n\t\t}\n\t\treturn hash.digest(\"hex\");\n\t} finally {\n\t\tawait handle.close();\n\t}\n}\n\nasync function getLocalMutationPayloadDirectory(): Promise<string> {\n\tlocalMutationPayloadDirectoryPromise ??= mkdtemp(join(tmpdir(), LOCAL_MUTATION_PAYLOAD_DIRECTORY_PREFIX)).then(\n\t\t(path) => {\n\t\t\tlocalMutationPayloadDirectory = path;\n\t\t\tif (!localMutationPayloadCleanupRegistered) {\n\t\t\t\tlocalMutationPayloadCleanupRegistered = true;\n\t\t\t\tprocess.once(\"exit\", () => {\n\t\t\t\t\tif (localMutationPayloadDirectory) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\trmSync(localMutationPayloadDirectory, { recursive: true, force: true });\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// Process shutdown cannot safely retry filesystem cleanup.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn path;\n\t\t},\n\t);\n\treturn localMutationPayloadDirectoryPromise;\n}\n\nasync function stageLocalMutationPayload(content: string, ttlMs: number): Promise<string> {\n\tconst byteLength = Buffer.byteLength(content, \"utf8\");\n\tlet stagedPath = \"\";\n\tconst operation = localMutationPayloadOperationTail.then(async () => {\n\t\tfor (const [path, record] of localMutationPayloadFiles) {\n\t\t\tif (record.expiresAt > Date.now()) continue;\n\t\t\tlocalMutationPayloadFiles.delete(path);\n\t\t\tlocalMutationPayloadBytes -= record.byteLength;\n\t\t\ttry {\n\t\t\t\tawait unlink(path);\n\t\t\t} catch (error) {\n\t\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\t}\n\t\t}\n\t\tif (\n\t\t\tlocalMutationPayloadFiles.size >= LOCAL_MUTATION_PAYLOAD_PROCESS_FILE_LIMIT ||\n\t\t\tlocalMutationPayloadBytes + byteLength > LOCAL_MUTATION_PAYLOAD_PROCESS_BYTE_LIMIT\n\t\t) {\n\t\t\tthrow new MutationPayloadCapacityError();\n\t\t}\n\t\tstagedPath = join(await getLocalMutationPayloadDirectory(), randomUUID());\n\t\tawait writeFile(stagedPath, content, { encoding: \"utf8\", flag: \"wx\", mode: 0o600 });\n\t\tlocalMutationPayloadFiles.set(stagedPath, { byteLength, expiresAt: Date.now() + ttlMs });\n\t\tlocalMutationPayloadBytes += byteLength;\n\t});\n\tlocalMutationPayloadOperationTail = operation.catch(() => {});\n\tawait operation;\n\treturn stagedPath;\n}\n\nasync function removeLocalMutationPayloadOrFile(path: string): Promise<void> {\n\tconst operation = localMutationPayloadOperationTail.then(async () => {\n\t\tconst record = localMutationPayloadFiles.get(path);\n\t\tif (record !== undefined) {\n\t\t\tlocalMutationPayloadFiles.delete(path);\n\t\t\tlocalMutationPayloadBytes -= record.byteLength;\n\t\t}\n\t\tawait unlink(path);\n\t});\n\tlocalMutationPayloadOperationTail = operation.catch(() => {});\n\tawait operation;\n}\n\nexport const localFileMutationIntentOperations: FileMutationIntentOperations = {\n\tinspect: inspectLocalPath,\n\taccess,\n\tcopyFileExclusive: (sourcePath, targetPath) => copyFile(sourcePath, targetPath, constants.COPYFILE_EXCL),\n\thashFile: hashLocalFile,\n\treadPayload: (path) => readFile(path, \"utf8\"),\n\tremoveFile: removeLocalMutationPayloadOrFile,\n\tstagePayload: stageLocalMutationPayload,\n};\n\n/**\n * Session-owned harness authority for file-mutation preflight, stale-target checks,\n * and bounded exact-content references. It retains only fixed-size metadata; file\n * bytes remain owned by the filesystem.\n */\nexport class FileMutationIntentController {\n\tprivate readonly operations: FileMutationIntentOperations;\n\tprivate readonly contentReferenceLimit: number;\n\tprivate readonly contentReferenceTtlMs: number;\n\tprivate readonly mutationPayloadLimit: number;\n\tprivate readonly mutationPayloadByteLimit: number;\n\tprivate readonly mutationPayloadTtlMs: number;\n\tprivate readonly now: () => number;\n\tprivate readonly contentReferences = new Map<string, ContentReferenceRecord>();\n\tprivate readonly mutationPayloads = new Map<string, MutationPayloadRecord>();\n\tprivate mutationPayloadBytes = 0;\n\tprivate lastMutationPayloadExpiresAt = 0;\n\tprivate mutationPayloadOperationTail: Promise<void> = Promise.resolve();\n\n\tconstructor(options: FileMutationIntentControllerOptions = {}) {\n\t\tthis.operations = options.operations ?? localFileMutationIntentOperations;\n\t\tthis.contentReferenceLimit = positiveBound(\n\t\t\toptions.contentReferenceLimit,\n\t\t\tDEFAULT_CONTENT_REFERENCE_LIMIT,\n\t\t\t\"Content reference limit\",\n\t\t);\n\t\tthis.contentReferenceTtlMs = positiveBound(\n\t\t\toptions.contentReferenceTtlMs,\n\t\t\tDEFAULT_CONTENT_REFERENCE_TTL_MS,\n\t\t\t\"Content reference TTL\",\n\t\t);\n\t\tthis.mutationPayloadLimit = positiveBound(\n\t\t\toptions.mutationPayloadLimit,\n\t\t\tDEFAULT_MUTATION_PAYLOAD_LIMIT,\n\t\t\t\"Mutation payload limit\",\n\t\t);\n\t\tthis.mutationPayloadByteLimit = positiveBound(\n\t\t\toptions.mutationPayloadByteLimit,\n\t\t\tDEFAULT_MUTATION_PAYLOAD_BYTE_LIMIT,\n\t\t\t\"Mutation payload byte limit\",\n\t\t);\n\t\tthis.mutationPayloadTtlMs = positiveBound(\n\t\t\toptions.mutationPayloadTtlMs,\n\t\t\tDEFAULT_MUTATION_PAYLOAD_TTL_MS,\n\t\t\t\"Mutation payload TTL\",\n\t\t);\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tasync prepare(\n\t\tkind: FileMutationKind,\n\t\tpath: string,\n\t\tsignal?: AbortSignal,\n\t\tdisplayPath: string = path,\n\t): Promise<FileMutationLease> {\n\t\tthis.pruneExpired();\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tconst absolutePath = resolve(path);\n\t\tlet identity: FilePathIdentity | undefined;\n\t\tif (kind === \"write\") {\n\t\t\tconst existing = await this.operations.inspect(absolutePath, false);\n\t\t\tif (existing) {\n\t\t\t\tthrow new FileMutationPreflightError(\"write_collision\", `Write collision: ${displayPath} already exists.`);\n\t\t\t}\n\t\t\tawait this.assertCreatableParent(absolutePath);\n\t\t} else {\n\t\t\tconst existing = await this.operations.inspect(absolutePath, true);\n\t\t\tif (!existing) {\n\t\t\t\tthrow new FileMutationPreflightError(\n\t\t\t\t\t\"edit_missing\",\n\t\t\t\t\t`Could not edit file: ${displayPath}. Error code: ENOENT.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (existing.kind !== \"file\") {\n\t\t\t\tthrow new FileMutationPreflightError(\n\t\t\t\t\t\"edit_not_file\",\n\t\t\t\t\t`Could not edit file: ${displayPath}. Target is not a regular file.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait this.operations.access(absolutePath, constants.R_OK | constants.W_OK);\n\t\t\t} catch (error) {\n\t\t\t\tconst code = errorCode(error);\n\t\t\t\tthrow new Error(`Could not edit file: ${displayPath}. ${code ? `Error code: ${code}` : String(error)}.`);\n\t\t\t}\n\t\t\tidentity = existing.identity;\n\t\t}\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\treturn {\n\t\t\tkind,\n\t\t\tpath: absolutePath,\n\t\t\tdisplayPath,\n\t\t\t...(identity ? { identity } : {}),\n\t\t};\n\t}\n\n\tasync assertCurrent(lease: FileMutationLease, signal?: AbortSignal): Promise<void> {\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tif (lease.kind === \"write\") {\n\t\t\tif (await this.operations.inspect(lease.path, false)) {\n\t\t\t\tthrow new FileMutationPreflightError(\n\t\t\t\t\t\"write_collision\",\n\t\t\t\t\t`Write collision: ${lease.displayPath} became occupied after preflight; no content was overwritten.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst current = await this.operations.inspect(lease.path, true);\n\t\tif (!current || current.kind !== \"file\") {\n\t\t\tthrow new Error(`Could not edit file: ${lease.displayPath}. The target no longer exists after preflight.`);\n\t\t}\n\t\tif (!identitiesMatch(lease.identity, current.identity)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not edit file: ${lease.displayPath}. The file changed during edit execution; read it again before retrying.`,\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tawait this.operations.access(lease.path, constants.R_OK | constants.W_OK);\n\t\t} catch (error) {\n\t\t\tconst code = errorCode(error);\n\t\t\tthrow new Error(`Could not edit file: ${lease.displayPath}. ${code ? `Error code: ${code}` : String(error)}.`);\n\t\t}\n\t}\n\n\trememberContent(sourcePath: string, content: string): FileContentReference {\n\t\tconst digest = createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n\t\treturn this.rememberContentDigest(sourcePath, digest, Buffer.byteLength(content, \"utf8\"));\n\t}\n\n\tasync retainMutationPayload(\n\t\tkind: FileMutationKind,\n\t\tpayload: string,\n\t): Promise<FileMutationPayloadReference | undefined> {\n\t\treturn this.runMutationPayloadOperation(async () => {\n\t\t\tawait this.pruneExpiredMutationPayloads();\n\t\t\tconst byteLength = Buffer.byteLength(payload, \"utf8\");\n\t\t\tif (byteLength > this.mutationPayloadByteLimit) return undefined;\n\t\t\twhile (\n\t\t\t\tthis.mutationPayloads.size >= this.mutationPayloadLimit ||\n\t\t\t\tthis.mutationPayloadBytes + byteLength > this.mutationPayloadByteLimit\n\t\t\t) {\n\t\t\t\tconst oldestPayloadRef = this.mutationPayloads.keys().next().value;\n\t\t\t\tif (typeof oldestPayloadRef !== \"string\") return undefined;\n\t\t\t\tawait this.deleteMutationPayload(oldestPayloadRef, true);\n\t\t\t}\n\n\t\t\t// Keep insertion and expiry order identical, even if the wall clock moves backward. Expiry\n\t\t\t// pruning can then stop at the first live record instead of rescanning every retained payload.\n\t\t\tconst expiresAt = Math.max(this.lastMutationPayloadExpiresAt, this.now() + this.mutationPayloadTtlMs);\n\t\t\tthis.lastMutationPayloadExpiresAt = expiresAt;\n\t\t\tlet sourcePath: string;\n\t\t\ttry {\n\t\t\t\tsourcePath = await this.operations.stagePayload(payload, this.mutationPayloadTtlMs);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof MutationPayloadCapacityError) return undefined;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst payloadRef = `${MUTATION_PAYLOAD_REFERENCE_PREFIX}${randomUUID()}`;\n\t\t\tthis.mutationPayloads.set(payloadRef, {\n\t\t\t\tpayloadRef,\n\t\t\t\tkind,\n\t\t\t\tsourcePath,\n\t\t\t\tdigest: createHash(\"sha256\").update(payload, \"utf8\").digest(\"hex\"),\n\t\t\t\tbyteLength,\n\t\t\t\texpiresAt,\n\t\t\t});\n\t\t\tthis.mutationPayloadBytes += byteLength;\n\t\t\treturn { payloadRef, byteLength };\n\t\t});\n\t}\n\n\tasync readMutationPayload(payloadRef: string, kind: FileMutationKind, signal?: AbortSignal): Promise<string> {\n\t\treturn this.runMutationPayloadOperation(async () => {\n\t\t\tconst record = await this.requireMutationPayload(payloadRef, kind, signal);\n\t\t\tconst payload = await this.operations.readPayload(record.sourcePath);\n\t\t\tif (createHash(\"sha256\").update(payload, \"utf8\").digest(\"hex\") !== record.digest) {\n\t\t\t\tawait this.deleteMutationPayload(payloadRef);\n\t\t\t\tthrow new Error(\"Retained file mutation payload changed after it was cached.\");\n\t\t\t}\n\t\t\treturn payload;\n\t\t});\n\t}\n\n\tasync assertMutationPayload(payloadRef: string, kind: FileMutationKind, signal?: AbortSignal): Promise<void> {\n\t\tawait this.runMutationPayloadOperation(async () => {\n\t\t\tawait this.requireVerifiedMutationPayload(payloadRef, kind, signal);\n\t\t});\n\t}\n\n\tasync assertContentReference(contentRef: string, signal?: AbortSignal): Promise<void> {\n\t\tawait this.requireVerifiedContentReference(contentRef, signal);\n\t}\n\n\tasync copyMutationPayload(\n\t\tpayloadRef: string,\n\t\ttargetPath: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<FileContentReference> {\n\t\treturn this.runMutationPayloadOperation(async () => {\n\t\t\tconst record = await this.requireVerifiedMutationPayload(payloadRef, \"write\", signal);\n\t\t\tawait this.copyVerifiedSource(record, targetPath, signal);\n\t\t\tawait this.deleteMutationPayload(payloadRef);\n\t\t\treturn this.rememberContentDigest(targetPath, record.digest, record.byteLength);\n\t\t});\n\t}\n\n\tasync discardMutationPayload(payloadRef: string): Promise<void> {\n\t\tawait this.runMutationPayloadOperation(() => this.deleteMutationPayload(payloadRef));\n\t}\n\n\t/** Release every payload owned by this controller without touching another controller's files. */\n\tasync dispose(): Promise<void> {\n\t\tawait this.runMutationPayloadOperation(async () => {\n\t\t\tlet firstError: unknown;\n\t\t\tfor (const payloadRef of [...this.mutationPayloads.keys()]) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.deleteMutationPayload(payloadRef, true);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tfirstError ??= error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (firstError) throw firstError;\n\t\t});\n\t}\n\n\tasync copyReferencedContent(\n\t\tcontentRef: string,\n\t\ttargetPath: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<FileContentReference> {\n\t\tconst record = await this.requireVerifiedContentReference(contentRef, signal);\n\t\tawait this.copyVerifiedSource(record, targetPath, signal);\n\t\treturn this.rememberContentDigest(targetPath, record.digest, record.byteLength);\n\t}\n\n\tprivate async requireVerifiedContentReference(\n\t\tcontentRef: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<ContentReferenceRecord> {\n\t\tthis.pruneExpired();\n\t\tconst record = this.contentReferences.get(contentRef);\n\t\tif (!record) throw new Error(\"File content reference is invalid, expired, or belongs to another session.\");\n\t\tthis.touchContentReference(record);\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\ttry {\n\t\t\tawait this.verifySource(record, signal, \"File content reference source\");\n\t\t} catch (error) {\n\t\t\tthis.contentReferences.delete(contentRef);\n\t\t\tthrow error;\n\t\t}\n\t\treturn record;\n\t}\n\n\tprivate async requireMutationPayload(\n\t\tpayloadRef: string,\n\t\tkind: FileMutationKind,\n\t\tsignal?: AbortSignal,\n\t): Promise<MutationPayloadRecord> {\n\t\tawait this.pruneExpiredMutationPayloads();\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tconst record = this.mutationPayloads.get(payloadRef);\n\t\tif (!record) {\n\t\t\tthrow new Error(\"File mutation payload reference is invalid, expired, or belongs to another session.\");\n\t\t}\n\t\tif (record.kind !== kind) {\n\t\t\tthrow new Error(`File mutation payload reference is for ${record.kind}, not ${kind}.`);\n\t\t}\n\t\treturn record;\n\t}\n\n\tprivate async requireVerifiedMutationPayload(\n\t\tpayloadRef: string,\n\t\tkind: FileMutationKind,\n\t\tsignal?: AbortSignal,\n\t): Promise<MutationPayloadRecord> {\n\t\tconst record = await this.requireMutationPayload(payloadRef, kind, signal);\n\t\ttry {\n\t\t\tawait this.verifySource(record, signal, \"Retained file mutation payload\");\n\t\t} catch (error) {\n\t\t\tawait this.deleteMutationPayload(payloadRef);\n\t\t\tthrow error;\n\t\t}\n\t\treturn record;\n\t}\n\n\tprivate async verifySource(\n\t\trecord: Pick<MutationPayloadRecord, \"sourcePath\" | \"digest\">,\n\t\tsignal: AbortSignal | undefined,\n\t\tlabel: string,\n\t): Promise<void> {\n\t\tlet sourceDigest: string;\n\t\ttry {\n\t\t\tsourceDigest = await this.operations.hashFile(record.sourcePath, signal);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`${label} is unavailable: ${String(error)}.`);\n\t\t}\n\t\tif (sourceDigest !== record.digest) throw new Error(`${label} changed after it was cached.`);\n\t}\n\n\tprivate async copyVerifiedSource(\n\t\trecord: Pick<MutationPayloadRecord, \"sourcePath\" | \"digest\">,\n\t\ttargetPath: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait this.operations.copyFileExclusive(record.sourcePath, targetPath);\n\t\tlet targetDigest: string;\n\t\ttry {\n\t\t\ttargetDigest = await this.operations.hashFile(targetPath, signal);\n\t\t} catch (error) {\n\t\t\tawait this.removeFailedCopy(targetPath, error);\n\t\t\tthrow error;\n\t\t}\n\t\tif (targetDigest !== record.digest) {\n\t\t\tawait this.removeFailedCopy(targetPath, new Error(\"copied bytes did not match the cached digest\"));\n\t\t\tthrow new Error(\"File content copy changed while it was being created; the target was removed.\");\n\t\t}\n\t}\n\n\tprivate async assertCreatableParent(path: string): Promise<void> {\n\t\tlet candidate = dirname(path);\n\t\twhile (true) {\n\t\t\tconst inspection = await this.operations.inspect(candidate, true);\n\t\t\tif (inspection) {\n\t\t\t\tif (inspection.kind !== \"directory\") {\n\t\t\t\t\tthrow new Error(`Write parent path is not a directory: ${candidate}.`);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.operations.access(candidate, constants.W_OK);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst code = errorCode(error);\n\t\t\t\t\tthrow new Error(`Write parent is not writable: ${candidate}${code ? ` (${code})` : \"\"}.`);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst parent = dirname(candidate);\n\t\t\tif (parent === candidate) throw new Error(`No writable parent exists for ${path}.`);\n\t\t\tcandidate = parent;\n\t\t}\n\t}\n\n\tprivate rememberContentDigest(sourcePath: string, digest: string, byteLength: number): FileContentReference {\n\t\tthis.pruneExpired();\n\t\tfor (const existing of this.contentReferences.values()) {\n\t\t\tif (existing.sourcePath === sourcePath && existing.digest === digest) {\n\t\t\t\tthis.touchContentReference(existing);\n\t\t\t\treturn { contentRef: existing.contentRef, byteLength: existing.byteLength };\n\t\t\t}\n\t\t}\n\t\twhile (this.contentReferences.size >= this.contentReferenceLimit) {\n\t\t\tconst oldestContentRef = this.contentReferences.keys().next().value;\n\t\t\tif (typeof oldestContentRef !== \"string\") break;\n\t\t\tthis.contentReferences.delete(oldestContentRef);\n\t\t}\n\t\tconst contentRef = `${CONTENT_REFERENCE_PREFIX}${randomUUID()}`;\n\t\tthis.contentReferences.set(contentRef, {\n\t\t\tcontentRef,\n\t\t\tsourcePath,\n\t\t\tdigest,\n\t\t\tbyteLength,\n\t\t\texpiresAt: this.now() + this.contentReferenceTtlMs,\n\t\t});\n\t\treturn { contentRef, byteLength };\n\t}\n\n\tprivate touchContentReference(record: ContentReferenceRecord): void {\n\t\trecord.expiresAt = this.now() + this.contentReferenceTtlMs;\n\t\tthis.contentReferences.delete(record.contentRef);\n\t\tthis.contentReferences.set(record.contentRef, record);\n\t}\n\n\tprivate async removeFailedCopy(path: string, cause: unknown): Promise<void> {\n\t\ttry {\n\t\t\tawait this.operations.removeFile(path);\n\t\t} catch (cleanupError) {\n\t\t\tthrow new Error(\n\t\t\t\t`Copied content verification failed (${String(cause)}), and the newly created target could not be removed: ${String(cleanupError)}.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async deleteMutationPayload(payloadRef: string, strict = false): Promise<void> {\n\t\tconst record = this.mutationPayloads.get(payloadRef);\n\t\tif (!record) return;\n\t\tthis.mutationPayloads.delete(payloadRef);\n\t\tthis.mutationPayloadBytes -= record.byteLength;\n\t\ttry {\n\t\t\tawait this.operations.removeFile(record.sourcePath);\n\t\t} catch (error) {\n\t\t\tif (strict && !isMissingPathError(error)) throw error;\n\t\t}\n\t}\n\n\tprivate runMutationPayloadOperation<T>(operation: () => Promise<T>): Promise<T> {\n\t\tconst run = this.mutationPayloadOperationTail.then(operation, operation);\n\t\tthis.mutationPayloadOperationTail = run.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn run;\n\t}\n\n\tprivate async pruneExpiredMutationPayloads(): Promise<void> {\n\t\tconst now = this.now();\n\t\tfor (const [payloadRef, record] of this.mutationPayloads) {\n\t\t\tif (record.expiresAt > now) break;\n\t\t\tawait this.deleteMutationPayload(payloadRef, true);\n\t\t}\n\t}\n\n\tprivate pruneExpired(): void {\n\t\tconst now = this.now();\n\t\tfor (const [contentRef, record] of this.contentReferences) {\n\t\t\tif (record.expiresAt <= now) this.contentReferences.delete(contentRef);\n\t\t}\n\t}\n}\n"]}
@@ -4,7 +4,7 @@ export { type BashOperations, type BashSpawnContext, type BashSpawnHook, type Ba
4
4
  export { CONTEXT_SCOUT_GUIDANCE, type ContextScoutToolDependencies, type ContextScoutToolDetails, type ContextScoutToolInput, createContextScoutToolDefinition, formatScoutResult, } from "./context-scout.ts";
5
5
  export { createEditTool, createEditToolDefinition, type EditOperations, type EditToolDetails, type EditToolInput, type EditToolOptions, } from "./edit.ts";
6
6
  export { createExtensionifyTool, createExtensionifyToolDefinition, type ExtensionifyInput, type ExtensionifyReport, type ExtensionifyToolDetails, type ExtensionifyToolOptions, } from "./extensionify.ts";
7
- export { type FileContentReference, FileMutationIntentController, type FileMutationIntentControllerOptions, type FileMutationIntentOperations, type FileMutationKind, type FileMutationLease, type FilePathIdentity, type FilePathInspection, localFileMutationIntentOperations, type PreparedFileMutation, } from "./file-mutation-intent.ts";
7
+ export { type FileContentReference, FileMutationIntentController, type FileMutationIntentControllerOptions, type FileMutationIntentOperations, type FileMutationKind, type FileMutationLease, type FileMutationPayloadReference, FileMutationPreflightError, type FileMutationPreflightFailureReason, type FilePathIdentity, type FilePathInspection, localFileMutationIntentOperations, } from "./file-mutation-intent.ts";
8
8
  export { withFileMutationQueue } from "./file-mutation-queue.ts";
9
9
  export { createFindTool, createFindToolDefinition, type FindOperations, type FindToolDetails, type FindToolInput, type FindToolOptions, } from "./find.ts";
10
10
  export { createGrepTool, createGrepToolDefinition, type GrepOperations, type GrepToolDetails, type GrepToolInput, type GrepToolOptions, } from "./grep.ts";