@earendil-works/pi-durable 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.4] - 2026-10-05
4
+
5
+ ### Breaking Changes
6
+
7
+ - `NodeExecutionEnv.watch()` fails with `permission_denied` when a target, or a watched directory itself, cannot be read for lack of permission; before, it was watched as if missing. Later rescans that hit this report `{ error }` with `permission_denied` instead of `invalid`.
8
+ - The env conformance suite checks three more `watch()` behaviors: changes to the file a watched symbolic link points to, recursive coverage below a target that a non-recursive target overlaps, and a directory replaced at the same path. Custom environments that passed the 1.0.3 suite may need changes.
9
+
10
+ ### Added
11
+
12
+ - `createPowerShellTool()` in `@earendil-works/pi-durable/tools`: a `powershell` tool that runs `pwsh`, else Windows PowerShell, directly through argv `exec` (no other shell parses the command), with UTF-8 output and the `bash` tool's output window, spill and errors.
13
+
14
+ ### Fixed
15
+
16
+ - The `read` tool reads a file that grows while it is read (an active log) instead of failing with "changed while it was read"; it reads again only when the file shrank or was rewritten in place.
17
+ - `NodeExecutionEnv.watch()`: a directory replaced at the same path gets a new native watcher; a target that is a symbolic link to a file reports changes to that file; a non-recursive target no longer stops an overlapping recursive target from covering subdirectories; closing during a rescan no longer leaks watchers.
18
+ - Bounded tool output keeps a U+FEFF at the start of the retained tail, and drops a byte-order mark only at the very start of byte output.
19
+ - `settings.progress` fields given as `undefined` keep their defaults.
20
+
3
21
  ## [1.0.3] - 2026-10-05
4
22
 
5
23
  ### Breaking Changes
@@ -1 +1 @@
1
- {"version":3,"file":"node-watch.d.ts","sourceRoot":"","sources":["../../src/env/node-watch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAa,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7F,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAChC;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAwGD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,WAAW;;IAelD,OAAO,eAWN;IAED,6FAA6F;IAC7F,OAAa,IAAI,CAChB,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,EACvC,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,eAAe,CAAC,CAwB1B;IAED,IAAI,IAAI,IAAI,QAAQ,GAAG,SAAS,CAE/B;IAEK,KAAK,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5C;CA+PD"}
1
+ {"version":3,"file":"node-watch.d.ts","sourceRoot":"","sources":["../../src/env/node-watch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAa,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7F,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAChC;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAkHD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,WAAW;;IAelD,OAAO,eAWN;IAED,6FAA6F;IAC7F,OAAa,IAAI,CAChB,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,EACvC,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,eAAe,CAAC,CAwB1B;IAED,IAAI,IAAI,IAAI,QAAQ,GAAG,SAAS,CAE/B;IAEK,KAAK,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5C;CA6SD"}
@@ -36,6 +36,9 @@ class BudgetExceeded extends Error {
36
36
  function isNodeError(error) {
37
37
  return error instanceof Error && "code" in error;
38
38
  }
39
+ function isDenied(error) {
40
+ return isNodeError(error) && (error.code === "EACCES" || error.code === "EPERM");
41
+ }
39
42
  function entryOf(stats, hash) {
40
43
  const kind = stats.isFile()
41
44
  ? "file"
@@ -146,7 +149,7 @@ export class NodeFileWatcher {
146
149
  this.#timer = undefined;
147
150
  clearTimeout(this.#settleTimer);
148
151
  this.#settleTimer = undefined;
149
- for (const watcher of this.#watchers.values())
152
+ for (const { watcher } of this.#watchers.values())
150
153
  watcher.close();
151
154
  this.#watchers.clear();
152
155
  }
@@ -204,9 +207,10 @@ export class NodeFileWatcher {
204
207
  } while (this.#dirty && !this.#closed);
205
208
  }
206
209
  catch (error) {
210
+ const message = error instanceof Error ? error.message : String(error);
207
211
  const fileError = error instanceof FileError
208
212
  ? error
209
- : new FileError("invalid", error instanceof Error ? error.message : String(error));
213
+ : new FileError(isDenied(error) ? "permission_denied" : "invalid", message);
210
214
  this.#deliver({ error: fileError });
211
215
  this.#stop();
212
216
  }
@@ -220,12 +224,16 @@ export class NodeFileWatcher {
220
224
  async #sync(report) {
221
225
  const changed = new Set();
222
226
  for (let round = 0; round < 10 && !this.#closed; round++) {
223
- const next = await this.#scan();
227
+ const scan = await this.#scan();
228
+ // Closed during the scan: installing watchers now would leak them.
229
+ if (this.#closed)
230
+ break;
231
+ const next = scan.snapshot;
224
232
  if (report)
225
233
  for (const path of this.#diff(this.#snapshot, next))
226
234
  changed.add(path);
227
235
  this.#snapshot = next;
228
- if (this.#mode === "polling" || !this.#reconcileWatchers(next))
236
+ if (this.#mode === "polling" || !this.#reconcileWatchers(scan))
229
237
  break;
230
238
  if (process.platform === "darwin")
231
239
  this.#scheduleSettle();
@@ -254,9 +262,17 @@ export class NodeFileWatcher {
254
262
  }
255
263
  async #scan() {
256
264
  const snapshot = new Map();
257
- let directories = 0;
258
- const countDirectory = () => {
259
- if (++directories > this.#maxDirectories) {
265
+ const linkedFiles = new Set();
266
+ // Directories are counted once, but traversed once per target: overlapping targets differ in recursion and
267
+ // exclusions, and an entry recorded for one target must still be descended into for another.
268
+ const counted = new Set();
269
+ const traversed = new Set();
270
+ /** Kinds of listed entries, not following links: a target that links to a directory is recorded by `stat`, but
271
+ * symbolic links below a recursive target are not followed. */
272
+ const listed = new Map();
273
+ const countDirectory = (path) => {
274
+ counted.add(path);
275
+ if (counted.size > this.#maxDirectories) {
260
276
  throw new BudgetExceeded(`Watched paths exceed ${this.#maxDirectories} directories`);
261
277
  }
262
278
  };
@@ -270,12 +286,19 @@ export class NodeFileWatcher {
270
286
  }
271
287
  snapshot.set(path, entryOf(stats, hash));
272
288
  };
273
- const scanDirectory = async (target, directory) => {
289
+ const scanDirectory = async (index, target, directory) => {
290
+ const key = `${index}\0${directory}`;
291
+ if (traversed.has(key))
292
+ return;
293
+ traversed.add(key);
274
294
  let names;
275
295
  try {
276
296
  names = await readdir(directory);
277
297
  }
278
298
  catch (error) {
299
+ // The watched directory itself must be readable; below it, unreadable directories are skipped.
300
+ if (directory === target.path && isDenied(error))
301
+ throw error;
279
302
  if (isNodeError(error) && ["ENOENT", "EACCES", "EPERM", "ENOTDIR"].includes(error.code ?? ""))
280
303
  return;
281
304
  throw error;
@@ -284,19 +307,24 @@ export class NodeFileWatcher {
284
307
  if (excluded(target, name))
285
308
  continue;
286
309
  const path = `${directory.endsWith(sep) ? directory : directory + sep}${name}`;
287
- if (snapshot.has(path))
288
- continue;
289
- const stats = await lstat(path).catch(() => undefined);
290
- if (stats === undefined)
291
- continue;
292
- await record(path, stats);
293
- if (target.recursive && stats.isDirectory()) {
294
- countDirectory();
295
- await scanDirectory(target, path);
310
+ let kind = listed.get(path);
311
+ if (kind === undefined) {
312
+ const stats = await lstat(path).catch(() => undefined);
313
+ if (stats === undefined)
314
+ continue;
315
+ kind = entryOf(stats).kind;
316
+ listed.set(path, kind);
317
+ // A target's own entry (following links) wins over its listing by another target.
318
+ if (!snapshot.has(path))
319
+ await record(path, stats);
320
+ }
321
+ if (target.recursive && kind === "directory") {
322
+ countDirectory(path);
323
+ await scanDirectory(index, target, path);
296
324
  }
297
325
  }
298
326
  };
299
- for (const target of this.#targets) {
327
+ for (const [index, target] of this.#targets.entries()) {
300
328
  for (const ancestor of ancestorsOf(target.path)) {
301
329
  if (snapshot.has(ancestor))
302
330
  continue;
@@ -305,24 +333,37 @@ export class NodeFileWatcher {
305
333
  if (stats !== undefined)
306
334
  snapshot.set(ancestor, { ...entryOf(stats), size: 0, mtimeMs: 0 });
307
335
  }
308
- // The target itself may be a symbolic link to what is watched; follow it.
309
- const stats = await stat(target.path).catch(() => undefined);
310
- if (stats === undefined)
336
+ // The target itself may be a symbolic link to what is watched; follow it. A missing target is watched for its
337
+ // creation; one that cannot be reached for lack of permission fails.
338
+ let stats;
339
+ try {
340
+ stats = await stat(target.path);
341
+ }
342
+ catch (error) {
343
+ if (isDenied(error))
344
+ throw error;
311
345
  continue;
346
+ }
312
347
  await record(target.path, stats);
348
+ if (stats.isFile() && (await lstat(target.path).catch(() => undefined))?.isSymbolicLink()) {
349
+ linkedFiles.add(target.path);
350
+ }
313
351
  if (stats.isDirectory()) {
314
- countDirectory();
315
- await scanDirectory(target, target.path);
352
+ countDirectory(target.path);
353
+ await scanDirectory(index, target, target.path);
316
354
  }
317
355
  }
318
- return snapshot;
356
+ return { snapshot, linkedFiles };
319
357
  }
320
358
  /**
321
- * Watch every existing ancestor of each target, each target directory, and on Linux each directory below a recursive
322
- * target (elsewhere one recursive watcher per target). Returns whether a watcher was added.
359
+ * Watch every existing ancestor of each target, each target directory, each target that is a symbolic link to a file
360
+ * (changes to that file are not events of the link's directory), and on Linux each directory below a recursive target
361
+ * (elsewhere one recursive watcher per target). Returns whether a watcher was added.
323
362
  */
324
- #reconcileWatchers(snapshot) {
363
+ #reconcileWatchers({ snapshot, linkedFiles }) {
325
364
  const wanted = new Map();
365
+ for (const path of linkedFiles)
366
+ wanted.set(path, false);
326
367
  const perDirectory = process.platform === "linux" || process.platform === "android";
327
368
  for (const target of this.#targets) {
328
369
  for (const ancestor of ancestorsOf(target.path)) {
@@ -341,26 +382,32 @@ export class NodeFileWatcher {
341
382
  }
342
383
  }
343
384
  }
344
- for (const [path, watcher] of this.#watchers) {
345
- if (!wanted.has(path)) {
346
- watcher.close();
385
+ for (const [path, installed] of this.#watchers) {
386
+ const entry = snapshot.get(path);
387
+ // Gone, or replaced: a watcher follows the directory it was installed on, not the path.
388
+ if (!wanted.has(path) || entry === undefined || entry.dev !== installed.dev || entry.ino !== installed.ino) {
389
+ installed.watcher.close();
347
390
  this.#watchers.delete(path);
348
391
  }
349
392
  }
350
393
  let added = false;
351
394
  for (const [path, recursive] of wanted) {
352
- if (this.#watchers.has(path))
395
+ const entry = snapshot.get(path);
396
+ if (this.#watchers.has(path) || entry === undefined)
353
397
  continue;
398
+ const linked = linkedFiles.has(path);
354
399
  try {
355
- const watcher = fsWatch(path, { recursive, persistent: false }, (_event, filename) => this.#onEvent(path, filename === null ? undefined : String(filename)));
400
+ const watcher = fsWatch(path, { recursive, persistent: false }, (_event, filename) => linked
401
+ ? this.#onLinkedFileEvent(path)
402
+ : this.#onEvent(path, filename === null ? undefined : String(filename)));
356
403
  // A watched directory that disappears or fails: rescan, which also replaces the watcher.
357
404
  watcher.on("error", () => {
358
405
  watcher.close();
359
- if (this.#watchers.get(path) === watcher)
406
+ if (this.#watchers.get(path)?.watcher === watcher)
360
407
  this.#watchers.delete(path);
361
408
  this.#scheduleFlush();
362
409
  });
363
- this.#watchers.set(path, watcher);
410
+ this.#watchers.set(path, { watcher, dev: entry.dev, ino: entry.ino });
364
411
  added = true;
365
412
  }
366
413
  catch (error) {
@@ -377,7 +424,7 @@ export class NodeFileWatcher {
377
424
  if (this.#mode === "polling")
378
425
  return;
379
426
  this.#mode = "polling";
380
- for (const watcher of this.#watchers.values())
427
+ for (const { watcher } of this.#watchers.values())
381
428
  watcher.close();
382
429
  this.#watchers.clear();
383
430
  this.#deliver({ overflow: true });
@@ -396,6 +443,13 @@ export class NodeFileWatcher {
396
443
  if (relevant || filename === undefined)
397
444
  this.#scheduleFlush();
398
445
  }
446
+ /** The file a target links to changed: report the target. */
447
+ #onLinkedFileEvent(target) {
448
+ if (this.#closed)
449
+ return;
450
+ this.#events.add(target);
451
+ this.#scheduleFlush();
452
+ }
399
453
  #inScope(path) {
400
454
  for (const target of this.#targets) {
401
455
  if (isWithin(target.path, path))
@@ -1 +1 @@
1
- {"version":3,"file":"node-watch.js","sourceRoot":"","sources":["../../src/env/node-watch.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { type FSWatcher, watch as fsWatch, type Stats } from \"node:fs\";\nimport { lstat, readdir, readFile, stat, statfs } from \"node:fs/promises\";\nimport { dirname, relative, sep } from \"node:path\";\nimport type { Context } from \"@earendil-works/chord\";\nimport { FileError, type FileWatcher, type WatchChange, type WatchTarget } from \"./index.ts\";\n\n/** How `NodeExecutionEnv` watches. */\nexport interface NodeWatchOptions {\n\t/**\n\t * Force a mode. By default `polling` is chosen on Windows, where native watchers keep directories open and so block\n\t * renaming their parents, and for file systems that do not report remote changes.\n\t */\n\tmode?: \"native\" | \"polling\";\n\t/** Interval between snapshots in `polling` mode; default 2000 ms. */\n\tpollIntervalMs?: number;\n\t/** Most directories one watcher covers; default 10,000. */\n\tmaxDirectories?: number;\n}\n\ntype ResolvedTarget = {\n\treadonly path: string;\n\treadonly recursive: boolean;\n\treadonly hidden: boolean;\n\treadonly names: ReadonlySet<string>;\n};\n\n/** What a snapshot remembers of one path. Directories and ancestors are compared by identity only. */\ntype Entry = {\n\treadonly kind: \"file\" | \"directory\" | \"symlink\" | \"other\";\n\treadonly dev: number;\n\treadonly ino: number;\n\treadonly size: number;\n\treadonly mtimeMs: number;\n\treadonly hash?: string;\n};\n\ntype Snapshot = Map<string, Entry>;\n\nconst DEBOUNCE_MS = 50;\n/**\n * On macOS, `fs.watch` returns before libuv's FSEvents stream is live, and changes in between are never reported\n * (about 2% of changes made right after the call, none after 100 ms). A rescan this long after installing watchers\n * catches them.\n */\nconst FSEVENTS_SETTLE_MS = 500;\nconst DEFAULT_POLL_MS = 2000;\nconst DEFAULT_MAX_DIRECTORIES = 10_000;\n/** In polling mode, recently modified small files are also compared by content: a second write within the file system's\n * timestamp granularity can keep size and modification time. */\nconst HASH_MAX_BYTES = 256 * 1024;\nconst HASH_RECENT_MS = 5000;\n\n/** Linux `statfs` magic numbers of file systems that accept watches but do not report changes made elsewhere. */\nconst UNRELIABLE_FILE_SYSTEMS = new Set([\n\t0x6969, // NFS\n\t0x517b, // SMB\n\t0xff534d42, // CIFS\n\t0xfe534d42, // SMB2\n\t0x65735546, // FUSE (sshfs, Android shared storage)\n\t0x01021997, // 9P (WSL2 Windows drives)\n\t0x0bd00bd0, // Lustre\n\t0x47504653, // GPFS\n\t0x00c36400, // Ceph\n\t0x5346414f, // OpenAFS\n\t0x6b414653, // kAFS\n\t0x5dca2df5, // sdcardfs\n]);\n\nclass BudgetExceeded extends Error {}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n\treturn error instanceof Error && \"code\" in error;\n}\n\nfunction entryOf(stats: Stats, hash?: string): Entry {\n\tconst kind = stats.isFile()\n\t\t? \"file\"\n\t\t: stats.isDirectory()\n\t\t\t? \"directory\"\n\t\t\t: stats.isSymbolicLink()\n\t\t\t\t? \"symlink\"\n\t\t\t\t: \"other\";\n\treturn {\n\t\tkind,\n\t\tdev: stats.dev,\n\t\tino: stats.ino,\n\t\tsize: kind === \"directory\" ? 0 : stats.size,\n\t\tmtimeMs: kind === \"directory\" ? 0 : stats.mtimeMs,\n\t\t...(hash === undefined ? {} : { hash }),\n\t};\n}\n\nfunction sameEntry(a: Entry, b: Entry): boolean {\n\treturn (\n\t\ta.kind === b.kind &&\n\t\ta.dev === b.dev &&\n\t\ta.ino === b.ino &&\n\t\ta.size === b.size &&\n\t\ta.mtimeMs === b.mtimeMs &&\n\t\ta.hash === b.hash\n\t);\n}\n\n/** Ancestors of `path`, nearest first, up to the root. */\nfunction ancestorsOf(path: string): string[] {\n\tconst result: string[] = [];\n\tfor (let current = dirname(path); ; current = dirname(current)) {\n\t\tresult.push(current);\n\t\tif (dirname(current) === current) return result;\n\t}\n}\n\n/** Whether `path` is `ancestor` or below it. */\nfunction isWithin(path: string, ancestor: string): boolean {\n\treturn path === ancestor || path.startsWith(ancestor.endsWith(sep) ? ancestor : ancestor + sep);\n}\n\nfunction excluded(target: ResolvedTarget, name: string): boolean {\n\treturn (target.hidden && name.startsWith(\".\")) || target.names.has(name);\n}\n\n/**\n * Watches by snapshots: native events only trigger a debounced rescan, and changes are the difference between\n * snapshots plus the event paths. A replaced file, a renamed or recreated ancestor, or a directory created with its\n * contents therefore never depends on which events an operating system sends. New directories get their watchers\n * before they are scanned again, so nothing written into them before the watcher existed is missed.\n */\nexport class NodeFileWatcher implements FileWatcher {\n\treadonly #targets: readonly ResolvedTarget[];\n\treadonly #onChange: (change: WatchChange) => void;\n\treadonly #pollIntervalMs: number;\n\treadonly #maxDirectories: number;\n\treadonly #watchers = new Map<string, FSWatcher>();\n\treadonly #events = new Set<string>();\n\t#mode: \"native\" | \"polling\";\n\t#snapshot: Snapshot = new Map();\n\t#timer: ReturnType<typeof setTimeout> | undefined;\n\t#settleTimer: ReturnType<typeof setTimeout> | undefined;\n\t#running: Promise<void> | undefined;\n\t#dirty = false;\n\t#closed = false;\n\n\tprivate constructor(\n\t\ttargets: readonly ResolvedTarget[],\n\t\tonChange: (change: WatchChange) => void,\n\t\tmode: \"native\" | \"polling\",\n\t\toptions: NodeWatchOptions,\n\t) {\n\t\tthis.#targets = targets;\n\t\tthis.#onChange = onChange;\n\t\tthis.#mode = mode;\n\t\tthis.#pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_MS;\n\t\tthis.#maxDirectories = options.maxDirectories ?? DEFAULT_MAX_DIRECTORIES;\n\t}\n\n\t/** Establish coverage: watchers first, then the snapshot later changes are compared with. */\n\tstatic async open(\n\t\ttargets: readonly WatchTarget[],\n\t\tresolvePath: (path: string) => string,\n\t\tonChange: (change: WatchChange) => void,\n\t\toptions: NodeWatchOptions,\n\t): Promise<NodeFileWatcher> {\n\t\tconst resolved = targets.map((target) => ({\n\t\t\tpath: resolvePath(target.path),\n\t\t\trecursive: target.recursive === true,\n\t\t\thidden: target.exclude?.hidden === true,\n\t\t\tnames: new Set(target.exclude?.names ?? []),\n\t\t}));\n\t\t// Windows refuses to rename a directory while another directory below it is open, and a native watcher keeps\n\t\t// every watched directory open: watching would break renames of their parents, so Windows polls.\n\t\tconst mode =\n\t\t\toptions.mode ??\n\t\t\t(process.platform === \"win32\" || (await anyUnreliable(resolved.map((target) => target.path)))\n\t\t\t\t? \"polling\"\n\t\t\t\t: \"native\");\n\t\tconst watcher = new NodeFileWatcher(resolved, onChange, mode, options);\n\t\ttry {\n\t\t\tawait watcher.#sync(false);\n\t\t} catch (error) {\n\t\t\twatcher.#stop();\n\t\t\tif (error instanceof BudgetExceeded) throw new FileError(\"invalid\", error.message);\n\t\t\tthrow error;\n\t\t}\n\t\twatcher.#schedulePoll();\n\t\treturn watcher;\n\t}\n\n\tget mode(): \"native\" | \"polling\" {\n\t\treturn this.#mode;\n\t}\n\n\tasync close(_context: Context): Promise<void> {\n\t\tif (this.#closed) return;\n\t\tthis.#stop();\n\t\tawait this.#running;\n\t}\n\n\t#stop(): void {\n\t\tthis.#closed = true;\n\t\tclearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tclearTimeout(this.#settleTimer);\n\t\tthis.#settleTimer = undefined;\n\t\tfor (const watcher of this.#watchers.values()) watcher.close();\n\t\tthis.#watchers.clear();\n\t}\n\n\t#deliver(change: WatchChange): void {\n\t\tif (this.#closed) return;\n\t\ttry {\n\t\t\tthis.#onChange(change);\n\t\t} catch {\n\t\t\t// A throwing callback must not stop watching.\n\t\t}\n\t}\n\n\t#schedulePoll(): void {\n\t\tif (this.#closed || this.#mode !== \"polling\") return;\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tthis.#timer = undefined;\n\t\t\tvoid this.#flush().finally(() => this.#schedulePoll());\n\t\t}, this.#pollIntervalMs);\n\t}\n\n\t#scheduleFlush(): void {\n\t\tif (this.#closed || this.#timer !== undefined) return;\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tthis.#timer = undefined;\n\t\t\tvoid this.#flush();\n\t\t}, DEBOUNCE_MS);\n\t}\n\n\t#scheduleSettle(): void {\n\t\tif (this.#closed) return;\n\t\tclearTimeout(this.#settleTimer);\n\t\tthis.#settleTimer = setTimeout(() => {\n\t\t\tthis.#settleTimer = undefined;\n\t\t\tvoid this.#flush();\n\t\t}, FSEVENTS_SETTLE_MS);\n\t}\n\n\t#flush(): Promise<void> {\n\t\tif (this.#running !== undefined) {\n\t\t\tthis.#dirty = true;\n\t\t\treturn this.#running;\n\t\t}\n\t\tthis.#running = (async () => {\n\t\t\ttry {\n\t\t\t\tdo {\n\t\t\t\t\tthis.#dirty = false;\n\t\t\t\t\tconst events = [...this.#events];\n\t\t\t\t\tthis.#events.clear();\n\t\t\t\t\tconst changed = await this.#sync(true);\n\t\t\t\t\tfor (const path of events) changed.add(path);\n\t\t\t\t\tif (changed.size > 0) this.#deliver({ paths: [...changed].sort() });\n\t\t\t\t} while (this.#dirty && !this.#closed);\n\t\t\t} catch (error) {\n\t\t\t\tconst fileError =\n\t\t\t\t\terror instanceof FileError\n\t\t\t\t\t\t? error\n\t\t\t\t\t\t: new FileError(\"invalid\", error instanceof Error ? error.message : String(error));\n\t\t\t\tthis.#deliver({ error: fileError });\n\t\t\t\tthis.#stop();\n\t\t\t} finally {\n\t\t\t\tthis.#running = undefined;\n\t\t\t}\n\t\t})();\n\t\treturn this.#running;\n\t}\n\n\t/** Rescan, report differences, and install watchers for new directories, rescanning until none are new. */\n\tasync #sync(report: boolean): Promise<Set<string>> {\n\t\tconst changed = new Set<string>();\n\t\tfor (let round = 0; round < 10 && !this.#closed; round++) {\n\t\t\tconst next = await this.#scan();\n\t\t\tif (report) for (const path of this.#diff(this.#snapshot, next)) changed.add(path);\n\t\t\tthis.#snapshot = next;\n\t\t\tif (this.#mode === \"polling\" || !this.#reconcileWatchers(next)) break;\n\t\t\tif (process.platform === \"darwin\") this.#scheduleSettle();\n\t\t\t// Something written into a new directory before its watcher existed shows up in the next round.\n\t\t\treport = true;\n\t\t}\n\t\treturn changed;\n\t}\n\n\t#diff(previous: Snapshot, next: Snapshot): string[] {\n\t\tconst changed: string[] = [];\n\t\tfor (const [path, entry] of next) {\n\t\t\tconst before = previous.get(path);\n\t\t\tif (before === undefined || !sameEntry(before, entry)) changed.push(this.#reported(path));\n\t\t}\n\t\tfor (const path of previous.keys()) if (!next.has(path)) changed.push(this.#reported(path));\n\t\treturn changed;\n\t}\n\n\t/** An ancestor that changed identity moved every target below it; report those targets. */\n\t#reported(path: string): string {\n\t\tif (this.#targets.some((target) => isWithin(path, target.path))) return path;\n\t\treturn this.#targets.find((target) => isWithin(target.path, path))?.path ?? path;\n\t}\n\n\tasync #scan(): Promise<Snapshot> {\n\t\tconst snapshot: Snapshot = new Map();\n\t\tlet directories = 0;\n\t\tconst countDirectory = (): void => {\n\t\t\tif (++directories > this.#maxDirectories) {\n\t\t\t\tthrow new BudgetExceeded(`Watched paths exceed ${this.#maxDirectories} directories`);\n\t\t\t}\n\t\t};\n\t\tconst record = async (path: string, stats: Stats): Promise<void> => {\n\t\t\tlet hash: string | undefined;\n\t\t\tif (\n\t\t\t\tthis.#mode === \"polling\" &&\n\t\t\t\tstats.isFile() &&\n\t\t\t\tstats.size <= HASH_MAX_BYTES &&\n\t\t\t\tDate.now() - stats.mtimeMs < HASH_RECENT_MS\n\t\t\t) {\n\t\t\t\thash = await readFile(path).then(\n\t\t\t\t\t(content) => createHash(\"sha256\").update(content).digest(\"hex\"),\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tsnapshot.set(path, entryOf(stats, hash));\n\t\t};\n\t\tconst scanDirectory = async (target: ResolvedTarget, directory: string): Promise<void> => {\n\t\t\tlet names: string[];\n\t\t\ttry {\n\t\t\t\tnames = await readdir(directory);\n\t\t\t} catch (error) {\n\t\t\t\tif (isNodeError(error) && [\"ENOENT\", \"EACCES\", \"EPERM\", \"ENOTDIR\"].includes(error.code ?? \"\")) return;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tfor (const name of names) {\n\t\t\t\tif (excluded(target, name)) continue;\n\t\t\t\tconst path = `${directory.endsWith(sep) ? directory : directory + sep}${name}`;\n\t\t\t\tif (snapshot.has(path)) continue;\n\t\t\t\tconst stats = await lstat(path).catch(() => undefined);\n\t\t\t\tif (stats === undefined) continue;\n\t\t\t\tawait record(path, stats);\n\t\t\t\tif (target.recursive && stats.isDirectory()) {\n\t\t\t\t\tcountDirectory();\n\t\t\t\t\tawait scanDirectory(target, path);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tfor (const target of this.#targets) {\n\t\t\tfor (const ancestor of ancestorsOf(target.path)) {\n\t\t\t\tif (snapshot.has(ancestor)) continue;\n\t\t\t\tconst stats = await lstat(ancestor).catch(() => undefined);\n\t\t\t\t// Identity only: an ancestor's own timestamps change with every unrelated sibling.\n\t\t\t\tif (stats !== undefined) snapshot.set(ancestor, { ...entryOf(stats), size: 0, mtimeMs: 0 });\n\t\t\t}\n\t\t\t// The target itself may be a symbolic link to what is watched; follow it.\n\t\t\tconst stats = await stat(target.path).catch(() => undefined);\n\t\t\tif (stats === undefined) continue;\n\t\t\tawait record(target.path, stats);\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tcountDirectory();\n\t\t\t\tawait scanDirectory(target, target.path);\n\t\t\t}\n\t\t}\n\t\treturn snapshot;\n\t}\n\n\t/**\n\t * Watch every existing ancestor of each target, each target directory, and on Linux each directory below a recursive\n\t * target (elsewhere one recursive watcher per target). Returns whether a watcher was added.\n\t */\n\t#reconcileWatchers(snapshot: Snapshot): boolean {\n\t\tconst wanted = new Map<string, boolean>();\n\t\tconst perDirectory = process.platform === \"linux\" || process.platform === \"android\";\n\t\tfor (const target of this.#targets) {\n\t\t\tfor (const ancestor of ancestorsOf(target.path)) {\n\t\t\t\tif (snapshot.get(ancestor)?.kind === \"directory\" && !wanted.has(ancestor)) wanted.set(ancestor, false);\n\t\t\t}\n\t\t\tif (snapshot.get(target.path)?.kind !== \"directory\") continue;\n\t\t\tconst recursiveRoot = target.recursive && !perDirectory;\n\t\t\twanted.set(target.path, recursiveRoot);\n\t\t\tif (target.recursive && perDirectory) {\n\t\t\t\tfor (const [path, entry] of snapshot) {\n\t\t\t\t\tif (entry.kind === \"directory\" && path !== target.path && isWithin(path, target.path)) {\n\t\t\t\t\t\twanted.set(path, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const [path, watcher] of this.#watchers) {\n\t\t\tif (!wanted.has(path)) {\n\t\t\t\twatcher.close();\n\t\t\t\tthis.#watchers.delete(path);\n\t\t\t}\n\t\t}\n\t\tlet added = false;\n\t\tfor (const [path, recursive] of wanted) {\n\t\t\tif (this.#watchers.has(path)) continue;\n\t\t\ttry {\n\t\t\t\tconst watcher = fsWatch(path, { recursive, persistent: false }, (_event, filename) =>\n\t\t\t\t\tthis.#onEvent(path, filename === null ? undefined : String(filename)),\n\t\t\t\t);\n\t\t\t\t// A watched directory that disappears or fails: rescan, which also replaces the watcher.\n\t\t\t\twatcher.on(\"error\", () => {\n\t\t\t\t\twatcher.close();\n\t\t\t\t\tif (this.#watchers.get(path) === watcher) this.#watchers.delete(path);\n\t\t\t\t\tthis.#scheduleFlush();\n\t\t\t\t});\n\t\t\t\tthis.#watchers.set(path, watcher);\n\t\t\t\tadded = true;\n\t\t\t} catch (error) {\n\t\t\t\t// Out of watches or unsupported: compare snapshots from now on, and say coverage was uncertain.\n\t\t\t\tif (isNodeError(error) && error.code !== \"ENOENT\" && error.code !== \"EACCES\" && error.code !== \"EPERM\") {\n\t\t\t\t\tthis.#switchToPolling();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn added;\n\t}\n\n\t#switchToPolling(): void {\n\t\tif (this.#mode === \"polling\") return;\n\t\tthis.#mode = \"polling\";\n\t\tfor (const watcher of this.#watchers.values()) watcher.close();\n\t\tthis.#watchers.clear();\n\t\tthis.#deliver({ overflow: true });\n\t\tclearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tthis.#schedulePoll();\n\t}\n\n\t#onEvent(directory: string, filename: string | undefined): void {\n\t\tif (this.#closed) return;\n\t\tconst path =\n\t\t\tfilename === undefined ? directory : `${directory.endsWith(sep) ? directory : directory + sep}${filename}`;\n\t\t// Events about unrelated siblings of an ancestor, or about excluded entries, are ignored.\n\t\tconst relevant = this.#inScope(path);\n\t\tif (relevant) this.#events.add(this.#reported(path));\n\t\tif (relevant || filename === undefined) this.#scheduleFlush();\n\t}\n\n\t#inScope(path: string): boolean {\n\t\tfor (const target of this.#targets) {\n\t\t\tif (isWithin(target.path, path)) return true;\n\t\t\tif (!isWithin(path, target.path) || path === target.path) continue;\n\t\t\tconst components = relative(target.path, path).split(sep);\n\t\t\tif (!target.recursive && components.length > 1) continue;\n\t\t\tif (components.some((name) => excluded(target, name))) continue;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\n\n/** Whether any path, or its nearest existing ancestor, is on a file system that does not report remote changes. */\nasync function anyUnreliable(paths: readonly string[]): Promise<boolean> {\n\tif (process.platform !== \"linux\" && process.platform !== \"android\") return false;\n\tfor (const path of paths) {\n\t\tfor (const candidate of [path, ...ancestorsOf(path)]) {\n\t\t\tconst info = await statfs(candidate).catch(() => undefined);\n\t\t\tif (info === undefined) continue;\n\t\t\tif (UNRELIABLE_FILE_SYSTEMS.has(info.type)) return true;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"node-watch.js","sourceRoot":"","sources":["../../src/env/node-watch.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { type FSWatcher, watch as fsWatch, type Stats } from \"node:fs\";\nimport { lstat, readdir, readFile, stat, statfs } from \"node:fs/promises\";\nimport { dirname, relative, sep } from \"node:path\";\nimport type { Context } from \"@earendil-works/chord\";\nimport { FileError, type FileWatcher, type WatchChange, type WatchTarget } from \"./index.ts\";\n\n/** How `NodeExecutionEnv` watches. */\nexport interface NodeWatchOptions {\n\t/**\n\t * Force a mode. By default `polling` is chosen on Windows, where native watchers keep directories open and so block\n\t * renaming their parents, and for file systems that do not report remote changes.\n\t */\n\tmode?: \"native\" | \"polling\";\n\t/** Interval between snapshots in `polling` mode; default 2000 ms. */\n\tpollIntervalMs?: number;\n\t/** Most directories one watcher covers; default 10,000. */\n\tmaxDirectories?: number;\n}\n\ntype ResolvedTarget = {\n\treadonly path: string;\n\treadonly recursive: boolean;\n\treadonly hidden: boolean;\n\treadonly names: ReadonlySet<string>;\n};\n\n/** What a snapshot remembers of one path. Directories and ancestors are compared by identity only. */\ntype Entry = {\n\treadonly kind: \"file\" | \"directory\" | \"symlink\" | \"other\";\n\treadonly dev: number;\n\treadonly ino: number;\n\treadonly size: number;\n\treadonly mtimeMs: number;\n\treadonly hash?: string;\n};\n\ntype Snapshot = Map<string, Entry>;\n\n/** A native watcher and the identity of what it was installed on: a path replaced by another file needs a new one. */\ntype Installed = { readonly watcher: FSWatcher; readonly dev: number; readonly ino: number };\n\n/** A scan: the snapshot, and targets that are symbolic links to files, whose files need their own watchers. */\ntype Scan = { readonly snapshot: Snapshot; readonly linkedFiles: ReadonlySet<string> };\n\nconst DEBOUNCE_MS = 50;\n/**\n * On macOS, `fs.watch` returns before libuv's FSEvents stream is live, and changes in between are never reported\n * (about 2% of changes made right after the call, none after 100 ms). A rescan this long after installing watchers\n * catches them.\n */\nconst FSEVENTS_SETTLE_MS = 500;\nconst DEFAULT_POLL_MS = 2000;\nconst DEFAULT_MAX_DIRECTORIES = 10_000;\n/** In polling mode, recently modified small files are also compared by content: a second write within the file system's\n * timestamp granularity can keep size and modification time. */\nconst HASH_MAX_BYTES = 256 * 1024;\nconst HASH_RECENT_MS = 5000;\n\n/** Linux `statfs` magic numbers of file systems that accept watches but do not report changes made elsewhere. */\nconst UNRELIABLE_FILE_SYSTEMS = new Set([\n\t0x6969, // NFS\n\t0x517b, // SMB\n\t0xff534d42, // CIFS\n\t0xfe534d42, // SMB2\n\t0x65735546, // FUSE (sshfs, Android shared storage)\n\t0x01021997, // 9P (WSL2 Windows drives)\n\t0x0bd00bd0, // Lustre\n\t0x47504653, // GPFS\n\t0x00c36400, // Ceph\n\t0x5346414f, // OpenAFS\n\t0x6b414653, // kAFS\n\t0x5dca2df5, // sdcardfs\n]);\n\nclass BudgetExceeded extends Error {}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n\treturn error instanceof Error && \"code\" in error;\n}\n\nfunction isDenied(error: unknown): boolean {\n\treturn isNodeError(error) && (error.code === \"EACCES\" || error.code === \"EPERM\");\n}\n\nfunction entryOf(stats: Stats, hash?: string): Entry {\n\tconst kind = stats.isFile()\n\t\t? \"file\"\n\t\t: stats.isDirectory()\n\t\t\t? \"directory\"\n\t\t\t: stats.isSymbolicLink()\n\t\t\t\t? \"symlink\"\n\t\t\t\t: \"other\";\n\treturn {\n\t\tkind,\n\t\tdev: stats.dev,\n\t\tino: stats.ino,\n\t\tsize: kind === \"directory\" ? 0 : stats.size,\n\t\tmtimeMs: kind === \"directory\" ? 0 : stats.mtimeMs,\n\t\t...(hash === undefined ? {} : { hash }),\n\t};\n}\n\nfunction sameEntry(a: Entry, b: Entry): boolean {\n\treturn (\n\t\ta.kind === b.kind &&\n\t\ta.dev === b.dev &&\n\t\ta.ino === b.ino &&\n\t\ta.size === b.size &&\n\t\ta.mtimeMs === b.mtimeMs &&\n\t\ta.hash === b.hash\n\t);\n}\n\n/** Ancestors of `path`, nearest first, up to the root. */\nfunction ancestorsOf(path: string): string[] {\n\tconst result: string[] = [];\n\tfor (let current = dirname(path); ; current = dirname(current)) {\n\t\tresult.push(current);\n\t\tif (dirname(current) === current) return result;\n\t}\n}\n\n/** Whether `path` is `ancestor` or below it. */\nfunction isWithin(path: string, ancestor: string): boolean {\n\treturn path === ancestor || path.startsWith(ancestor.endsWith(sep) ? ancestor : ancestor + sep);\n}\n\nfunction excluded(target: ResolvedTarget, name: string): boolean {\n\treturn (target.hidden && name.startsWith(\".\")) || target.names.has(name);\n}\n\n/**\n * Watches by snapshots: native events only trigger a debounced rescan, and changes are the difference between\n * snapshots plus the event paths. A replaced file, a renamed or recreated ancestor, or a directory created with its\n * contents therefore never depends on which events an operating system sends. New directories get their watchers\n * before they are scanned again, so nothing written into them before the watcher existed is missed.\n */\nexport class NodeFileWatcher implements FileWatcher {\n\treadonly #targets: readonly ResolvedTarget[];\n\treadonly #onChange: (change: WatchChange) => void;\n\treadonly #pollIntervalMs: number;\n\treadonly #maxDirectories: number;\n\treadonly #watchers = new Map<string, Installed>();\n\treadonly #events = new Set<string>();\n\t#mode: \"native\" | \"polling\";\n\t#snapshot: Snapshot = new Map();\n\t#timer: ReturnType<typeof setTimeout> | undefined;\n\t#settleTimer: ReturnType<typeof setTimeout> | undefined;\n\t#running: Promise<void> | undefined;\n\t#dirty = false;\n\t#closed = false;\n\n\tprivate constructor(\n\t\ttargets: readonly ResolvedTarget[],\n\t\tonChange: (change: WatchChange) => void,\n\t\tmode: \"native\" | \"polling\",\n\t\toptions: NodeWatchOptions,\n\t) {\n\t\tthis.#targets = targets;\n\t\tthis.#onChange = onChange;\n\t\tthis.#mode = mode;\n\t\tthis.#pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_MS;\n\t\tthis.#maxDirectories = options.maxDirectories ?? DEFAULT_MAX_DIRECTORIES;\n\t}\n\n\t/** Establish coverage: watchers first, then the snapshot later changes are compared with. */\n\tstatic async open(\n\t\ttargets: readonly WatchTarget[],\n\t\tresolvePath: (path: string) => string,\n\t\tonChange: (change: WatchChange) => void,\n\t\toptions: NodeWatchOptions,\n\t): Promise<NodeFileWatcher> {\n\t\tconst resolved = targets.map((target) => ({\n\t\t\tpath: resolvePath(target.path),\n\t\t\trecursive: target.recursive === true,\n\t\t\thidden: target.exclude?.hidden === true,\n\t\t\tnames: new Set(target.exclude?.names ?? []),\n\t\t}));\n\t\t// Windows refuses to rename a directory while another directory below it is open, and a native watcher keeps\n\t\t// every watched directory open: watching would break renames of their parents, so Windows polls.\n\t\tconst mode =\n\t\t\toptions.mode ??\n\t\t\t(process.platform === \"win32\" || (await anyUnreliable(resolved.map((target) => target.path)))\n\t\t\t\t? \"polling\"\n\t\t\t\t: \"native\");\n\t\tconst watcher = new NodeFileWatcher(resolved, onChange, mode, options);\n\t\ttry {\n\t\t\tawait watcher.#sync(false);\n\t\t} catch (error) {\n\t\t\twatcher.#stop();\n\t\t\tif (error instanceof BudgetExceeded) throw new FileError(\"invalid\", error.message);\n\t\t\tthrow error;\n\t\t}\n\t\twatcher.#schedulePoll();\n\t\treturn watcher;\n\t}\n\n\tget mode(): \"native\" | \"polling\" {\n\t\treturn this.#mode;\n\t}\n\n\tasync close(_context: Context): Promise<void> {\n\t\tif (this.#closed) return;\n\t\tthis.#stop();\n\t\tawait this.#running;\n\t}\n\n\t#stop(): void {\n\t\tthis.#closed = true;\n\t\tclearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tclearTimeout(this.#settleTimer);\n\t\tthis.#settleTimer = undefined;\n\t\tfor (const { watcher } of this.#watchers.values()) watcher.close();\n\t\tthis.#watchers.clear();\n\t}\n\n\t#deliver(change: WatchChange): void {\n\t\tif (this.#closed) return;\n\t\ttry {\n\t\t\tthis.#onChange(change);\n\t\t} catch {\n\t\t\t// A throwing callback must not stop watching.\n\t\t}\n\t}\n\n\t#schedulePoll(): void {\n\t\tif (this.#closed || this.#mode !== \"polling\") return;\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tthis.#timer = undefined;\n\t\t\tvoid this.#flush().finally(() => this.#schedulePoll());\n\t\t}, this.#pollIntervalMs);\n\t}\n\n\t#scheduleFlush(): void {\n\t\tif (this.#closed || this.#timer !== undefined) return;\n\t\tthis.#timer = setTimeout(() => {\n\t\t\tthis.#timer = undefined;\n\t\t\tvoid this.#flush();\n\t\t}, DEBOUNCE_MS);\n\t}\n\n\t#scheduleSettle(): void {\n\t\tif (this.#closed) return;\n\t\tclearTimeout(this.#settleTimer);\n\t\tthis.#settleTimer = setTimeout(() => {\n\t\t\tthis.#settleTimer = undefined;\n\t\t\tvoid this.#flush();\n\t\t}, FSEVENTS_SETTLE_MS);\n\t}\n\n\t#flush(): Promise<void> {\n\t\tif (this.#running !== undefined) {\n\t\t\tthis.#dirty = true;\n\t\t\treturn this.#running;\n\t\t}\n\t\tthis.#running = (async () => {\n\t\t\ttry {\n\t\t\t\tdo {\n\t\t\t\t\tthis.#dirty = false;\n\t\t\t\t\tconst events = [...this.#events];\n\t\t\t\t\tthis.#events.clear();\n\t\t\t\t\tconst changed = await this.#sync(true);\n\t\t\t\t\tfor (const path of events) changed.add(path);\n\t\t\t\t\tif (changed.size > 0) this.#deliver({ paths: [...changed].sort() });\n\t\t\t\t} while (this.#dirty && !this.#closed);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconst fileError =\n\t\t\t\t\terror instanceof FileError\n\t\t\t\t\t\t? error\n\t\t\t\t\t\t: new FileError(isDenied(error) ? \"permission_denied\" : \"invalid\", message);\n\t\t\t\tthis.#deliver({ error: fileError });\n\t\t\t\tthis.#stop();\n\t\t\t} finally {\n\t\t\t\tthis.#running = undefined;\n\t\t\t}\n\t\t})();\n\t\treturn this.#running;\n\t}\n\n\t/** Rescan, report differences, and install watchers for new directories, rescanning until none are new. */\n\tasync #sync(report: boolean): Promise<Set<string>> {\n\t\tconst changed = new Set<string>();\n\t\tfor (let round = 0; round < 10 && !this.#closed; round++) {\n\t\t\tconst scan = await this.#scan();\n\t\t\t// Closed during the scan: installing watchers now would leak them.\n\t\t\tif (this.#closed) break;\n\t\t\tconst next = scan.snapshot;\n\t\t\tif (report) for (const path of this.#diff(this.#snapshot, next)) changed.add(path);\n\t\t\tthis.#snapshot = next;\n\t\t\tif (this.#mode === \"polling\" || !this.#reconcileWatchers(scan)) break;\n\t\t\tif (process.platform === \"darwin\") this.#scheduleSettle();\n\t\t\t// Something written into a new directory before its watcher existed shows up in the next round.\n\t\t\treport = true;\n\t\t}\n\t\treturn changed;\n\t}\n\n\t#diff(previous: Snapshot, next: Snapshot): string[] {\n\t\tconst changed: string[] = [];\n\t\tfor (const [path, entry] of next) {\n\t\t\tconst before = previous.get(path);\n\t\t\tif (before === undefined || !sameEntry(before, entry)) changed.push(this.#reported(path));\n\t\t}\n\t\tfor (const path of previous.keys()) if (!next.has(path)) changed.push(this.#reported(path));\n\t\treturn changed;\n\t}\n\n\t/** An ancestor that changed identity moved every target below it; report those targets. */\n\t#reported(path: string): string {\n\t\tif (this.#targets.some((target) => isWithin(path, target.path))) return path;\n\t\treturn this.#targets.find((target) => isWithin(target.path, path))?.path ?? path;\n\t}\n\n\tasync #scan(): Promise<Scan> {\n\t\tconst snapshot: Snapshot = new Map();\n\t\tconst linkedFiles = new Set<string>();\n\t\t// Directories are counted once, but traversed once per target: overlapping targets differ in recursion and\n\t\t// exclusions, and an entry recorded for one target must still be descended into for another.\n\t\tconst counted = new Set<string>();\n\t\tconst traversed = new Set<string>();\n\t\t/** Kinds of listed entries, not following links: a target that links to a directory is recorded by `stat`, but\n\t\t * symbolic links below a recursive target are not followed. */\n\t\tconst listed = new Map<string, Entry[\"kind\"]>();\n\t\tconst countDirectory = (path: string): void => {\n\t\t\tcounted.add(path);\n\t\t\tif (counted.size > this.#maxDirectories) {\n\t\t\t\tthrow new BudgetExceeded(`Watched paths exceed ${this.#maxDirectories} directories`);\n\t\t\t}\n\t\t};\n\t\tconst record = async (path: string, stats: Stats): Promise<void> => {\n\t\t\tlet hash: string | undefined;\n\t\t\tif (\n\t\t\t\tthis.#mode === \"polling\" &&\n\t\t\t\tstats.isFile() &&\n\t\t\t\tstats.size <= HASH_MAX_BYTES &&\n\t\t\t\tDate.now() - stats.mtimeMs < HASH_RECENT_MS\n\t\t\t) {\n\t\t\t\thash = await readFile(path).then(\n\t\t\t\t\t(content) => createHash(\"sha256\").update(content).digest(\"hex\"),\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tsnapshot.set(path, entryOf(stats, hash));\n\t\t};\n\t\tconst scanDirectory = async (index: number, target: ResolvedTarget, directory: string): Promise<void> => {\n\t\t\tconst key = `${index}\\0${directory}`;\n\t\t\tif (traversed.has(key)) return;\n\t\t\ttraversed.add(key);\n\t\t\tlet names: string[];\n\t\t\ttry {\n\t\t\t\tnames = await readdir(directory);\n\t\t\t} catch (error) {\n\t\t\t\t// The watched directory itself must be readable; below it, unreadable directories are skipped.\n\t\t\t\tif (directory === target.path && isDenied(error)) throw error;\n\t\t\t\tif (isNodeError(error) && [\"ENOENT\", \"EACCES\", \"EPERM\", \"ENOTDIR\"].includes(error.code ?? \"\")) return;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tfor (const name of names) {\n\t\t\t\tif (excluded(target, name)) continue;\n\t\t\t\tconst path = `${directory.endsWith(sep) ? directory : directory + sep}${name}`;\n\t\t\t\tlet kind = listed.get(path);\n\t\t\t\tif (kind === undefined) {\n\t\t\t\t\tconst stats = await lstat(path).catch(() => undefined);\n\t\t\t\t\tif (stats === undefined) continue;\n\t\t\t\t\tkind = entryOf(stats).kind;\n\t\t\t\t\tlisted.set(path, kind);\n\t\t\t\t\t// A target's own entry (following links) wins over its listing by another target.\n\t\t\t\t\tif (!snapshot.has(path)) await record(path, stats);\n\t\t\t\t}\n\t\t\t\tif (target.recursive && kind === \"directory\") {\n\t\t\t\t\tcountDirectory(path);\n\t\t\t\t\tawait scanDirectory(index, target, path);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tfor (const [index, target] of this.#targets.entries()) {\n\t\t\tfor (const ancestor of ancestorsOf(target.path)) {\n\t\t\t\tif (snapshot.has(ancestor)) continue;\n\t\t\t\tconst stats = await lstat(ancestor).catch(() => undefined);\n\t\t\t\t// Identity only: an ancestor's own timestamps change with every unrelated sibling.\n\t\t\t\tif (stats !== undefined) snapshot.set(ancestor, { ...entryOf(stats), size: 0, mtimeMs: 0 });\n\t\t\t}\n\t\t\t// The target itself may be a symbolic link to what is watched; follow it. A missing target is watched for its\n\t\t\t// creation; one that cannot be reached for lack of permission fails.\n\t\t\tlet stats: Stats;\n\t\t\ttry {\n\t\t\t\tstats = await stat(target.path);\n\t\t\t} catch (error) {\n\t\t\t\tif (isDenied(error)) throw error;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tawait record(target.path, stats);\n\t\t\tif (stats.isFile() && (await lstat(target.path).catch(() => undefined))?.isSymbolicLink()) {\n\t\t\t\tlinkedFiles.add(target.path);\n\t\t\t}\n\t\t\tif (stats.isDirectory()) {\n\t\t\t\tcountDirectory(target.path);\n\t\t\t\tawait scanDirectory(index, target, target.path);\n\t\t\t}\n\t\t}\n\t\treturn { snapshot, linkedFiles };\n\t}\n\n\t/**\n\t * Watch every existing ancestor of each target, each target directory, each target that is a symbolic link to a file\n\t * (changes to that file are not events of the link's directory), and on Linux each directory below a recursive target\n\t * (elsewhere one recursive watcher per target). Returns whether a watcher was added.\n\t */\n\t#reconcileWatchers({ snapshot, linkedFiles }: Scan): boolean {\n\t\tconst wanted = new Map<string, boolean>();\n\t\tfor (const path of linkedFiles) wanted.set(path, false);\n\t\tconst perDirectory = process.platform === \"linux\" || process.platform === \"android\";\n\t\tfor (const target of this.#targets) {\n\t\t\tfor (const ancestor of ancestorsOf(target.path)) {\n\t\t\t\tif (snapshot.get(ancestor)?.kind === \"directory\" && !wanted.has(ancestor)) wanted.set(ancestor, false);\n\t\t\t}\n\t\t\tif (snapshot.get(target.path)?.kind !== \"directory\") continue;\n\t\t\tconst recursiveRoot = target.recursive && !perDirectory;\n\t\t\twanted.set(target.path, recursiveRoot);\n\t\t\tif (target.recursive && perDirectory) {\n\t\t\t\tfor (const [path, entry] of snapshot) {\n\t\t\t\t\tif (entry.kind === \"directory\" && path !== target.path && isWithin(path, target.path)) {\n\t\t\t\t\t\twanted.set(path, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const [path, installed] of this.#watchers) {\n\t\t\tconst entry = snapshot.get(path);\n\t\t\t// Gone, or replaced: a watcher follows the directory it was installed on, not the path.\n\t\t\tif (!wanted.has(path) || entry === undefined || entry.dev !== installed.dev || entry.ino !== installed.ino) {\n\t\t\t\tinstalled.watcher.close();\n\t\t\t\tthis.#watchers.delete(path);\n\t\t\t}\n\t\t}\n\t\tlet added = false;\n\t\tfor (const [path, recursive] of wanted) {\n\t\t\tconst entry = snapshot.get(path);\n\t\t\tif (this.#watchers.has(path) || entry === undefined) continue;\n\t\t\tconst linked = linkedFiles.has(path);\n\t\t\ttry {\n\t\t\t\tconst watcher = fsWatch(path, { recursive, persistent: false }, (_event, filename) =>\n\t\t\t\t\tlinked\n\t\t\t\t\t\t? this.#onLinkedFileEvent(path)\n\t\t\t\t\t\t: this.#onEvent(path, filename === null ? undefined : String(filename)),\n\t\t\t\t);\n\t\t\t\t// A watched directory that disappears or fails: rescan, which also replaces the watcher.\n\t\t\t\twatcher.on(\"error\", () => {\n\t\t\t\t\twatcher.close();\n\t\t\t\t\tif (this.#watchers.get(path)?.watcher === watcher) this.#watchers.delete(path);\n\t\t\t\t\tthis.#scheduleFlush();\n\t\t\t\t});\n\t\t\t\tthis.#watchers.set(path, { watcher, dev: entry.dev, ino: entry.ino });\n\t\t\t\tadded = true;\n\t\t\t} catch (error) {\n\t\t\t\t// Out of watches or unsupported: compare snapshots from now on, and say coverage was uncertain.\n\t\t\t\tif (isNodeError(error) && error.code !== \"ENOENT\" && error.code !== \"EACCES\" && error.code !== \"EPERM\") {\n\t\t\t\t\tthis.#switchToPolling();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn added;\n\t}\n\n\t#switchToPolling(): void {\n\t\tif (this.#mode === \"polling\") return;\n\t\tthis.#mode = \"polling\";\n\t\tfor (const { watcher } of this.#watchers.values()) watcher.close();\n\t\tthis.#watchers.clear();\n\t\tthis.#deliver({ overflow: true });\n\t\tclearTimeout(this.#timer);\n\t\tthis.#timer = undefined;\n\t\tthis.#schedulePoll();\n\t}\n\n\t#onEvent(directory: string, filename: string | undefined): void {\n\t\tif (this.#closed) return;\n\t\tconst path =\n\t\t\tfilename === undefined ? directory : `${directory.endsWith(sep) ? directory : directory + sep}${filename}`;\n\t\t// Events about unrelated siblings of an ancestor, or about excluded entries, are ignored.\n\t\tconst relevant = this.#inScope(path);\n\t\tif (relevant) this.#events.add(this.#reported(path));\n\t\tif (relevant || filename === undefined) this.#scheduleFlush();\n\t}\n\n\t/** The file a target links to changed: report the target. */\n\t#onLinkedFileEvent(target: string): void {\n\t\tif (this.#closed) return;\n\t\tthis.#events.add(target);\n\t\tthis.#scheduleFlush();\n\t}\n\n\t#inScope(path: string): boolean {\n\t\tfor (const target of this.#targets) {\n\t\t\tif (isWithin(target.path, path)) return true;\n\t\t\tif (!isWithin(path, target.path) || path === target.path) continue;\n\t\t\tconst components = relative(target.path, path).split(sep);\n\t\t\tif (!target.recursive && components.length > 1) continue;\n\t\t\tif (components.some((name) => excluded(target, name))) continue;\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n}\n\n/** Whether any path, or its nearest existing ancestor, is on a file system that does not report remote changes. */\nasync function anyUnreliable(paths: readonly string[]): Promise<boolean> {\n\tif (process.platform !== \"linux\" && process.platform !== \"android\") return false;\n\tfor (const path of paths) {\n\t\tfor (const candidate of [path, ...ancestorsOf(path)]) {\n\t\t\tconst info = await statfs(candidate).catch(() => undefined);\n\t\t\tif (info === undefined) continue;\n\t\t\tif (UNRELIABLE_FILE_SYSTEMS.has(info.type)) return true;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn false;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EACX,KAAK,EACL,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,uBAAuB,EAEvB,eAAe,EACf,cAAc,EAEd,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,oBAAoB,EAAE,uBAKlC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,gBAKvC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,kEAQnB,CAAC;AAEH,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,QAAQ,CAY/E;AAED,4HAA4H;AAC5H,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1G;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9G;AAuCD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzF;AAED,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAS,gBAAgB,EACzD,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,EACvC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC9B,KAAK,CAAC,IAAI,CAAC,CA0Db"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,KAAK,EACX,KAAK,EACL,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,uBAAuB,EAEvB,eAAe,EACf,cAAc,EAEd,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,oBAAoB,EAAE,uBAKlC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,gBAKvC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAE/C,6FAA6F;AAC7F,eAAO,MAAM,QAAQ,kEAQnB,CAAC;AAEH,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,QAAQ,CAgB/E;AAED,4HAA4H;AAC5H,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1G;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9G;AAuCD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzF;AAED,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAMnE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAS,gBAAgB,EACzD,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,EACvC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC9B,KAAK,CAAC,IAAI,CAAC,CA0Db"}
@@ -36,7 +36,11 @@ export function resolveSettings(settings) {
36
36
  stream: { ...settings?.stream },
37
37
  retry: { ...DEFAULT_RETRY_POLICY, ...settings?.retry },
38
38
  compaction: { ...DEFAULT_COMPACTION_POLICY, ...settings?.compaction },
39
- progress: { ...DEFAULT_PROGRESS_POLICY, ...settings?.progress },
39
+ // Field by field, so an explicitly undefined interval keeps its default.
40
+ progress: {
41
+ partialIntervalMs: settings?.progress?.partialIntervalMs ?? DEFAULT_PROGRESS_POLICY.partialIntervalMs,
42
+ outputIntervalMs: settings?.progress?.outputIntervalMs ?? DEFAULT_PROGRESS_POLICY.outputIntervalMs,
43
+ },
40
44
  toolExecution: settings?.toolExecution ?? "parallel",
41
45
  steeringMode: settings?.steeringMode ?? "one-at-a-time",
42
46
  followUpMode: settings?.followUpMode ?? "one-at-a-time",
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAc,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAiB5C,MAAM,CAAC,MAAM,oBAAoB,GAA4B;IAC5D,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,IAAI;IACjB,eAAe,EAAE,KAAK;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAqB;IAC1D,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,KAAK;IACvB,gBAAgB,EAAE,KAAK;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAmB;IACtD,iBAAiB,EAAE,GAAG;IACtB,gBAAgB,EAAE,GAAG;CACrB,CAAC;AAEF,8DAA8D;AAC9D,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAE/C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,QAAQ,GAAG,SAAS,CAAa;IAC7C,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,YAAY;IACrB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IACnB,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;CAC1B,CAAC,CAAC;AAEH,8FAA8F;AAC9F,MAAM,UAAU,eAAe,CAAC,QAAqC;IACpE,MAAM,UAAU,GAAG,QAAQ,EAAE,UAAU,CAAC;IACxC,OAAO;QACN,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;QACnD,MAAM,EAAE,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE;QAC/B,KAAK,EAAE,EAAE,GAAG,oBAAoB,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE;QACtD,UAAU,EAAE,EAAE,GAAG,yBAAyB,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE;QACrE,QAAQ,EAAE,EAAE,GAAG,uBAAuB,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE;QAC/D,aAAa,EAAE,QAAQ,EAAE,aAAa,IAAI,UAAU;QACpD,YAAY,EAAE,QAAQ,EAAE,YAAY,IAAI,eAAe;QACvD,YAAY,EAAE,QAAQ,EAAE,YAAY,IAAI,eAAe;KACvD,CAAC;AACH,CAAC;AAED,4HAA4H;AAC5H,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAM,EAAE,cAA8B,EAAE,MAAmB;IAC1F,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAM,EAAE,cAA8B,EAAE,KAAwB;IAC9F,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC;SAAM,CAAC;QACP,MAAM,MAAM,GAAI,KAA8B,CAAC,MAAM,CAAC;QACtD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACjD,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1E,CAAC;IACF,CAAC;AACF,CAAC;AAED,SAAS,WAAW,CAAC,KAAwB,EAAE,MAAmB;IACjE,MAAM,GAAG,GAAG,CAA6B,GAAM,EAAE,KAAuC,EAAE,EAAE;QAC3F,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QAChC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;;YACjC,KAAK,CAAC,GAAG,CAAC,GAAG,KAA6B,CAAC;IACjD,CAAC,CAAC;IACF,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACvG,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,GAAG,CACF,YAAY,EACZ,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI;QAC9C,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACnB,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC;YACnB,CAAC,CAAC;gBACA,GAAG,CAAC,UAAU,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvE,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;aAChF,CACJ,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,GAAG,CACF,OAAO,EACP,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAC9G,CAAC;IACF,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IACzC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,MAAM,CAAI,KAA4B;IAC9C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,KAAK,CAAC,KAA2C;IACzD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAAM,EAAE,YAAgC;IACzE,IAAI,YAAY,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO;IAC9C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;IACtD,IAAI,YAAY,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO;IAC7C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACxE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAe,CAAC,CAAC;AACrD,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,UAAU,CAAC,KAAY,EAAE,QAAgB;IACxD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,IAAI,EAAE;YAAE,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC3B,KAAuC,EACvC,QAAgC,EAChC,QAAkB,EAClB,MAAgC;IAEhC,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE3E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgB,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,UAAU;QAAE,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,IAAI,EAAE;YAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5G,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IACxD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,QAAQ,IAAI,EAAE;YAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACpF,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YAC1C,IAAI,MAAM,IAAI,IAAI;gBACjB,SAAS,CACR,QAAQ,EACR,IAAI,CAAC,IAAI,EACT,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACzB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EACnB,MAAM,CACN,CAAC;;gBAEF,SAAS,CACR,QAAQ,EACR,IAAI,CAAC,OAAO,EACZ,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAC/B,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EACxB,MAAM,CACN,CAAC;QACJ,CAAC;IACF,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,EAAE,KAAK,CAAC;IAC5B,IAAI,KAAa,CAAC;IAClB,IAAI,MAAM,KAAK,SAAS;QAAE,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;SACpD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,KAAK,GAAG,EAAE,CAAC;QACX,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,IAAI,KAAK,SAAS;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;IACF,CAAC;SAAM,CAAC;QACP,MAAM,OAAO,GAAG,IAAI,GAAG,CAAE,MAA+B,CAAC,MAAM,CAAC,CAAC;QACjE,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,EAAE,YAAY,CAAC;IACzC,MAAM,aAAa,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,IAAI,YAAY,KAAK,SAAS;QAAE,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC;IAE1G,MAAM,KAAK,GAAgB;QAC1B,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QAC7D,aAAa,EAAE,KAAK,EAAE,aAAa,IAAI,KAAK;QAC5C,UAAU;QACV,KAAK;QACL,QAAQ,EAAE,aAAa;QACvB,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;QACvD,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;KACvD,CAAC;IACF,OAAO,KAAK,CAAC;AACd,CAAC;AAED,6GAA6G;AAC7G,SAAS,gBAAgB,CACxB,MAAgC,EAChC,QAAgC,EAChC,QAAkB;IAElB,IAAI,QAAkB,CAAC;IACvB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,QAAQ,GAAG,MAAM,CAAC;SACxC,CAAC;QACL,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChH,MAAM,IAAI,GAAG,MAA2D,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QAC5C,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACjF,CAAC;IACD,MAAM,UAAU,GAAsB,EAAE,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,SAAS,KAAK,SAAS;YAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,UAAU,CAAC;AACnB,CAAC;AAED,SAAS,SAAS,CACjB,KAAqB,EACrB,MAAc,EACd,IAAoB,EACpB,MAA2B,EAC3B,MAAgC;IAEhC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,OAAO,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnG,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,KAAK,CAAC,CAAC;IACf,CAAC;AACF,CAAC","sourcesContent":["import { copyJson, type Draft } from \"@earendil-works/chord\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { ConversationId, ConversationRecord, Tx } from \"../types.ts\";\nimport type {\n\tAgent,\n\tAgentChange,\n\tAgentState,\n\tCompactionPolicy,\n\tConversationRetryPolicy,\n\tExtension,\n\tHarnessSettings,\n\tProgressPolicy,\n\tPromptSection,\n\tRegistrySnapshot,\n\tSettings,\n\tToolRegistration,\n} from \"./types.ts\";\n\nexport const DEFAULT_RETRY_POLICY: ConversationRetryPolicy = {\n\tenabled: true,\n\tmaxRetries: 3,\n\tbaseDelayMs: 2000,\n\tmaxAgentDelayMs: 60000,\n};\n\nexport const DEFAULT_COMPACTION_POLICY: CompactionPolicy = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n\tbackgroundTokens: 32768,\n};\n\nexport const DEFAULT_PROGRESS_POLICY: ProgressPolicy = {\n\tpartialIntervalMs: 100,\n\toutputIntervalMs: 100,\n};\n\n/** The reserved section key of the agent's `instructions`. */\nexport const INSTRUCTIONS_KEY = \"instructions\";\n\n/** Built-in agent document; rewindable so forks start from the agent at their fork entry. */\nexport const AgentDoc = defineDoc<AgentState>({\n\tkind: \"pi.agent\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"rewindable\",\n\tfork: \"asOf\",\n\tinitial: () => ({}),\n\tcheckpointWhen: () => true,\n});\n\n/** Resolve the host settings: every field over its built-in default, object fields merged. */\nexport function resolveSettings(settings: HarnessSettings | undefined): Settings {\n\tconst extensions = settings?.extensions;\n\treturn {\n\t\t...(extensions === undefined ? {} : { extensions }),\n\t\tstream: { ...settings?.stream },\n\t\tretry: { ...DEFAULT_RETRY_POLICY, ...settings?.retry },\n\t\tcompaction: { ...DEFAULT_COMPACTION_POLICY, ...settings?.compaction },\n\t\tprogress: { ...DEFAULT_PROGRESS_POLICY, ...settings?.progress },\n\t\ttoolExecution: settings?.toolExecution ?? \"parallel\",\n\t\tsteeringMode: settings?.steeringMode ?? \"one-at-a-time\",\n\t\tfollowUpMode: settings?.followUpMode ?? \"one-at-a-time\",\n\t};\n}\n\n/** Apply one change to `pi.agent`: a given field replaces the stored one, `null` clears it, `undefined` changes nothing. */\nexport async function configure(tx: Tx, conversationId: ConversationId, change: AgentChange): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tapplyChange(state, change);\n}\n\n/**\n * `addTools` of a tool round: an array gets each name it lacks appended, `{ remove }` loses the names, and unset tools\n * already offer every tool, so nothing is written.\n */\nexport async function addTools(tx: Tx, conversationId: ConversationId, added: readonly string[]): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tconst tools = state.tools;\n\tif (tools === undefined) return;\n\tif (Array.isArray(tools)) {\n\t\tfor (const name of added) if (!tools.includes(name)) tools.push(name);\n\t} else {\n\t\tconst remove = (tools as { remove: string[] }).remove;\n\t\tif (remove.some((name) => added.includes(name))) {\n\t\t\tstate.tools = { remove: remove.filter((name) => !added.includes(name)) };\n\t\t}\n\t}\n}\n\nfunction applyChange(state: Draft<AgentState>, change: AgentChange): void {\n\tconst set = <K extends keyof AgentState>(key: K, value: AgentState[K] | null | undefined) => {\n\t\tif (value === undefined) return;\n\t\tif (value === null) delete state[key];\n\t\telse state[key] = value as Draft<AgentState>[K];\n\t};\n\tset(\"model\", change.model === undefined || change.model === null ? change.model : { ...change.model });\n\tset(\"thinkingLevel\", change.thinkingLevel);\n\tconst extensions = change.extensions;\n\tset(\n\t\t\"extensions\",\n\t\textensions === undefined || extensions === null\n\t\t\t? extensions\n\t\t\t: isList(extensions)\n\t\t\t\t? names(extensions)\n\t\t\t\t: {\n\t\t\t\t\t\t...(extensions.add === undefined ? {} : { add: names(extensions.add) }),\n\t\t\t\t\t\t...(extensions.remove === undefined ? {} : { remove: names(extensions.remove) }),\n\t\t\t\t\t},\n\t);\n\tconst tools = change.tools;\n\tset(\n\t\t\"tools\",\n\t\ttools === undefined || tools === null ? tools : isList(tools) ? names(tools) : { remove: names(tools.remove) },\n\t);\n\tset(\"instructions\", change.instructions);\n\tset(\"cwd\", change.cwd);\n}\n\nfunction isList<T>(value: readonly T[] | object): value is readonly T[] {\n\treturn Array.isArray(value);\n}\n\nfunction names(items: readonly { readonly name: string }[]): string[] {\n\treturn items.map((item) => item.name);\n}\n\n/**\n * Built-in part of every Harness commit that creates or forks a conversation, for `pi.agent`: a fork keeps its `asOf`\n * copy; a new task-owned conversation copies the stored agent of its owner task's conversation; a new ownerless one\n * starts empty.\n */\nexport async function createAgent(tx: Tx, conversation: ConversationRecord): Promise<void> {\n\tif (conversation.parent !== undefined) return;\n\tconst agent = await tx.doc(AgentDoc, conversation.id);\n\tif (conversation.owner === undefined) return;\n\tconst owner = await tx.doc(AgentDoc, conversation.owner.conversationId);\n\tObject.assign(agent, copyJson(owner) as AgentState);\n}\n\n/** Handlers of the selected extensions' hooks for a task name, in extension order. */\nexport function agentHooks(agent: Agent, taskName: string): object[] {\n\tconst handlers: object[] = [];\n\tfor (const extension of agent.extensions) {\n\t\tfor (const hook of extension.hooks ?? []) if (hook.task === taskName) handlers.push(hook.handlers);\n\t}\n\treturn handlers;\n}\n\n/**\n * Resolve an agent from its stored state (absent: every field unset), a registry snapshot, and resolved settings. A\n * wrapper that throws or renames drops its target and is reported; a wrapper without a target does nothing.\n */\nexport function resolveAgent<Tool extends ToolRegistration>(\n\tstate: Readonly<AgentState> | undefined,\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n\treport: (error: unknown) => void,\n): Agent<Tool> {\n\tconst extensions = selectExtensions(state?.extensions, snapshot, settings);\n\n\tconst composed = new Map<string, Tool>();\n\tfor (const extension of extensions) for (const tool of extension.tools ?? []) composed.set(tool.name, tool);\n\tconst sections = new Map<string, PromptSection<Tool>>();\n\tfor (const extension of extensions) {\n\t\tfor (const section of extension.sections ?? []) sections.set(section.key, section);\n\t}\n\tfor (const extension of extensions) {\n\t\tfor (const wrap of extension.wraps ?? []) {\n\t\t\tif (\"tool\" in wrap)\n\t\t\t\tapplyWrap(\n\t\t\t\t\tcomposed,\n\t\t\t\t\twrap.tool,\n\t\t\t\t\t(tool) => wrap.wrap(tool),\n\t\t\t\t\t(tool) => tool.name,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t\telse\n\t\t\t\tapplyWrap(\n\t\t\t\t\tsections,\n\t\t\t\t\twrap.section,\n\t\t\t\t\t(section) => wrap.wrap(section),\n\t\t\t\t\t(section) => section.key,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t}\n\t}\n\n\tconst filter = state?.tools;\n\tlet tools: Tool[];\n\tif (filter === undefined) tools = [...composed.values()];\n\telse if (Array.isArray(filter)) {\n\t\ttools = [];\n\t\tfor (const name of new Set(filter)) {\n\t\t\tconst tool = composed.get(name);\n\t\t\tif (tool !== undefined) tools.push(tool);\n\t\t}\n\t} else {\n\t\tconst removed = new Set((filter as { remove: string[] }).remove);\n\t\ttools = [...composed.values()].filter((tool) => !removed.has(tool.name));\n\t}\n\n\tconst instructions = state?.instructions;\n\tconst agentSections = [...sections.values()];\n\tif (instructions !== undefined) agentSections.push({ key: INSTRUCTIONS_KEY, render: () => instructions });\n\n\tconst agent: Agent<Tool> = {\n\t\t...(state?.model === undefined ? {} : { model: state.model }),\n\t\tthinkingLevel: state?.thinkingLevel ?? \"off\",\n\t\textensions,\n\t\ttools,\n\t\tsections: agentSections,\n\t\t...(instructions === undefined ? {} : { instructions }),\n\t\t...(state?.cwd === undefined ? {} : { cwd: state.cwd }),\n\t};\n\treturn agent;\n}\n\n/** Selected installed extensions: the stored array, or the default selection edited by `{ add, remove }`. */\nfunction selectExtensions<Tool extends ToolRegistration>(\n\tstored: AgentState[\"extensions\"],\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n): Extension<Tool>[] {\n\tlet selected: string[];\n\tif (Array.isArray(stored)) selected = stored;\n\telse {\n\t\tconst base = settings.extensions?.map((extension) => extension.name) ?? snapshot.installed().map((e) => e.name);\n\t\tconst edit = stored as { add?: string[]; remove?: string[] } | undefined;\n\t\tconst removed = new Set(edit?.remove ?? []);\n\t\tselected = [...base, ...(edit?.add ?? [])].filter((name) => !removed.has(name));\n\t}\n\tconst extensions: Extension<Tool>[] = [];\n\tfor (const name of new Set(selected)) {\n\t\tconst extension = snapshot.extension(name);\n\t\tif (extension !== undefined) extensions.push(extension);\n\t}\n\treturn extensions;\n}\n\nfunction applyWrap<T>(\n\titems: Map<string, T>,\n\ttarget: string,\n\twrap: (item: T) => T,\n\tnameOf: (item: T) => string,\n\treport: (error: unknown) => void,\n): void {\n\tconst item = items.get(target);\n\tif (item === undefined) return;\n\ttry {\n\t\tconst wrapped = wrap(item);\n\t\tif (nameOf(wrapped) !== target) throw new Error(`Wrapper renamed ${target} to ${nameOf(wrapped)}`);\n\t\titems.set(target, wrapped);\n\t} catch (error) {\n\t\titems.delete(target);\n\t\treport(error);\n\t}\n}\n"]}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/harness/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import { copyJson, type Draft } from \"@earendil-works/chord\";\nimport { defineDoc } from \"../documents.ts\";\nimport type { ConversationId, ConversationRecord, Tx } from \"../types.ts\";\nimport type {\n\tAgent,\n\tAgentChange,\n\tAgentState,\n\tCompactionPolicy,\n\tConversationRetryPolicy,\n\tExtension,\n\tHarnessSettings,\n\tProgressPolicy,\n\tPromptSection,\n\tRegistrySnapshot,\n\tSettings,\n\tToolRegistration,\n} from \"./types.ts\";\n\nexport const DEFAULT_RETRY_POLICY: ConversationRetryPolicy = {\n\tenabled: true,\n\tmaxRetries: 3,\n\tbaseDelayMs: 2000,\n\tmaxAgentDelayMs: 60000,\n};\n\nexport const DEFAULT_COMPACTION_POLICY: CompactionPolicy = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n\tbackgroundTokens: 32768,\n};\n\nexport const DEFAULT_PROGRESS_POLICY: ProgressPolicy = {\n\tpartialIntervalMs: 100,\n\toutputIntervalMs: 100,\n};\n\n/** The reserved section key of the agent's `instructions`. */\nexport const INSTRUCTIONS_KEY = \"instructions\";\n\n/** Built-in agent document; rewindable so forks start from the agent at their fork entry. */\nexport const AgentDoc = defineDoc<AgentState>({\n\tkind: \"pi.agent\",\n\tversion: 1,\n\tscope: \"conversation\",\n\thistory: \"rewindable\",\n\tfork: \"asOf\",\n\tinitial: () => ({}),\n\tcheckpointWhen: () => true,\n});\n\n/** Resolve the host settings: every field over its built-in default, object fields merged. */\nexport function resolveSettings(settings: HarnessSettings | undefined): Settings {\n\tconst extensions = settings?.extensions;\n\treturn {\n\t\t...(extensions === undefined ? {} : { extensions }),\n\t\tstream: { ...settings?.stream },\n\t\tretry: { ...DEFAULT_RETRY_POLICY, ...settings?.retry },\n\t\tcompaction: { ...DEFAULT_COMPACTION_POLICY, ...settings?.compaction },\n\t\t// Field by field, so an explicitly undefined interval keeps its default.\n\t\tprogress: {\n\t\t\tpartialIntervalMs: settings?.progress?.partialIntervalMs ?? DEFAULT_PROGRESS_POLICY.partialIntervalMs,\n\t\t\toutputIntervalMs: settings?.progress?.outputIntervalMs ?? DEFAULT_PROGRESS_POLICY.outputIntervalMs,\n\t\t},\n\t\ttoolExecution: settings?.toolExecution ?? \"parallel\",\n\t\tsteeringMode: settings?.steeringMode ?? \"one-at-a-time\",\n\t\tfollowUpMode: settings?.followUpMode ?? \"one-at-a-time\",\n\t};\n}\n\n/** Apply one change to `pi.agent`: a given field replaces the stored one, `null` clears it, `undefined` changes nothing. */\nexport async function configure(tx: Tx, conversationId: ConversationId, change: AgentChange): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tapplyChange(state, change);\n}\n\n/**\n * `addTools` of a tool round: an array gets each name it lacks appended, `{ remove }` loses the names, and unset tools\n * already offer every tool, so nothing is written.\n */\nexport async function addTools(tx: Tx, conversationId: ConversationId, added: readonly string[]): Promise<void> {\n\tconst state = await tx.doc(AgentDoc, conversationId);\n\tconst tools = state.tools;\n\tif (tools === undefined) return;\n\tif (Array.isArray(tools)) {\n\t\tfor (const name of added) if (!tools.includes(name)) tools.push(name);\n\t} else {\n\t\tconst remove = (tools as { remove: string[] }).remove;\n\t\tif (remove.some((name) => added.includes(name))) {\n\t\t\tstate.tools = { remove: remove.filter((name) => !added.includes(name)) };\n\t\t}\n\t}\n}\n\nfunction applyChange(state: Draft<AgentState>, change: AgentChange): void {\n\tconst set = <K extends keyof AgentState>(key: K, value: AgentState[K] | null | undefined) => {\n\t\tif (value === undefined) return;\n\t\tif (value === null) delete state[key];\n\t\telse state[key] = value as Draft<AgentState>[K];\n\t};\n\tset(\"model\", change.model === undefined || change.model === null ? change.model : { ...change.model });\n\tset(\"thinkingLevel\", change.thinkingLevel);\n\tconst extensions = change.extensions;\n\tset(\n\t\t\"extensions\",\n\t\textensions === undefined || extensions === null\n\t\t\t? extensions\n\t\t\t: isList(extensions)\n\t\t\t\t? names(extensions)\n\t\t\t\t: {\n\t\t\t\t\t\t...(extensions.add === undefined ? {} : { add: names(extensions.add) }),\n\t\t\t\t\t\t...(extensions.remove === undefined ? {} : { remove: names(extensions.remove) }),\n\t\t\t\t\t},\n\t);\n\tconst tools = change.tools;\n\tset(\n\t\t\"tools\",\n\t\ttools === undefined || tools === null ? tools : isList(tools) ? names(tools) : { remove: names(tools.remove) },\n\t);\n\tset(\"instructions\", change.instructions);\n\tset(\"cwd\", change.cwd);\n}\n\nfunction isList<T>(value: readonly T[] | object): value is readonly T[] {\n\treturn Array.isArray(value);\n}\n\nfunction names(items: readonly { readonly name: string }[]): string[] {\n\treturn items.map((item) => item.name);\n}\n\n/**\n * Built-in part of every Harness commit that creates or forks a conversation, for `pi.agent`: a fork keeps its `asOf`\n * copy; a new task-owned conversation copies the stored agent of its owner task's conversation; a new ownerless one\n * starts empty.\n */\nexport async function createAgent(tx: Tx, conversation: ConversationRecord): Promise<void> {\n\tif (conversation.parent !== undefined) return;\n\tconst agent = await tx.doc(AgentDoc, conversation.id);\n\tif (conversation.owner === undefined) return;\n\tconst owner = await tx.doc(AgentDoc, conversation.owner.conversationId);\n\tObject.assign(agent, copyJson(owner) as AgentState);\n}\n\n/** Handlers of the selected extensions' hooks for a task name, in extension order. */\nexport function agentHooks(agent: Agent, taskName: string): object[] {\n\tconst handlers: object[] = [];\n\tfor (const extension of agent.extensions) {\n\t\tfor (const hook of extension.hooks ?? []) if (hook.task === taskName) handlers.push(hook.handlers);\n\t}\n\treturn handlers;\n}\n\n/**\n * Resolve an agent from its stored state (absent: every field unset), a registry snapshot, and resolved settings. A\n * wrapper that throws or renames drops its target and is reported; a wrapper without a target does nothing.\n */\nexport function resolveAgent<Tool extends ToolRegistration>(\n\tstate: Readonly<AgentState> | undefined,\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n\treport: (error: unknown) => void,\n): Agent<Tool> {\n\tconst extensions = selectExtensions(state?.extensions, snapshot, settings);\n\n\tconst composed = new Map<string, Tool>();\n\tfor (const extension of extensions) for (const tool of extension.tools ?? []) composed.set(tool.name, tool);\n\tconst sections = new Map<string, PromptSection<Tool>>();\n\tfor (const extension of extensions) {\n\t\tfor (const section of extension.sections ?? []) sections.set(section.key, section);\n\t}\n\tfor (const extension of extensions) {\n\t\tfor (const wrap of extension.wraps ?? []) {\n\t\t\tif (\"tool\" in wrap)\n\t\t\t\tapplyWrap(\n\t\t\t\t\tcomposed,\n\t\t\t\t\twrap.tool,\n\t\t\t\t\t(tool) => wrap.wrap(tool),\n\t\t\t\t\t(tool) => tool.name,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t\telse\n\t\t\t\tapplyWrap(\n\t\t\t\t\tsections,\n\t\t\t\t\twrap.section,\n\t\t\t\t\t(section) => wrap.wrap(section),\n\t\t\t\t\t(section) => section.key,\n\t\t\t\t\treport,\n\t\t\t\t);\n\t\t}\n\t}\n\n\tconst filter = state?.tools;\n\tlet tools: Tool[];\n\tif (filter === undefined) tools = [...composed.values()];\n\telse if (Array.isArray(filter)) {\n\t\ttools = [];\n\t\tfor (const name of new Set(filter)) {\n\t\t\tconst tool = composed.get(name);\n\t\t\tif (tool !== undefined) tools.push(tool);\n\t\t}\n\t} else {\n\t\tconst removed = new Set((filter as { remove: string[] }).remove);\n\t\ttools = [...composed.values()].filter((tool) => !removed.has(tool.name));\n\t}\n\n\tconst instructions = state?.instructions;\n\tconst agentSections = [...sections.values()];\n\tif (instructions !== undefined) agentSections.push({ key: INSTRUCTIONS_KEY, render: () => instructions });\n\n\tconst agent: Agent<Tool> = {\n\t\t...(state?.model === undefined ? {} : { model: state.model }),\n\t\tthinkingLevel: state?.thinkingLevel ?? \"off\",\n\t\textensions,\n\t\ttools,\n\t\tsections: agentSections,\n\t\t...(instructions === undefined ? {} : { instructions }),\n\t\t...(state?.cwd === undefined ? {} : { cwd: state.cwd }),\n\t};\n\treturn agent;\n}\n\n/** Selected installed extensions: the stored array, or the default selection edited by `{ add, remove }`. */\nfunction selectExtensions<Tool extends ToolRegistration>(\n\tstored: AgentState[\"extensions\"],\n\tsnapshot: RegistrySnapshot<Tool>,\n\tsettings: Settings,\n): Extension<Tool>[] {\n\tlet selected: string[];\n\tif (Array.isArray(stored)) selected = stored;\n\telse {\n\t\tconst base = settings.extensions?.map((extension) => extension.name) ?? snapshot.installed().map((e) => e.name);\n\t\tconst edit = stored as { add?: string[]; remove?: string[] } | undefined;\n\t\tconst removed = new Set(edit?.remove ?? []);\n\t\tselected = [...base, ...(edit?.add ?? [])].filter((name) => !removed.has(name));\n\t}\n\tconst extensions: Extension<Tool>[] = [];\n\tfor (const name of new Set(selected)) {\n\t\tconst extension = snapshot.extension(name);\n\t\tif (extension !== undefined) extensions.push(extension);\n\t}\n\treturn extensions;\n}\n\nfunction applyWrap<T>(\n\titems: Map<string, T>,\n\ttarget: string,\n\twrap: (item: T) => T,\n\tnameOf: (item: T) => string,\n\treport: (error: unknown) => void,\n): void {\n\tconst item = items.get(target);\n\tif (item === undefined) return;\n\ttry {\n\t\tconst wrapped = wrap(item);\n\t\tif (nameOf(wrapped) !== target) throw new Error(`Wrapper renamed ${target} to ${nameOf(wrapped)}`);\n\t\titems.set(target, wrapped);\n\t} catch (error) {\n\t\titems.delete(target);\n\t\treport(error);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/harness/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,6CAA6C;AAC7C,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAEtH,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpH,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B,CAAC;AAOF,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,CAU3E;AA0CD,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrE;AAgBD;;;;GAIG;AACH,qBAAa,YAAY;;IAYxB,YAAY,MAAM,EAAE,YAAY,EAE/B;IAED,kEAAkE;IAClE,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAUnE;IAaD,oGAAoG;IACpG,GAAG,IAAI,IAAI,CAEV;IAgCD,oFAAoF;IACpF,QAAQ,IAAI,aAAa,CAoBxB;CACD;AAiCD,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,QAAa,CAAC;AAEpD;;;;GAIG;AACH,qBAAa,QAAQ;;IAWpB,YAAY,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAAE,aAAa,EAAE,MAAM,EAIjG;IAED,yBAAyB;IACzB,IAAI,IAAI,IAAI,CAGX;IAED,wFAAwF;IACxF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;IAED,2GAA2G;IACrG,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAMlD;CAmCD"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/harness/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGvD,6CAA6C;AAC7C,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAEtH,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpH,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B,CAAC;AAQF,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,WAAW,CAU3E;AA0CD,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrE;AAgBD;;;;GAIG;AACH,qBAAa,YAAY;;IAkBxB,YAAY,MAAM,EAAE,YAAY,EAE/B;IAED,kEAAkE;IAClE,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAanE;IAaD,oGAAoG;IACpG,GAAG,IAAI,IAAI,CAEV;IAgCD,oFAAoF;IACpF,QAAQ,IAAI,aAAa,CAoBxB;CACD;AAiCD,4EAA4E;AAC5E,eAAO,MAAM,yBAAyB,QAAa,CAAC;AAEpD;;;;GAIG;AACH,qBAAa,QAAQ;;IAWpB,YAAY,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EAAE,aAAa,EAAE,MAAM,EAIjG;IAED,yBAAyB;IACzB,IAAI,IAAI,IAAI,CAGX;IAED,wFAAwF;IACxF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;IAED,2GAA2G;IACrG,IAAI,IAAI,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAMlD;CAmCD"}
@@ -2,7 +2,8 @@ import { utf8ByteLength } from "../truncate.js";
2
2
  const NEWLINE = 0x0a;
3
3
  const INVALID_OUTPUT = /[\x00-\x08\x0b-\x1f\ufff9-\ufffb]/g;
4
4
  const encoder = new TextEncoder();
5
- const decoder = new TextDecoder();
5
+ /** Slices decode exactly: a U+FEFF at a slice's start is text, not a byte-order mark. */
6
+ const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
6
7
  /** Remove control characters that break display and transcripts; tabs and newlines stay. */
7
8
  export function sanitizeOutput(text) {
8
9
  return text.replace(INVALID_OUTPUT, "");
@@ -92,7 +93,13 @@ function lineCount(bytes) {
92
93
  */
93
94
  export class OutputBuffer {
94
95
  #limits;
95
- #decoder = new TextDecoder();
96
+ /**
97
+ * Decoder of byte chunks. A string chunk or a skip ends an incomplete character of earlier bytes (it becomes U+FFFD);
98
+ * the next byte chunk then starts a new character. As in `StreamDecoder`, only a byte-order mark at the very start of
99
+ * the output is dropped, never a U+FEFF later in it.
100
+ */
101
+ #decoder = new TextDecoder("utf-8", { ignoreBOM: true });
102
+ #started = false;
96
103
  /** Stored chunks: for head the start of the stream, for tail a suffix that still contains the next window. */
97
104
  #chunks = [];
98
105
  #storedBytes = 0;
@@ -116,7 +123,12 @@ export class OutputBuffer {
116
123
  push(chunk, skipped) {
117
124
  // Bytes of an incomplete character from an earlier byte chunk come first.
118
125
  const pending = typeof chunk === "string" || skipped !== undefined ? this.#decoder.decode() : "";
119
- const text = typeof chunk === "string" ? chunk : this.#decoder.decode(chunk, { stream: true });
126
+ let text = typeof chunk === "string" ? chunk : this.#decoder.decode(chunk, { stream: true });
127
+ const first = !this.#started && pending === "" && skipped === undefined;
128
+ if (pending !== "" || text !== "" || skipped !== undefined)
129
+ this.#started = true;
130
+ if (first && typeof chunk !== "string" && text.startsWith("\ufeff"))
131
+ text = text.slice(1);
120
132
  if (skipped === undefined)
121
133
  return this.#accept(pending + text);
122
134
  if (this.#limits.retain !== "tail")