@abelxiaoxing/cadence 2.0.1 → 2.0.3

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.
@@ -4,37 +4,11 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/service/host.ts
6
6
  import { lstatSync as lstatSync10, opendirSync as opendirSync3 } from "node:fs";
7
- import { join as join16 } from "node:path";
7
+ import { join as join17 } from "node:path";
8
8
 
9
- // src/history/files.ts
10
- import { createHash as createHash5, randomBytes, randomUUID as randomUUID3 } from "node:crypto";
11
- import {
12
- closeSync as closeSync4,
13
- constants as constants4,
14
- fstatSync as fstatSync4,
15
- fsyncSync as fsyncSync3,
16
- lstatSync as lstatSync4,
17
- opendirSync,
18
- openSync as openSync4,
19
- statfsSync
20
- } from "node:fs";
21
- import { open } from "node:fs/promises";
22
- import { dirname as dirname4, join as join3 } from "node:path";
23
-
24
- // src/service/private-files.ts
25
- import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
26
- import {
27
- closeSync as closeSync2,
28
- constants as constants2,
29
- fstatSync as fstatSync2,
30
- fsyncSync as fsyncSync2,
31
- lstatSync as lstatSync2,
32
- openSync as openSync2,
33
- renameSync as renameSync2,
34
- unlinkSync as unlinkSync2,
35
- writeFileSync as writeFileSync2
36
- } from "node:fs";
37
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
9
+ // src/agent-registry.ts
10
+ import { createHash as createHash2 } from "node:crypto";
11
+ import { fileURLToPath } from "node:url";
38
12
 
39
13
  // src/task-store.ts
40
14
  import { spawnSync } from "node:child_process";
@@ -170,11 +144,8 @@ function safeDirectory(path, create = false, inspectExisting) {
170
144
  at = join(at, part);
171
145
  let existing;
172
146
  try {
173
- const st = lstatSync(at);
174
- if (!st.isDirectory())
175
- throw new Error("Unsafe directory");
176
147
  const checked = lstatSync(at);
177
- if (checked.isSymbolicLink() || inspectExisting && !checked.isDirectory())
148
+ if (!checked.isDirectory() || checked.isSymbolicLink())
178
149
  throw new Error("Unsafe directory");
179
150
  existing = checked;
180
151
  } catch (error) {
@@ -188,7 +159,8 @@ function safeDirectory(path, create = false, inspectExisting) {
188
159
  if (!object(mkdirError) || mkdirError.code !== "EEXIST")
189
160
  throw mkdirError;
190
161
  }
191
- if (!lstatSync(at).isDirectory() || lstatSync(at).isSymbolicLink())
162
+ const created = lstatSync(at);
163
+ if (!created.isDirectory() || created.isSymbolicLink())
192
164
  throw new Error("Unsafe directory");
193
165
  }
194
166
  if (existing)
@@ -196,13 +168,29 @@ function safeDirectory(path, create = false, inspectExisting) {
196
168
  }
197
169
  return true;
198
170
  }
199
- function readBounded(path, max) {
171
+ var transientWindows = new Set(["EPERM", "EACCES", "EBUSY"]);
172
+ function retryWindowsFileOperation(operation, recheck) {
173
+ if (process.platform !== "win32")
174
+ return operation();
175
+ const deadline = performance.now() + 2000;
176
+ for (let delay = 2;; delay = Math.min(delay * 2, 50)) {
177
+ try {
178
+ return operation();
179
+ } catch (error) {
180
+ if (!object(error) || !transientWindows.has(String(error.code)) || performance.now() >= deadline)
181
+ throw error;
182
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
183
+ recheck?.();
184
+ }
185
+ }
186
+ }
187
+ function readBounded(path, max, packaged = false) {
200
188
  if (!safeDirectory(dirname(path)))
201
189
  return;
202
190
  let fd;
203
191
  try {
204
192
  const st = lstatSync(path);
205
- if (!st.isFile() || st.isSymbolicLink() || st.nlink !== 1 || st.size > max)
193
+ if (!st.isFile() || st.isSymbolicLink() || !packaged && st.nlink !== 1 || st.size > max)
206
194
  throw new Error("Unsafe or oversized file");
207
195
  fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
208
196
  } catch (error) {
@@ -212,7 +200,7 @@ function readBounded(path, max) {
212
200
  }
213
201
  try {
214
202
  const st = fstatSync(fd);
215
- if (!st.isFile() || st.nlink !== 1 || st.size > max)
203
+ if (!st.isFile() || !packaged && st.nlink !== 1 || st.size > max)
216
204
  throw new Error("Unsafe or oversized file");
217
205
  const buffer = Buffer.alloc(max + 1);
218
206
  let size = 0;
@@ -293,7 +281,13 @@ function canonicalAmbientRoot(path) {
293
281
  return join(canonicalAmbientRoot(parent), full.slice(parent.length));
294
282
  }
295
283
  }
296
-
284
+ function privateRootFor(canonicalRoot, stateHome) {
285
+ return join(canonicalAmbientRoot(stateHome), "cadence-assistant-v1", createHash("sha256").update(canonicalRoot).digest("hex"));
286
+ }
287
+ function contains(root, path) {
288
+ const within = relative(root, resolve(path));
289
+ return !within || !within.startsWith(`..${sep}`) && within !== ".." && !isAbsolute(within);
290
+ }
297
291
  class TaskStore {
298
292
  root;
299
293
  tasks;
@@ -301,9 +295,8 @@ class TaskStore {
301
295
  constructor(root, stateHome = defaultStateHome()) {
302
296
  this.root = realpathSync(root);
303
297
  this.tasks = join(this.root, ".cadence", "assistant", "tasks");
304
- this.privateRoot = join(canonicalAmbientRoot(stateHome), "cadence-assistant-v1", createHash("sha256").update(this.root).digest("hex"));
305
- const within = relative(this.root, resolve(this.privateRoot));
306
- if (!within || !within.startsWith(`..${sep}`) && within !== ".." && !isAbsolute(within))
298
+ this.privateRoot = privateRootFor(this.root, stateHome);
299
+ if (contains(this.root, this.privateRoot))
307
300
  throw new Error("Private state home must be outside the working copy");
308
301
  }
309
302
  get lockPath() {
@@ -355,7 +348,7 @@ class TaskStore {
355
348
  };
356
349
  let fd;
357
350
  try {
358
- fd = openSync(this.lockPath, "wx", 384);
351
+ fd = retryWindowsFileOperation(() => openSync(this.lockPath, "wx", 384));
359
352
  } catch {
360
353
  throw new Error("Checkout busy or unresolved lock; inspect and explicitly recover only a dead owner");
361
354
  }
@@ -375,7 +368,7 @@ class TaskStore {
375
368
  if (lock.pid !== process.pid || lock.host !== hostname())
376
369
  throw new Error("Only the owning process may release a lock");
377
370
  this.assertOwner(lock);
378
- unlinkSync(this.lockPath);
371
+ retryWindowsFileOperation(() => unlinkSync(this.lockPath), () => this.assertOwner(lock));
379
372
  }
380
373
  recover(expected, acknowledgeEscapedProcesses) {
381
374
  if (!acknowledgeEscapedProcesses || expected.host !== hostname())
@@ -455,7 +448,12 @@ class TaskStore {
455
448
  const latest = readBounded(join(dir, "task.json"), MAX_RECORD_BYTES);
456
449
  if ((latest ? contentIdentity(latest) : undefined) !== expectedIdentity)
457
450
  throw new Error("Task content changed; replacement preserved without overwrite");
458
- renameSync(temporary, join(dir, "task.json"));
451
+ retryWindowsFileOperation(() => renameSync(temporary, join(dir, "task.json")), () => {
452
+ this.assertOwner(lock);
453
+ const now = readBounded(join(dir, "task.json"), MAX_RECORD_BYTES);
454
+ if ((now ? contentIdentity(now) : undefined) !== expectedIdentity)
455
+ throw new Error("Task content changed; replacement preserved without overwrite");
456
+ });
459
457
  return next;
460
458
  } finally {
461
459
  if (!held)
@@ -495,6 +493,61 @@ class TaskStore {
495
493
  }
496
494
  var TASK_CONFIG_SCHEMA = "cadence-assistant-checks/1";
497
495
 
496
+ // src/agent-registry.ts
497
+ var AGENT_FILES = [
498
+ "design-explorer.md",
499
+ "implementation-worker.md",
500
+ "diagnosis-worker.md"
501
+ ];
502
+ function loadAgentDefinitions() {
503
+ return AGENT_FILES.map((fileName) => {
504
+ const path = fileURLToPath(new URL(`../agents/${fileName}`, import.meta.url));
505
+ const data = readBounded(path, 32768, true);
506
+ if (!data)
507
+ throw new Error("Missing package Agent");
508
+ return {
509
+ role: fileName.replace(/\.md$/, ""),
510
+ fileName,
511
+ path,
512
+ sha256: createHash2("sha256").update(data).digest("hex"),
513
+ bytes: data.length,
514
+ content: data.toString("utf8")
515
+ };
516
+ });
517
+ }
518
+
519
+ // src/history/files.ts
520
+ import { createHash as createHash6, randomBytes, randomUUID as randomUUID3 } from "node:crypto";
521
+ import {
522
+ closeSync as closeSync4,
523
+ constants as constants4,
524
+ fstatSync as fstatSync4,
525
+ fsyncSync as fsyncSync3,
526
+ lstatSync as lstatSync4,
527
+ opendirSync,
528
+ openSync as openSync4,
529
+ statfsSync
530
+ } from "node:fs";
531
+ import { open } from "node:fs/promises";
532
+ import { dirname as dirname4, join as join3 } from "node:path";
533
+
534
+ // src/service/private-files.ts
535
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
536
+ import {
537
+ closeSync as closeSync2,
538
+ constants as constants2,
539
+ fstatSync as fstatSync2,
540
+ fsyncSync as fsyncSync2,
541
+ lstatSync as lstatSync2,
542
+ mkdirSync as mkdirSync2,
543
+ openSync as openSync2,
544
+ readSync as readSync2,
545
+ renameSync as renameSync2,
546
+ unlinkSync as unlinkSync2,
547
+ writeFileSync as writeFileSync2
548
+ } from "node:fs";
549
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
550
+
498
551
  // src/worker-public.mjs
499
552
  var SERVICE_PROTOCOL = 1;
500
553
  var WORKER_PROTOCOL = 2;
@@ -2079,10 +2132,10 @@ function refused() {
2079
2132
  throw new ContractError("corrupt-selected");
2080
2133
  }
2081
2134
  function hash(bytes) {
2082
- return createHash2("sha256").update(bytes).digest("hex");
2135
+ return createHash3("sha256").update(bytes).digest("hex");
2083
2136
  }
2084
- function privateMode(st) {
2085
- if (process.platform !== "win32" && ((st.mode & 63) !== 0 || process.getuid && st.uid !== process.getuid()))
2137
+ function privateMode(st, ownerOnly = true) {
2138
+ if (process.platform !== "win32" && (ownerOnly && (st.mode & 63) !== 0 || process.getuid && st.uid !== process.getuid()))
2086
2139
  refused();
2087
2140
  }
2088
2141
  function same(a, b) {
@@ -2091,38 +2144,110 @@ function same(a, b) {
2091
2144
  function missing(error) {
2092
2145
  return error instanceof Error && "code" in error && error.code === "ENOENT";
2093
2146
  }
2147
+ var job = 0;
2148
+ var advancing = false;
2149
+ function currentJob() {
2150
+ if (!advancing) {
2151
+ advancing = true;
2152
+ queueMicrotask(() => {
2153
+ job++;
2154
+ advancing = false;
2155
+ });
2156
+ }
2157
+ return job;
2158
+ }
2159
+
2160
+ class DirectoryWalks {
2161
+ job = currentJob();
2162
+ walked = new Map;
2163
+ open() {
2164
+ if (this.job === job)
2165
+ return true;
2166
+ this.walked.clear();
2167
+ return false;
2168
+ }
2169
+ remember(path, st) {
2170
+ if (this.open())
2171
+ this.walked.set(path, { dev: st.dev, ino: st.ino });
2172
+ }
2173
+ reuse(path, ownerOnly = true) {
2174
+ const walked = this.open() ? this.walked.get(path) : undefined;
2175
+ if (!walked)
2176
+ return false;
2177
+ let st;
2178
+ try {
2179
+ st = lstatSync2(path);
2180
+ } catch (error) {
2181
+ if (!missing(error))
2182
+ throw error;
2183
+ this.walked.delete(path);
2184
+ return false;
2185
+ }
2186
+ if (!st.isDirectory() || st.isSymbolicLink() || st.dev !== walked.dev || st.ino !== walked.ino) {
2187
+ this.walked.delete(path);
2188
+ return false;
2189
+ }
2190
+ privateMode(st, ownerOnly);
2191
+ return true;
2192
+ }
2193
+ }
2094
2194
 
2095
2195
  class PrivateFiles {
2096
2196
  namespace;
2097
- constructor(namespace) {
2197
+ ownerOnly;
2198
+ constructor(namespace, ownerOnly = true) {
2098
2199
  this.namespace = namespace;
2200
+ this.ownerOnly = ownerOnly;
2099
2201
  }
2100
- directory(path, create = false) {
2202
+ directory(path, create = false, walks) {
2101
2203
  const tail = relative2(this.namespace, path);
2102
2204
  if (isAbsolute2(tail) || tail === ".." || tail.startsWith(`..${sep2}`))
2103
2205
  refused();
2104
2206
  if (!create) {
2207
+ const full = resolve2(path);
2208
+ if (walks?.reuse(full, this.ownerOnly))
2209
+ return true;
2105
2210
  const namespace = resolve2(this.namespace);
2106
- let inside = false;
2107
- return safeDirectory(path, false, (at2, st) => {
2211
+ let inside = false, last;
2212
+ const found = safeDirectory(path, false, (at2, st) => {
2108
2213
  if (at2 === namespace)
2109
2214
  inside = true;
2110
2215
  if (inside)
2111
- privateMode(st);
2216
+ privateMode(st, this.ownerOnly);
2217
+ last = st;
2112
2218
  });
2219
+ if (found && last)
2220
+ walks?.remember(full, last);
2221
+ return found;
2113
2222
  }
2223
+ if (!safeDirectory(this.namespace, true))
2224
+ return false;
2225
+ privateMode(lstatSync2(this.namespace), this.ownerOnly);
2114
2226
  let at = this.namespace;
2115
- for (const part of ["", ...tail.split(sep2).filter(Boolean)]) {
2116
- if (part)
2117
- at = join2(at, part);
2118
- if (!safeDirectory(at, create))
2119
- return false;
2120
- privateMode(lstatSync2(at));
2227
+ for (const part of tail.split(sep2).filter(Boolean)) {
2228
+ at = join2(at, part);
2229
+ let st;
2230
+ try {
2231
+ st = lstatSync2(at);
2232
+ } catch (error) {
2233
+ if (!missing(error))
2234
+ throw error;
2235
+ try {
2236
+ mkdirSync2(at, { mode: 448 });
2237
+ } catch (mkdirError) {
2238
+ if (!(mkdirError instanceof Error && ("code" in mkdirError) && mkdirError.code === "EEXIST"))
2239
+ throw mkdirError;
2240
+ }
2241
+ st = lstatSync2(at);
2242
+ }
2243
+ if (!st.isDirectory() || st.isSymbolicLink())
2244
+ throw new Error("Unsafe directory");
2245
+ privateMode(st, this.ownerOnly);
2121
2246
  }
2122
2247
  return true;
2123
2248
  }
2124
- read(path, maximum = METADATA_BYTES) {
2125
- if (!this.directory(dirname2(path)))
2249
+ read(path, maximum = METADATA_BYTES, walks) {
2250
+ if (!this.directory(dirname2(path), false, walks))
2126
2251
  return;
2127
2252
  let before;
2128
2253
  try {
@@ -2134,22 +2259,30 @@ class PrivateFiles {
2134
2259
  }
2135
2260
  if (!before.isFile() || before.nlink !== 1 || before.size > maximum)
2136
2261
  refused();
2137
- privateMode(before);
2262
+ privateMode(before, this.ownerOnly);
2138
2263
  const fd = openSync2(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0));
2139
2264
  try {
2140
2265
  const opened = fstatSync2(fd);
2141
2266
  if (!same(before, opened))
2142
2267
  refused();
2143
- const bytes = readBounded(path, opened.size);
2144
- if (!bytes || bytes.length !== opened.size || !same(opened, fstatSync2(fd)) || !same(opened, lstatSync2(path)))
2268
+ const buffer = Buffer.alloc(opened.size + 1);
2269
+ let size = 0;
2270
+ while (size < buffer.length) {
2271
+ const n = readSync2(fd, buffer, size, buffer.length - size, null);
2272
+ if (!n)
2273
+ break;
2274
+ size += n;
2275
+ }
2276
+ const bytes = buffer.subarray(0, size);
2277
+ if (size !== opened.size || !same(opened, fstatSync2(fd)) || !same(opened, lstatSync2(path)))
2145
2278
  refused();
2146
2279
  return { bytes, identity: hash(bytes), stamp: stamp(opened) };
2147
2280
  } finally {
2148
2281
  closeSync2(fd);
2149
2282
  }
2150
2283
  }
2151
- assert(path, expected, maximum = METADATA_BYTES) {
2152
- const current = this.read(path, maximum);
2284
+ assert(path, expected, maximum = METADATA_BYTES, walks) {
2285
+ const current = this.read(path, maximum, walks);
2153
2286
  if (current?.identity !== expected?.identity || current?.stamp !== expected?.stamp)
2154
2287
  throw new ContractError("conflict");
2155
2288
  }
@@ -2157,7 +2290,7 @@ class PrivateFiles {
2157
2290
  if (Buffer.byteLength(bytes) > maximum)
2158
2291
  throw new ContractError("oversize");
2159
2292
  this.directory(dirname2(path), true);
2160
- const fd = openSync2(path, "wx", 384);
2293
+ const fd = retryWindowsFileOperation(() => openSync2(path, "wx", 384));
2161
2294
  try {
2162
2295
  writeFileSync2(fd, bytes);
2163
2296
  fsyncSync2(fd);
@@ -2175,7 +2308,7 @@ class PrivateFiles {
2175
2308
  const temporary = join2(dirname2(path), `pending-${randomUUID2()}.json`);
2176
2309
  this.create(temporary, bytes, maximum);
2177
2310
  this.assert(path, expected, maximum);
2178
- renameSync2(temporary, path);
2311
+ retryWindowsFileOperation(() => renameSync2(temporary, path), () => this.assert(path, expected, maximum));
2179
2312
  this.syncDirectory(dirname2(path));
2180
2313
  const result = this.read(path, maximum);
2181
2314
  if (!result || result.identity !== hash(bytes))
@@ -2184,7 +2317,7 @@ class PrivateFiles {
2184
2317
  }
2185
2318
  remove(path, expected, maximum = METADATA_BYTES) {
2186
2319
  this.assert(path, expected, maximum);
2187
- unlinkSync2(path);
2320
+ retryWindowsFileOperation(() => unlinkSync2(path), () => this.assert(path, expected, maximum));
2188
2321
  this.syncDirectory(dirname2(path));
2189
2322
  }
2190
2323
  syncDirectory(path) {
@@ -3915,22 +4048,22 @@ function decodeRunAdmission(value) {
3915
4048
  }
3916
4049
 
3917
4050
  // src/history/read-io.ts
3918
- import { createHash as createHash3 } from "node:crypto";
4051
+ import { createHash as createHash4 } from "node:crypto";
3919
4052
  import {
3920
4053
  closeSync as closeSync3,
3921
4054
  constants as constants3,
3922
4055
  fstatSync as fstatSync3,
3923
4056
  lstatSync as lstatSync3,
3924
4057
  openSync as openSync3,
3925
- readSync as readSync2
4058
+ readSync as readSync3
3926
4059
  } from "node:fs";
3927
4060
  import { dirname as dirname3 } from "node:path";
3928
4061
  function bad() {
3929
4062
  throw new ContractError("corrupt-selected");
3930
4063
  }
3931
4064
  var stamp2 = (s) => [s.dev, s.ino, s.size, s.mode, s.uid, s.nlink, s.ctimeMs, s.mtimeMs].join(":");
3932
- function selected(files, path, read) {
3933
- if (!files.files.directory(dirname3(path)))
4065
+ function selected(files, path, read, walks) {
4066
+ if (!files.files.directory(dirname3(path), false, walks))
3934
4067
  bad();
3935
4068
  const before = lstatSync3(path);
3936
4069
  if (!before.isFile() || before.nlink !== 1 || process.platform !== "win32" && (before.mode & 63 || process.getuid && before.uid !== process.getuid()))
@@ -3967,7 +4100,7 @@ function reproveAppend(files, path, expected, length, sha256, budget) {
3967
4100
  if (b.slice(0, 2).join(":") !== a.slice(0, 2).join(":") || b.slice(3, 6).join(":") !== a.slice(3, 6).join(":") || Number(b[2]) <= Number(a[2]))
3968
4101
  bad();
3969
4102
  const bytes = readRange(files, path, 0, length, budget);
3970
- if (createHash3("sha256").update(bytes).digest("hex") !== sha256)
4103
+ if (createHash4("sha256").update(bytes).digest("hex") !== sha256)
3971
4104
  bad();
3972
4105
  assertIncarnation(files, path, current.stamp);
3973
4106
  return current.stamp;
@@ -3989,17 +4122,17 @@ function readRange(files, path, offset, length, budget) {
3989
4122
  let n = 0;
3990
4123
  while (n < length) {
3991
4124
  budget.reserve(0);
3992
- const got = readSync2(fd, out, n, length - n, offset + n);
4125
+ const got = readSync3(fd, out, n, length - n, offset + n);
3993
4126
  if (!got)
3994
4127
  bad();
3995
4128
  n += got;
3996
4129
  }
3997
4130
  return out;
3998
- });
4131
+ }, budget.walks);
3999
4132
  }
4000
4133
 
4001
4134
  // src/history/types.ts
4002
- import { createHash as createHash4 } from "node:crypto";
4135
+ import { createHash as createHash5 } from "node:crypto";
4003
4136
  var RECORD_BYTES = LIMITS.controlBytes + 64 * 1024;
4004
4137
  var LEGACY_FORMAT = "cadence-history-store/2";
4005
4138
  var FORMAT = "cadence-history-store/3";
@@ -4365,7 +4498,7 @@ function batchArtifacts(rows) {
4365
4498
  }
4366
4499
  var decodedLines = new Map;
4367
4500
  var decodedBytes = 0;
4368
- var lineKey = (text2) => createHash4("sha256").update(text2).digest("hex");
4501
+ var lineKey = (text2) => createHash5("sha256").update(text2).digest("hex");
4369
4502
  function remember(text2, value) {
4370
4503
  const key = lineKey(text2);
4371
4504
  if (decodedLines.has(key))
@@ -4428,13 +4561,45 @@ function decodeJournalText(line) {
4428
4561
 
4429
4562
  // src/history/files.ts
4430
4563
  class HistoryReadBudget {
4564
+ clock;
4431
4565
  started = performance.now();
4432
4566
  bytes = 0;
4567
+ walks = new DirectoryWalks;
4568
+ constructor(clock = true) {
4569
+ this.clock = clock;
4570
+ }
4571
+ static authority() {
4572
+ return new HistoryReadBudget(false);
4573
+ }
4433
4574
  reserve(bytes) {
4434
- if (!Number.isSafeInteger(bytes) || bytes < 0 || this.bytes + bytes > LIMITS.searchBytes || performance.now() - this.started >= LIMITS.searchMs)
4575
+ if (!Number.isSafeInteger(bytes) || bytes < 0 || this.bytes + bytes > LIMITS.searchBytes || this.elapsed)
4435
4576
  throw new ContractError("receipt-inconclusive");
4436
4577
  this.bytes += bytes;
4437
4578
  }
4579
+ get elapsed() {
4580
+ return this.clock && performance.now() - this.started >= LIMITS.searchMs;
4581
+ }
4582
+ }
4583
+
4584
+ class StalledQuantum extends ContractError {
4585
+ constructor() {
4586
+ super("receipt-inconclusive");
4587
+ }
4588
+ }
4589
+ function noProgress(budget) {
4590
+ throw budget.elapsed ? new StalledQuantum : new ContractError("receipt-inconclusive");
4591
+ }
4592
+ var STALL_RETRIES = 2;
4593
+ async function retryStalled(read) {
4594
+ for (let retry = 0;; retry++) {
4595
+ try {
4596
+ return await read();
4597
+ } catch (error) {
4598
+ if (!(error instanceof StalledQuantum) || retry >= STALL_RETRIES)
4599
+ throw error;
4600
+ await new Promise((resolve3) => setImmediate(resolve3));
4601
+ }
4602
+ }
4438
4603
  }
4439
4604
  function corrupt() {
4440
4605
  throw new ContractError("corrupt-selected");
@@ -4596,12 +4761,12 @@ class HistoryFiles {
4596
4761
  try {
4597
4762
  size = lstatSync4(path).size;
4598
4763
  } catch {
4599
- return this.files.read(path, 0);
4764
+ return this.files.read(path, 0, budget.walks);
4600
4765
  }
4601
4766
  if (size > maximum)
4602
4767
  corrupt();
4603
4768
  budget.reserve(size);
4604
- return this.files.read(path, size);
4769
+ return this.files.read(path, size, budget.walks);
4605
4770
  }
4606
4771
  readCommand(commandId, budget) {
4607
4772
  decodeId(commandId);
@@ -4754,7 +4919,7 @@ class HistoryFiles {
4754
4919
  state.bytes = file.bytes.length;
4755
4920
  state.identity = file.identity;
4756
4921
  state.records = 0;
4757
- let at = 0, pending = [], pendingBytes = 0, checksum = createHash5("sha256");
4922
+ let at = 0, pending = [], pendingBytes = 0, checksum = createHash6("sha256");
4758
4923
  while (at < file.bytes.length) {
4759
4924
  budget?.reserve(0);
4760
4925
  const end = file.bytes.indexOf(10, at);
@@ -4822,7 +4987,7 @@ class HistoryFiles {
4822
4987
  };
4823
4988
  pending = [];
4824
4989
  pendingBytes = 0;
4825
- checksum = createHash5("sha256");
4990
+ checksum = createHash6("sha256");
4826
4991
  }
4827
4992
  if (pending.length)
4828
4993
  state.tail = true;
@@ -5014,7 +5179,7 @@ class HistoryFiles {
5014
5179
  lease.assert();
5015
5180
  this.assertWritable();
5016
5181
  const after = this.files.read(path, LIMITS.segmentBytes);
5017
- if (!after || after.identity !== createHash5("sha256").update(before.bytes).update(body).update(trailer).digest("hex"))
5182
+ if (!after || after.identity !== createHash6("sha256").update(before.bytes).update(body).update(trailer).digest("hex"))
5018
5183
  corrupt();
5019
5184
  return {
5020
5185
  committedSeq: marker.last,
@@ -5283,7 +5448,7 @@ class HistoryLive {
5283
5448
  }
5284
5449
 
5285
5450
  // src/history/navigation.ts
5286
- import { createHash as createHash6 } from "node:crypto";
5451
+ import { createHash as createHash7 } from "node:crypto";
5287
5452
  import { lstatSync as lstatSync6 } from "node:fs";
5288
5453
  import { dirname as dirname5, join as join5 } from "node:path";
5289
5454
 
@@ -5465,7 +5630,7 @@ class IndexTree {
5465
5630
 
5466
5631
  // src/history/run-state.ts
5467
5632
  var uncertainSettlement = (completion) => completion.native.closure === "unconfirmed" || completion.recording.status === "pending" && completion.recording.selected !== null || completion.recording.status === "uncertain";
5468
- function readRunState(files, runId, snapshotSeq, budget = new HistoryReadBudget) {
5633
+ function readRunState(files, runId, snapshotSeq, budget = HistoryReadBudget.authority()) {
5469
5634
  const state = new RunState(runId);
5470
5635
  if (snapshotSeq === "0")
5471
5636
  return state;
@@ -5543,6 +5708,27 @@ class TaskPublicationLineage {
5543
5708
  };
5544
5709
  }
5545
5710
  }
5711
+ get operationId() {
5712
+ return this.operation?.operationId ?? null;
5713
+ }
5714
+ progress() {
5715
+ return {
5716
+ operationId: this.operation?.operationId ?? null,
5717
+ state: structuredClone(this.state),
5718
+ endpoint: this.endpoint && { ...this.endpoint },
5719
+ graphIdentity: this.graphIdentity
5720
+ };
5721
+ }
5722
+ static resumed(operation, progress) {
5723
+ if ((operation?.operationId ?? null) !== progress.operationId)
5724
+ throw new ContractError("invalid");
5725
+ const lineage = new TaskPublicationLineage;
5726
+ lineage.operation = operation;
5727
+ lineage.state = structuredClone(progress.state);
5728
+ lineage.endpoint = progress.endpoint && { ...progress.endpoint };
5729
+ lineage.graphIdentity = progress.graphIdentity;
5730
+ return lineage;
5731
+ }
5546
5732
  current() {
5547
5733
  requireFact(this.operation && this.state.status === "published" && this.endpoint?.identity);
5548
5734
  return {
@@ -5596,6 +5782,21 @@ class OperationState {
5596
5782
  }, fact, seq);
5597
5783
  }
5598
5784
  }
5785
+ progress() {
5786
+ return {
5787
+ reviewed: this.reviewed,
5788
+ publication: structuredClone(this.publication)
5789
+ };
5790
+ }
5791
+ static resumed(operationId, command, progress) {
5792
+ if (command.intent.operationId !== operationId)
5793
+ throw new ContractError("invalid");
5794
+ const state = new OperationState(operationId);
5795
+ state.command = command;
5796
+ state.reviewed = progress.reviewed;
5797
+ state.publication = structuredClone(progress.publication);
5798
+ return state;
5799
+ }
5599
5800
  publicationExpected() {
5600
5801
  const identity = this.command?.intent.identity;
5601
5802
  requireFact(identity?.kind === "action" && (!this.publication.intent || this.publication.status === "published"));
@@ -5621,7 +5822,7 @@ class OperationState {
5621
5822
  }
5622
5823
  function readOperationState(files, operationId) {
5623
5824
  const state = new OperationState(operationId);
5624
- for (const batch of files.scan(emptyScan(), false, new HistoryReadBudget))
5825
+ for (const batch of files.scan(emptyScan(), false, HistoryReadBudget.authority()))
5625
5826
  for (const row of batch.rows)
5626
5827
  state.apply(row, batch.marker.last);
5627
5828
  return state;
@@ -6247,7 +6448,7 @@ class HistoryNavigation {
6247
6448
  return structuredClone(source.value);
6248
6449
  }
6249
6450
  try {
6250
- if (!this.files.files.directory(dirname5(path)))
6451
+ if (!this.files.files.directory(dirname5(path), false, budget.walks))
6251
6452
  return source ? structuredClone(source.value) : undefined;
6252
6453
  const size = lstatSync6(path).size;
6253
6454
  if (size > RECORD_BYTES)
@@ -6474,7 +6675,7 @@ class HistoryNavigation {
6474
6675
  continue;
6475
6676
  }
6476
6677
  let pending = Buffer.alloc(0), at = offset, bodyBytes = 0;
6477
- const checksum = createHash6("sha256"), rows = [];
6678
+ const checksum = createHash7("sha256"), rows = [];
6478
6679
  while (at < size) {
6479
6680
  const chunk = this.block(path, at, size, budget);
6480
6681
  at += chunk.length;
@@ -6758,16 +6959,41 @@ class HistoryNavigation {
6758
6959
  throw new ContractError("receipt-inconclusive");
6759
6960
  let head = expected;
6760
6961
  while (head) {
6761
- const value = this.index(roster ? "roster-links" : "entity-links", `${id}/${head.seq}`, budget);
6762
- if (!value)
6763
- throw new ContractError("receipt-inconclusive");
6764
- const o = closed(value, ["pointer", "previous", "previousHash", "hash"]), p = pointer(o.pointer);
6765
- const previous = o.previous === null ? null : decodeSequence(o.previous), previousHash = o.previousHash === null ? null : typeof o.previousHash === "string" ? o.previousHash : undefined;
6766
- if (previousHash === undefined || previous === null !== (previousHash === null) || p.seq !== head.seq || o.hash !== head.hash || chainHash(p.seq, previous, previousHash) !== head.hash || previous !== null && BigInt(previous) >= BigInt(p.seq))
6767
- throw new ContractError("receipt-inconclusive");
6768
- yield this.batch(p, budget);
6769
- head = previous !== null && previousHash !== null ? { seq: previous, hash: previousHash } : null;
6962
+ const link = this.link(roster ? "roster-links" : "entity-links", id, head, budget);
6963
+ yield this.batch(link.pointer, budget);
6964
+ head = link.previous;
6965
+ }
6966
+ }
6967
+ link(group, id, head, budget) {
6968
+ const value = this.index(group, `${id}/${head.seq}`, budget);
6969
+ if (!value)
6970
+ throw new ContractError("receipt-inconclusive");
6971
+ const o = closed(value, ["pointer", "previous", "previousHash", "hash"]), p = pointer(o.pointer);
6972
+ const previous = o.previous === null ? null : decodeSequence(o.previous), previousHash = o.previousHash === null ? null : typeof o.previousHash === "string" ? o.previousHash : undefined;
6973
+ if (previousHash === undefined || previous === null !== (previousHash === null) || p.seq !== head.seq || o.hash !== head.hash || chainHash(p.seq, previous, previousHash) !== head.hash || previous !== null && BigInt(previous) >= BigInt(p.seq))
6974
+ throw new ContractError("receipt-inconclusive");
6975
+ return {
6976
+ pointer: p,
6977
+ previous: previous !== null && previousHash !== null ? { seq: previous, hash: previousHash } : null
6978
+ };
6979
+ }
6980
+ taskSuffix(taskId, snapshot, budget, accept) {
6981
+ decodeId(taskId);
6982
+ try {
6983
+ let head = this.anchoredHead(taskId, snapshot, budget) ?? null;
6984
+ const newer = [];
6985
+ while (head) {
6986
+ const base = accept(head);
6987
+ if (base !== undefined)
6988
+ return { base, batches: newer.reverse() };
6989
+ const link = this.link("entity-links", taskId, head, budget);
6990
+ newer.push(this.batch(link.pointer, budget));
6991
+ head = link.previous;
6992
+ }
6993
+ } catch {
6994
+ budget.reserve(0);
6770
6995
  }
6996
+ return;
6771
6997
  }
6772
6998
  runBatches(id, snapshot, budget) {
6773
6999
  const expected = this.anchoredHead(id, snapshot, budget);
@@ -6952,19 +7178,77 @@ function navigationKeys(row) {
6952
7178
  }
6953
7179
 
6954
7180
  // src/history/projection.ts
6955
- import { createHash as createHash12 } from "node:crypto";
7181
+ import { createHash as createHash13 } from "node:crypto";
7182
+
7183
+ // src/history/task-folds.ts
7184
+ var TASKS = 1024;
7185
+ var STATES = 8;
7186
+
7187
+ class TaskFoldMemory {
7188
+ tasks = new Map;
7189
+ has(taskId) {
7190
+ return this.tasks.has(taskId);
7191
+ }
7192
+ remember(taskId, anchor, state) {
7193
+ let states = this.tasks.get(taskId);
7194
+ if (!states) {
7195
+ if (this.tasks.size >= TASKS)
7196
+ this.tasks.delete(this.tasks.keys().next().value ?? "");
7197
+ states = new Map;
7198
+ this.tasks.set(taskId, states);
7199
+ }
7200
+ states.delete(anchor.seq);
7201
+ states.set(anchor.seq, {
7202
+ hash: anchor.hash,
7203
+ state: structuredClone(state)
7204
+ });
7205
+ if (states.size > STATES)
7206
+ states.delete(states.keys().next().value ?? "");
7207
+ }
7208
+ recall(taskId, anchor) {
7209
+ const entry = this.tasks.get(taskId)?.get(anchor.seq);
7210
+ return entry?.hash === anchor.hash ? structuredClone(entry.state) : undefined;
7211
+ }
7212
+ }
7213
+ var memories = new Map;
7214
+ function taskFoldMemory(files) {
7215
+ const key = `${files.directory}\x00${files.format.storeId}`;
7216
+ let memory = memories.get(key);
7217
+ if (!memory) {
7218
+ memory = new TaskFoldMemory;
7219
+ memories.set(key, memory);
7220
+ }
7221
+ return memory;
7222
+ }
7223
+ function resumeTaskFold(files, navigation, taskId, snapshot, budget, resume) {
7224
+ const memory = taskFoldMemory(files);
7225
+ const suffix = memory.has(taskId) ? navigation.taskSuffix(taskId, snapshot, budget, (head) => {
7226
+ const state = memory.recall(taskId, head);
7227
+ if (!state)
7228
+ return;
7229
+ try {
7230
+ return resume(state);
7231
+ } catch {
7232
+ budget.reserve(0);
7233
+ return;
7234
+ }
7235
+ }) : undefined;
7236
+ return suffix ?? {
7237
+ base: undefined,
7238
+ batches: navigation.entityBatches(taskId, snapshot, budget) ?? navigation.scanSnapshot(snapshot, budget)
7239
+ };
7240
+ }
6956
7241
 
6957
7242
  // src/history/checks.ts
6958
- function currentChecks(files, taskId, configured, current, snapshotSeq) {
6959
- const budget = new HistoryReadBudget, navigation = new HistoryNavigation(files);
6960
- const snapshot = snapshotSeq ?? navigation.latest(budget);
6961
- navigation.snapshot(snapshot, budget);
6962
- const starts = new Map;
6963
- const results = new Map;
6964
- const incomplete = new Map;
6965
- const batches = navigation.entityBatches(taskId, snapshot, budget) ?? navigation.scanSnapshot(snapshot, budget);
6966
- for (const b of batches)
6967
- for (const row of b.rows) {
7243
+ class CheckFold {
7244
+ starts;
7245
+ settled;
7246
+ constructor(state) {
7247
+ this.starts = new Map(state?.starts);
7248
+ this.settled = new Map(state?.settled);
7249
+ }
7250
+ apply(batch, taskId) {
7251
+ for (const row of batch.rows) {
6968
7252
  const v = row.value;
6969
7253
  if (v.kind !== "owner" || v.link.taskId !== taskId)
6970
7254
  continue;
@@ -6973,44 +7257,83 @@ function currentChecks(files, taskId, configured, current, snapshotSeq) {
6973
7257
  continue;
6974
7258
  const f = e.value.fact;
6975
7259
  if (f.kind === "check-start")
6976
- starts.set(f.checkId, {
7260
+ this.starts.set(f.checkId, {
6977
7261
  selection: f.selection,
6978
7262
  epoch: v.link.epoch,
6979
7263
  cwdId: f.cwdId,
6980
7264
  checkEnvGeneration: f.checkEnvGeneration
6981
7265
  });
6982
7266
  if (f.kind === "check-settled") {
6983
- const s = starts.get(f.checkId);
7267
+ const s = this.starts.get(f.checkId);
6984
7268
  if (!s)
6985
7269
  throw new ContractError("corrupt-selected");
6986
- if (s.selection.kind === "manual" || configured.selections.some((c) => canonicalJson(c) === canonicalJson(s.selection))) {
6987
- const fresh = current.status === "available" && current.version.epoch === s.epoch && current.version.cwdId === s.cwdId && current.version.checkEnvGeneration === s.checkEnvGeneration && current.version.observation.identity === f.after.identity && current.version.observation.complete === f.after.complete && current.version.checkoutGeneration === f.after.checkoutGeneration;
6988
- incomplete.set(canonicalJson(s.selection), f.output === null || f.coverage.observed !== "complete" || f.coverage.source !== "complete" || f.coverage.omissions.length > 0);
6989
- results.set(canonicalJson(s.selection), {
6990
- selection: s.selection,
6991
- status: f.status === "passed" && !fresh ? "stale" : f.status,
6992
- checkId: f.checkId
6993
- });
6994
- }
6995
- starts.delete(f.checkId);
7270
+ this.settled.set(canonicalJson(s.selection), {
7271
+ ...s,
7272
+ checkId: f.checkId,
7273
+ status: f.status,
7274
+ after: {
7275
+ identity: f.after.identity,
7276
+ complete: f.after.complete,
7277
+ checkoutGeneration: f.after.checkoutGeneration
7278
+ },
7279
+ incomplete: f.output === null || f.coverage.observed !== "complete" || f.coverage.source !== "complete" || f.coverage.omissions.length > 0
7280
+ });
7281
+ this.starts.delete(f.checkId);
6996
7282
  }
6997
- if (starts.size > LIMITS.childReservations || results.size > 32)
7283
+ if (this.starts.size > LIMITS.childReservations)
6998
7284
  throw new ContractError("receipt-inconclusive");
6999
7285
  }
7000
7286
  }
7001
- return {
7002
- checks: decodeCheckObligations({
7003
- taskId,
7004
- revision: configured.revision,
7005
- definitions: configured.selections.map((selection) => results.get(canonicalJson(selection)) ?? {
7006
- selection,
7007
- status: "not-run",
7008
- checkId: null
7009
- })
7010
- }),
7011
- recorded: [...results.values()],
7012
- recordingIncomplete: [...incomplete.values()].some(Boolean)
7013
- };
7287
+ }
7288
+ state() {
7289
+ return {
7290
+ starts: [...this.starts],
7291
+ settled: [...this.settled]
7292
+ };
7293
+ }
7294
+ evaluate(taskId, configured, current) {
7295
+ const results = new Map;
7296
+ const incomplete = new Map;
7297
+ for (const [key, s] of this.settled) {
7298
+ if (s.selection.kind !== "manual" && !configured.selections.some((c) => canonicalJson(c) === key))
7299
+ continue;
7300
+ const fresh = current.status === "available" && current.version.epoch === s.epoch && current.version.cwdId === s.cwdId && current.version.checkEnvGeneration === s.checkEnvGeneration && current.version.observation.identity === s.after.identity && current.version.observation.complete === s.after.complete && current.version.checkoutGeneration === s.after.checkoutGeneration;
7301
+ incomplete.set(key, s.incomplete);
7302
+ results.set(key, {
7303
+ selection: s.selection,
7304
+ status: s.status === "passed" && !fresh ? "stale" : s.status,
7305
+ checkId: s.checkId
7306
+ });
7307
+ }
7308
+ if (results.size > 32)
7309
+ throw new ContractError("receipt-inconclusive");
7310
+ return {
7311
+ checks: decodeCheckObligations({
7312
+ taskId,
7313
+ revision: configured.revision,
7314
+ definitions: configured.selections.map((selection) => results.get(canonicalJson(selection)) ?? {
7315
+ selection,
7316
+ status: "not-run",
7317
+ checkId: null
7318
+ })
7319
+ }),
7320
+ recorded: [...results.values()],
7321
+ recordingIncomplete: [...incomplete.values()].some(Boolean)
7322
+ };
7323
+ }
7324
+ }
7325
+ function checkFold(files, taskId, snapshot, budget, navigation = new HistoryNavigation(files)) {
7326
+ navigation.snapshot(snapshot, budget);
7327
+ const { base, batches } = resumeTaskFold(files, navigation, taskId, snapshot, budget, (state) => state.checks);
7328
+ const fold = new CheckFold(base);
7329
+ for (const b of batches)
7330
+ fold.apply(b, taskId);
7331
+ return fold;
7332
+ }
7333
+ function currentChecks(files, taskId, configured, current, snapshotSeq) {
7334
+ const budget = HistoryReadBudget.authority(), navigation = new HistoryNavigation(files);
7335
+ const snapshot = snapshotSeq ?? navigation.latest(budget);
7336
+ return checkFold(files, taskId, snapshot, budget, navigation).evaluate(taskId, configured, current);
7014
7337
  }
7015
7338
 
7016
7339
  // src/history/entity-state.ts
@@ -7198,17 +7521,17 @@ function materializeEntity(s, sources) {
7198
7521
  }
7199
7522
 
7200
7523
  // src/history/export.ts
7201
- import { createHash as createHash10, randomUUID as randomUUID4 } from "node:crypto";
7524
+ import { createHash as createHash11, randomUUID as randomUUID4 } from "node:crypto";
7202
7525
  import { readFileSync } from "node:fs";
7203
7526
 
7204
7527
  // src/history/search.ts
7205
- import { createHash as createHash9 } from "node:crypto";
7528
+ import { createHash as createHash10 } from "node:crypto";
7206
7529
 
7207
7530
  // src/history/read-proof.ts
7208
7531
  import {
7209
7532
  createCipheriv,
7210
7533
  createDecipheriv,
7211
- createHash as createHash7,
7534
+ createHash as createHash8,
7212
7535
  randomBytes as randomBytes2
7213
7536
  } from "node:crypto";
7214
7537
  var PREFIX = "rp1.";
@@ -7260,7 +7583,7 @@ class HistoryReadProof {
7260
7583
  root: this.files.format.rootIdentity,
7261
7584
  format: material.formatStamp,
7262
7585
  key: material.keyStamp,
7263
- request: createHash7("sha256").update(canonicalJson(readIdentity(request))).digest("hex")
7586
+ request: createHash8("sha256").update(canonicalJson(readIdentity(request))).digest("hex")
7264
7587
  }));
7265
7588
  }
7266
7589
  seal(context, plaintext, budget) {
@@ -8032,11 +8355,11 @@ function decodeRetainedProgress(value) {
8032
8355
  }
8033
8356
 
8034
8357
  // src/history/retained-traversal.ts
8035
- import { createHash as createHash8 } from "node:crypto";
8358
+ import { createHash as createHash9 } from "node:crypto";
8036
8359
  function bad5() {
8037
8360
  throw new ContractError("corrupt-selected");
8038
8361
  }
8039
- var fingerprint = (v) => createHash8("sha256").update(canonicalJson(v)).digest("hex");
8362
+ var fingerprint = (v) => createHash9("sha256").update(canonicalJson(v)).digest("hex");
8040
8363
  var ready = (v) => {
8041
8364
  const s = v.state;
8042
8365
  if (s.phase !== "ready")
@@ -8392,11 +8715,11 @@ var digits = (id) => {
8392
8715
  return s.slice(0, 12) + s.slice(13, 16) + s.slice(17);
8393
8716
  };
8394
8717
  var publicTextRef = (id, label, text2, field, batch, row, slot) => ({
8395
- recordId: uuid(address(batch.pointer).toString(16).padStart(14, "0") + createHash9("sha256").update(`${id}/${label}`).digest("hex").slice(0, 16)),
8718
+ recordId: uuid(address(batch.pointer).toString(16).padStart(14, "0") + createHash10("sha256").update(`${id}/${label}`).digest("hex").slice(0, 16)),
8396
8719
  version: batch.rows.indexOf(row) * EVENT_SLOTS + slot + 1,
8397
8720
  field,
8398
8721
  bytes: Buffer.byteLength(text2),
8399
- sha256: createHash9("sha256").update(text2).digest("hex")
8722
+ sha256: createHash10("sha256").update(text2).digest("hex")
8400
8723
  });
8401
8724
  var initial = () => ({
8402
8725
  pos: { segment: 1, offset: 0 },
@@ -8412,7 +8735,7 @@ function scanCursor(base, p) {
8412
8735
  const snapshot = p.snapshot.toString(16).padStart(14, "0");
8413
8736
  if (snapshot.length !== 14)
8414
8737
  throw new ContractError("capacity");
8415
- const binding = createHash9("sha256").update(canonicalJson({
8738
+ const binding = createHash10("sha256").update(canonicalJson({
8416
8739
  ...readIdentity(base),
8417
8740
  recordId: null,
8418
8741
  recordVersion: version,
@@ -8753,7 +9076,7 @@ class RetainedScan {
8753
9076
  graph: {
8754
9077
  recordId: v.intent.operationId,
8755
9078
  revision: graph.revision,
8756
- sha256: createHash9("sha256").update(canonicalJson(graph)).digest("hex")
9079
+ sha256: createHash10("sha256").update(canonicalJson(graph)).digest("hex")
8757
9080
  }
8758
9081
  },
8759
9082
  graph
@@ -8941,6 +9264,9 @@ class RetainedScan {
8941
9264
  }
8942
9265
  }
8943
9266
  async function searchHistory(reader, value, limit = LIMITS.pageEntries) {
9267
+ return retryStalled(() => searchQuantum(reader, value, limit));
9268
+ }
9269
+ async function searchQuantum(reader, value, limit = LIMITS.pageEntries) {
8944
9270
  const cursor = decodeCursor(value);
8945
9271
  if (!cursor.query || !Number.isInteger(limit) || limit < 1 || limit > LIMITS.maxPageEntries)
8946
9272
  throw new ContractError("invalid");
@@ -9034,7 +9360,7 @@ async function searchHistory(reader, value, limit = LIMITS.pageEntries) {
9034
9360
  if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive")
9035
9361
  throw error;
9036
9362
  if (!hits.length && before === coordinate())
9037
- throw error;
9363
+ noProgress(originalBudget);
9038
9364
  done = false;
9039
9365
  }
9040
9366
  scan.traversal?.assertCurrent();
@@ -9049,7 +9375,7 @@ async function searchHistory(reader, value, limit = LIMITS.pageEntries) {
9049
9375
  // src/history/export.ts
9050
9376
  var exporterIdentity;
9051
9377
  function buildIdentity() {
9052
- exporterIdentity ??= createHash10("sha256").update(readFileSync(new URL(import.meta.url))).digest("hex");
9378
+ exporterIdentity ??= createHash11("sha256").update(readFileSync(new URL(import.meta.url))).digest("hex");
9053
9379
  return exporterIdentity;
9054
9380
  }
9055
9381
  function aborted(signal) {
@@ -9109,7 +9435,7 @@ Coverage: ${canonicalJson(event.coverage)}
9109
9435
  }
9110
9436
  }
9111
9437
  async function* exportHistory(projection, input, signal) {
9112
- const request = decodeExportRequest(input), exportId = randomUUID4(), digest = createHash10("sha256");
9438
+ const request = decodeExportRequest(input), exportId = randomUUID4(), digest = createHash11("sha256");
9113
9439
  let ordinal = 0, encodedBytes = 0, coverage = { ...COMPLETE, omissions: [] };
9114
9440
  const frame = (event) => {
9115
9441
  aborted(signal);
@@ -9138,7 +9464,7 @@ async function* exportHistory(projection, input, signal) {
9138
9464
  snapshot: 0
9139
9465
  };
9140
9466
  const scan = new RetainedScan(projection.reader, request, new HistoryReadBudget, true);
9141
- for (;; ) {
9467
+ for (let stalls2 = 0;; ) {
9142
9468
  aborted(signal);
9143
9469
  const before = canonicalJson({ point, state: scan.checkpoint() });
9144
9470
  scan.renew(new HistoryReadBudget);
@@ -9146,8 +9472,13 @@ async function* exportHistory(projection, input, signal) {
9146
9472
  scan.prepare(point);
9147
9473
  break;
9148
9474
  } catch (error) {
9149
- if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive" || before === canonicalJson({ point, state: scan.checkpoint() }))
9475
+ if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive")
9150
9476
  throw error;
9477
+ if (before === canonicalJson({ point, state: scan.checkpoint() })) {
9478
+ if (!scan.budget.elapsed || ++stalls2 > STALL_RETRIES)
9479
+ throw error;
9480
+ } else
9481
+ stalls2 = 0;
9151
9482
  await yieldTurn();
9152
9483
  }
9153
9484
  }
@@ -9173,7 +9504,7 @@ async function* exportHistory(projection, input, signal) {
9173
9504
  const { detail, owner } = item, original = open2.get(detail.content.recordId);
9174
9505
  if (detail.content.bytes ? !original || original.bytes !== detail.content.bytes || original.content !== canonicalJson(detail.content) : original !== undefined)
9175
9506
  throw new ContractError("corrupt-selected");
9176
- if ((original?.hash ?? createHash10("sha256")).digest("hex") !== detail.content.sha256)
9507
+ if ((original?.hash ?? createHash11("sha256")).digest("hex") !== detail.content.sha256)
9177
9508
  throw new ContractError("corrupt-selected");
9178
9509
  open2.delete(detail.content.recordId);
9179
9510
  const events = [];
@@ -9204,7 +9535,7 @@ async function* exportHistory(projection, input, signal) {
9204
9535
  coverage = combineCoverage(coverage, item.coverage);
9205
9536
  const length = Buffer.byteLength(item.text);
9206
9537
  if (item.inline) {
9207
- if (item.start !== 0 || length !== item.content.bytes || createHash10("sha256").update(item.text).digest("hex") !== item.content.sha256)
9538
+ if (item.start !== 0 || length !== item.content.bytes || createHash11("sha256").update(item.text).digest("hex") !== item.content.sha256)
9208
9539
  throw new ContractError("corrupt-selected");
9209
9540
  } else {
9210
9541
  let original = open2.get(item.content.recordId);
@@ -9214,7 +9545,7 @@ async function* exportHistory(projection, input, signal) {
9214
9545
  original = {
9215
9546
  bytes: 0,
9216
9547
  content: canonicalJson(item.content),
9217
- hash: createHash10("sha256")
9548
+ hash: createHash11("sha256")
9218
9549
  };
9219
9550
  open2.set(item.content.recordId, original);
9220
9551
  }
@@ -9234,7 +9565,7 @@ async function* exportHistory(projection, input, signal) {
9234
9565
  }
9235
9566
  ];
9236
9567
  };
9237
- let finished = false;
9568
+ let finished = false, stalls = 0;
9238
9569
  while (!finished) {
9239
9570
  aborted(signal);
9240
9571
  const before = canonicalJson({ point, state: scan.checkpoint() });
@@ -9293,8 +9624,13 @@ async function* exportHistory(projection, input, signal) {
9293
9624
  if (finished)
9294
9625
  scan.finish();
9295
9626
  } catch (error) {
9296
- if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive" || before === canonicalJson({ point, state: scan.checkpoint() }))
9627
+ if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive")
9297
9628
  throw error;
9629
+ if (before === canonicalJson({ point, state: scan.checkpoint() })) {
9630
+ if (!scan.budget.elapsed || ++stalls > STALL_RETRIES)
9631
+ throw error;
9632
+ } else
9633
+ stalls = 0;
9298
9634
  finished = false;
9299
9635
  }
9300
9636
  coverage = combineCoverage(coverage, scan.coverage);
@@ -9324,7 +9660,7 @@ async function* exportHistory(projection, input, signal) {
9324
9660
  }
9325
9661
 
9326
9662
  // src/history/list-progress.ts
9327
- import { createHash as createHash11 } from "node:crypto";
9663
+ import { createHash as createHash12 } from "node:crypto";
9328
9664
 
9329
9665
  // src/history/detail-progress.ts
9330
9666
  function bad7() {
@@ -9365,6 +9701,9 @@ function detailPreparation(cursor, completion) {
9365
9701
  });
9366
9702
  }
9367
9703
  async function readRetainedDetail(reader, input) {
9704
+ return retryStalled(() => detailQuantum(reader, input));
9705
+ }
9706
+ async function detailQuantum(reader, input) {
9368
9707
  const cursor = decodeCursor(input);
9369
9708
  if (cursor.storeId !== reader.storeId || !cursor.recordId)
9370
9709
  bad7();
@@ -9466,8 +9805,10 @@ async function readRetainedDetail(reader, input) {
9466
9805
  p.event = 0;
9467
9806
  }
9468
9807
  } catch (error) {
9469
- if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive" || coordinate() === before)
9808
+ if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive")
9470
9809
  throw error;
9810
+ if (coordinate() === before)
9811
+ noProgress(original);
9471
9812
  return decodeDetailReadResult({
9472
9813
  kind: "progress",
9473
9814
  cursor,
@@ -9793,7 +10134,7 @@ function fits(link, scope) {
9793
10134
  return scope.kind === "workspace" || (scope.kind === "task" ? link.taskId === scope.id : scope.kind === "run" ? link.runId === scope.id : link.childRunId === scope.id);
9794
10135
  }
9795
10136
  function nextAfter(request, n) {
9796
- const h = createHash11("sha256").update(canonicalJson({ ...readIdentity(request), after: null, ordinal: n })).digest("hex");
10137
+ const h = createHash12("sha256").update(canonicalJson({ ...readIdentity(request), after: null, ordinal: n })).digest("hex");
9797
10138
  return {
9798
10139
  recordId: `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-8${h.slice(17, 20)}-${h.slice(20, 32)}`,
9799
10140
  version: 1,
@@ -9900,10 +10241,10 @@ function advanceTarget(p, t, snapshot, budget, sources) {
9900
10241
  }
9901
10242
  }
9902
10243
  async function readRetainedList(p, request) {
9903
- return read(p, request, false);
10244
+ return retryStalled(() => read(p, request, false));
9904
10245
  }
9905
10246
  async function readRetainedEntity(p, request) {
9906
- return read(p, request, true);
10247
+ return retryStalled(() => read(p, request, true));
9907
10248
  }
9908
10249
  async function read(p, request, entity) {
9909
10250
  const budget = new HistoryReadBudget, proof = new HistoryReadProof(p.reader.files), completion = proof.prepareCompletion(budget);
@@ -9932,7 +10273,7 @@ async function read(p, request, entity) {
9932
10273
  };
9933
10274
  const progress = () => {
9934
10275
  if (coordinate() === before)
9935
- throw new ContractError("receipt-inconclusive");
10276
+ noProgress(budget);
9936
10277
  const next = seal(request);
9937
10278
  return entity ? decodeEntityReadResult({
9938
10279
  kind: "progress",
@@ -10177,7 +10518,7 @@ async function read(p, request, entity) {
10177
10518
  const tasks = [...p.environment.legacyTasks(budget)].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
10178
10519
  if (tasks.length > 1000)
10179
10520
  throw new ContractError("capacity");
10180
- const sum = createHash11("sha256");
10521
+ const sum = createHash12("sha256");
10181
10522
  for (const task2 of tasks) {
10182
10523
  budget.reserve(0);
10183
10524
  sum.update(JSON.stringify({
@@ -10190,7 +10531,7 @@ async function read(p, request, entity) {
10190
10531
  if (s.legacy > tasks.length)
10191
10532
  bad9();
10192
10533
  if (!s.legacyIdentity && request.after?.version === Number.MAX_SAFE_INTEGER) {
10193
- const h = createHash11("sha256").update(canonicalJson({
10534
+ const h = createHash12("sha256").update(canonicalJson({
10194
10535
  ...readIdentity(request),
10195
10536
  after: null,
10196
10537
  legacy: s.legacy === 0 ? "begin" : identity,
@@ -10470,7 +10811,7 @@ class HistoryProjection {
10470
10811
  };
10471
10812
  cursor(request, pos, row, item) {
10472
10813
  const offset = pos.offset * (LIMITS.segmentRecords + 1) * ROW_SLOTS + row * ROW_SLOTS + item;
10473
- const digest = createHash12("sha256").update(canonicalJson({
10814
+ const digest = createHash13("sha256").update(canonicalJson({
10474
10815
  ...readIdentity(request),
10475
10816
  after: null,
10476
10817
  position: { ...pos, row, item }
@@ -10661,7 +11002,7 @@ class HistoryProjection {
10661
11002
  if (legacyPhase)
10662
11003
  this.navigation.snapshot(snapshot, budget);
10663
11004
  const cursor = (offset2, identity2) => {
10664
- const digest2 = createHash12("sha256").update(canonicalJson({ ...request, after: null, legacy: identity2, offset: offset2 })).digest("hex");
11005
+ const digest2 = createHash13("sha256").update(canonicalJson({ ...request, after: null, legacy: identity2, offset: offset2 })).digest("hex");
10665
11006
  return {
10666
11007
  recordId: `${digest2.slice(0, 8)}-${digest2.slice(8, 12)}-4${digest2.slice(13, 16)}-8${digest2.slice(17, 20)}-${digest2.slice(20, 32)}`,
10667
11008
  version: legacyVersion,
@@ -10688,7 +11029,7 @@ class HistoryProjection {
10688
11029
  }
10689
11030
  if (legacy.length > 1000)
10690
11031
  throw new ContractError("capacity");
10691
- const items = [...legacy].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0), digest = createHash12("sha256");
11032
+ const items = [...legacy].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0), digest = createHash13("sha256");
10692
11033
  for (const item of items) {
10693
11034
  budget.reserve(0);
10694
11035
  if (Buffer.byteLength(item.goal) > 8192)
@@ -10878,7 +11219,7 @@ class HistoryProjection {
10878
11219
  }
10879
11220
 
10880
11221
  // src/history/reader.ts
10881
- import { createHash as createHash13 } from "node:crypto";
11222
+ import { createHash as createHash14 } from "node:crypto";
10882
11223
  import { existsSync } from "node:fs";
10883
11224
  import { join as join6 } from "node:path";
10884
11225
 
@@ -11010,6 +11351,9 @@ function graphPreparation(request, completion) {
11010
11351
  });
11011
11352
  }
11012
11353
  async function readRetainedGraph(reader, request) {
11354
+ return retryStalled(() => graphQuantum(reader, request));
11355
+ }
11356
+ async function graphQuantum(reader, request) {
11013
11357
  if (request.storeId !== reader.storeId)
11014
11358
  throw new ContractError("corrupt-selected");
11015
11359
  const budget = new HistoryReadBudget, proof = new HistoryReadProof(reader.files), completion = proof.prepareCompletion(budget);
@@ -11069,8 +11413,10 @@ async function readRetainedGraph(reader, request) {
11069
11413
  point.event = 0;
11070
11414
  }
11071
11415
  } catch (error) {
11072
- if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive" || coordinate() === before)
11416
+ if (!(error instanceof ContractError) || error.code !== "receipt-inconclusive")
11073
11417
  throw error;
11418
+ if (coordinate() === before)
11419
+ noProgress(budget);
11074
11420
  traversal.assertCurrent();
11075
11421
  const next = {
11076
11422
  ...readIdentity(request),
@@ -11089,6 +11435,95 @@ async function readRetainedGraph(reader, request) {
11089
11435
  }
11090
11436
 
11091
11437
  // src/history/reader.ts
11438
+ class PlanFold {
11439
+ lineage;
11440
+ operation = null;
11441
+ graphIdentity = "";
11442
+ savePlanAt = null;
11443
+ lineageAt = null;
11444
+ constructor(lineage) {
11445
+ this.lineage = lineage;
11446
+ }
11447
+ apply(batch, taskId) {
11448
+ batch.rows.forEach((row, index) => {
11449
+ const value = row.value;
11450
+ if (value.kind === "admission" && value.intent.identity.kind === "action" && value.intent.identity.action.kind === "save-plan" && value.intent.identity.action.taskId === taskId) {
11451
+ if (value.intent.runId !== null)
11452
+ throw new ContractError("corrupt-selected");
11453
+ this.operation = new OperationState(value.intent.operationId);
11454
+ this.graphIdentity = hash(canonicalJson(value.intent.identity.action.graph));
11455
+ this.savePlanAt = { pointer: batch.pointer, row: index };
11456
+ }
11457
+ this.operation?.apply(row, batch.pointer.seq);
11458
+ if (!this.lineage)
11459
+ return;
11460
+ this.lineage.apply(row, batch.pointer.seq, taskId, this.graphIdentity);
11461
+ if (value.kind === "admission" && this.lineage.operationId === value.intent.operationId)
11462
+ this.lineageAt = { pointer: batch.pointer, row: index };
11463
+ });
11464
+ }
11465
+ state() {
11466
+ if (!this.lineage)
11467
+ throw new ContractError("invalid");
11468
+ return {
11469
+ savePlan: this.operation && this.savePlanAt ? { at: this.savePlanAt, ...this.operation.progress() } : null,
11470
+ graphIdentity: this.graphIdentity,
11471
+ lineage: { at: this.lineageAt, progress: this.lineage.progress() }
11472
+ };
11473
+ }
11474
+ static resumed(state, withLineage, read2) {
11475
+ let lineage = null;
11476
+ if (withLineage) {
11477
+ const row = state.lineage.at && read2(state.lineage.at).value;
11478
+ if (row && row.kind !== "admission")
11479
+ throw new ContractError("invalid");
11480
+ lineage = TaskPublicationLineage.resumed(row ? row.intent : null, state.lineage.progress);
11481
+ }
11482
+ const fold = new PlanFold(lineage);
11483
+ fold.lineageAt = state.lineage.at;
11484
+ fold.graphIdentity = state.graphIdentity;
11485
+ if (!state.savePlan) {
11486
+ if (state.graphIdentity !== "")
11487
+ throw new ContractError("invalid");
11488
+ return fold;
11489
+ }
11490
+ const v = read2(state.savePlan.at).value;
11491
+ if (v.kind !== "admission" || v.intent.identity.kind !== "action" || v.intent.identity.action.kind !== "save-plan" || hash(canonicalJson(v.intent.identity.action.graph)) !== state.graphIdentity)
11492
+ throw new ContractError("invalid");
11493
+ fold.operation = OperationState.resumed(v.intent.operationId, { intent: v.intent, record: v.record }, state.savePlan);
11494
+ fold.savePlanAt = state.savePlan.at;
11495
+ return fold;
11496
+ }
11497
+ }
11498
+ function planFold(files, taskId, snapshot, budget, navigation, withLineage) {
11499
+ navigation.snapshot(snapshot, budget);
11500
+ const read2 = (at) => {
11501
+ if (BigInt(at.pointer.seq) > BigInt(snapshot))
11502
+ throw new ContractError("invalid");
11503
+ const row = navigation.batch(at.pointer, budget).rows[at.row];
11504
+ if (!row)
11505
+ throw new ContractError("invalid");
11506
+ return row;
11507
+ };
11508
+ const { base, batches } = resumeTaskFold(files, navigation, taskId, snapshot, budget, (state) => PlanFold.resumed(state.plan, withLineage, read2));
11509
+ const fold = base ?? new PlanFold(withLineage ? new TaskPublicationLineage : null);
11510
+ for (const batch of batches)
11511
+ fold.apply(batch, taskId);
11512
+ return fold;
11513
+ }
11514
+ function latestTaskRun(files, taskId, snapshot, budget, navigation = new HistoryNavigation(files)) {
11515
+ navigation.snapshot(snapshot, budget);
11516
+ const { base, batches } = resumeTaskFold(files, navigation, taskId, snapshot, budget, (state) => state.run);
11517
+ let latest = base ?? null;
11518
+ for (const batch of batches)
11519
+ for (const row of batch.rows) {
11520
+ const v = row.value;
11521
+ if (v.kind === "admission" && v.intent.identity.kind === "action" && v.intent.identity.expected.task?.id === taskId && (v.intent.identity.action.kind === "start" || v.intent.identity.action.kind === "generate-plan"))
11522
+ latest = v.intent.runId;
11523
+ }
11524
+ return latest;
11525
+ }
11526
+
11092
11527
  class HistoryReader {
11093
11528
  files;
11094
11529
  cached;
@@ -11104,7 +11539,7 @@ class HistoryReader {
11104
11539
  admission(runId, snapshotSeq) {
11105
11540
  decodeId(runId);
11106
11541
  decodeSequence(snapshotSeq);
11107
- const budget = new HistoryReadBudget;
11542
+ const budget = HistoryReadBudget.authority();
11108
11543
  this.navigation.snapshot(snapshotSeq, budget);
11109
11544
  const batches = this.navigation.entityBatches(runId, snapshotSeq, budget);
11110
11545
  const state = batches ? new RunState(runId) : readRunState(this.files, runId, snapshotSeq, budget);
@@ -11119,26 +11554,9 @@ class HistoryReader {
11119
11554
  publishedPlan(taskId, snapshotSeq, current2) {
11120
11555
  decodeId(taskId);
11121
11556
  decodeSequence(snapshotSeq);
11122
- const budget = new HistoryReadBudget;
11123
- this.navigation.snapshot(snapshotSeq, budget);
11124
- const batches = this.navigation.entityBatches(taskId, snapshotSeq, budget) ?? this.navigation.scanSnapshot(snapshotSeq, budget);
11125
- let operation = null;
11126
- const lineage = new TaskPublicationLineage;
11127
- let graphIdentity = "";
11128
- for (const batch of batches) {
11129
- for (const row of batch.rows) {
11130
- const value = row.value;
11131
- if (value.kind === "admission" && value.intent.identity.kind === "action" && value.intent.identity.action.kind === "save-plan" && value.intent.identity.action.taskId === taskId) {
11132
- if (value.intent.runId !== null)
11133
- throw new ContractError("corrupt-selected");
11134
- operation = new OperationState(value.intent.operationId);
11135
- graphIdentity = hash(canonicalJson(value.intent.identity.action.graph));
11136
- }
11137
- operation?.apply(row, batch.pointer.seq);
11138
- if (current2)
11139
- lineage.apply(row, batch.pointer.seq, taskId, graphIdentity);
11140
- }
11141
- }
11557
+ const budget = HistoryReadBudget.authority();
11558
+ const fold = planFold(this.files, taskId, snapshotSeq, budget, this.navigation, !!current2);
11559
+ const { operation, lineage } = fold;
11142
11560
  if (!operation)
11143
11561
  return null;
11144
11562
  const command = operation.command;
@@ -11149,7 +11567,7 @@ class HistoryReader {
11149
11567
  throw new ContractError("conflict");
11150
11568
  const { graph, plan } = command.intent.identity.action;
11151
11569
  const ref = graph.plan;
11152
- const detail = this.selected({
11570
+ const { detail, bytes: text2 } = this.planText({
11153
11571
  storeId: this.storeId,
11154
11572
  scope: { kind: "task", id: taskId },
11155
11573
  snapshotSeq,
@@ -11161,13 +11579,10 @@ class HistoryReader {
11161
11579
  }, budget);
11162
11580
  if (canonicalJson(detail.content) !== canonicalJson(ref) || detail.origin.kind !== "owner" || detail.origin.id !== operation.operationId || detail.owner.taskId !== taskId || detail.owner.runId !== null || detail.owner.childRunId !== null || detail.owner.entity.kind !== "task" || detail.owner.entity.id !== taskId || detail.owner.entity.version !== graph.planRevision || ref.bytes > 32768)
11163
11581
  throw new ContractError("corrupt-selected");
11164
- const chunks = [];
11165
- for (const part of this.parts(detail, snapshotSeq, budget))
11166
- chunks.push(part.bytes);
11167
- if (Buffer.concat(chunks).toString("utf8") !== plan)
11582
+ if (text2.toString("utf8") !== plan)
11168
11583
  throw new ContractError("corrupt-selected");
11169
- const publication = current2 ? lineage.current() : operation.publicationExpected();
11170
- if (!publication.identity)
11584
+ const publication = current2 ? lineage?.current() : operation.publicationExpected();
11585
+ if (!publication?.identity)
11171
11586
  throw new ContractError("corrupt-selected");
11172
11587
  if (current2 && (current2.identity !== publication.identity || current2.revision !== publication.revision || current2.plan !== plan))
11173
11588
  throw new ContractError("stale");
@@ -11186,19 +11601,11 @@ class HistoryReader {
11186
11601
  committedSeq: receipt.receipt.committedSeq
11187
11602
  };
11188
11603
  }
11189
- reviewRun(taskId, runId, snapshotSeq, budget = new HistoryReadBudget) {
11604
+ reviewRun(taskId, runId, snapshotSeq, budget = HistoryReadBudget.authority()) {
11190
11605
  decodeId(taskId);
11191
11606
  decodeId(runId);
11192
11607
  decodeSequence(snapshotSeq);
11193
- this.navigation.snapshot(snapshotSeq, budget);
11194
- const batches = () => this.navigation.entityBatches(taskId, snapshotSeq, budget) ?? this.navigation.scanSnapshot(snapshotSeq, budget);
11195
- let latest = null;
11196
- for (const batch of batches())
11197
- for (const row of batch.rows) {
11198
- const v = row.value;
11199
- if (v.kind === "admission" && v.intent.identity.kind === "action" && v.intent.identity.expected.task?.id === taskId && (v.intent.identity.action.kind === "start" || v.intent.identity.action.kind === "generate-plan"))
11200
- latest = v.intent.runId;
11201
- }
11608
+ const latest = latestTaskRun(this.files, taskId, snapshotSeq, budget, this.navigation);
11202
11609
  if (latest !== runId)
11203
11610
  throw new ContractError("stale");
11204
11611
  const state = new RunState(runId);
@@ -11212,7 +11619,7 @@ class HistoryReader {
11212
11619
  return state;
11213
11620
  }
11214
11621
  reviewSelection(taskId, runId, snapshotSeq) {
11215
- const budget = new HistoryReadBudget;
11622
+ const budget = HistoryReadBudget.authority();
11216
11623
  const run = this.reviewRun(taskId, runId, snapshotSeq, budget);
11217
11624
  if (!run.admission)
11218
11625
  throw new ContractError("corrupt-selected");
@@ -11220,7 +11627,7 @@ class HistoryReader {
11220
11627
  if (!graph)
11221
11628
  return { run, graph, readablePlan: "" };
11222
11629
  const ref = graph.plan;
11223
- const detail = this.selected({
11630
+ const { detail, bytes } = this.planText({
11224
11631
  storeId: this.storeId,
11225
11632
  scope: { kind: "task", id: taskId },
11226
11633
  snapshotSeq,
@@ -11232,7 +11639,6 @@ class HistoryReader {
11232
11639
  }, budget);
11233
11640
  if (ref.bytes > 32768 || canonicalJson(detail.content) !== canonicalJson(ref) || detail.owner.taskId !== taskId || detail.owner.runId !== null || detail.owner.childRunId !== null || detail.owner.entity.kind !== "task" || detail.owner.entity.id !== taskId || detail.owner.entity.version !== graph.planRevision)
11234
11641
  throw new ContractError("corrupt-selected");
11235
- const bytes = Buffer.concat([...this.parts(detail, snapshotSeq, budget)].map((p) => p.bytes));
11236
11642
  return { run, graph, readablePlan: bytes.toString("utf8") };
11237
11643
  }
11238
11644
  async graph(value) {
@@ -11437,6 +11843,52 @@ class HistoryReader {
11437
11843
  throw new ContractError("invalid");
11438
11844
  return this.cached.detail;
11439
11845
  }
11846
+ planText(cursor, budget) {
11847
+ const indexed = this.indexedText(cursor, budget);
11848
+ if (indexed)
11849
+ return indexed;
11850
+ const detail = this.selected(cursor, budget);
11851
+ return {
11852
+ detail,
11853
+ bytes: Buffer.concat([...this.parts(detail, cursor.snapshotSeq, budget)].map((p) => p.bytes))
11854
+ };
11855
+ }
11856
+ indexedText(cursor, budget) {
11857
+ const nav = this.navigation, id = cursor.recordId;
11858
+ if (!id)
11859
+ return;
11860
+ try {
11861
+ this.files.assertReadFormat(budget);
11862
+ nav.snapshot(cursor.snapshotSeq, budget);
11863
+ const detail = nav.content(id, cursor.snapshotSeq, budget);
11864
+ if (!detail || detail.content.version !== cursor.recordVersion || detail.content.field !== cursor.field)
11865
+ return;
11866
+ const pieces = [];
11867
+ let end = 0;
11868
+ if (detail.content.bytes) {
11869
+ const count2 = nav.partCount(id, budget);
11870
+ for (let part = 0;part < count2; part++) {
11871
+ const chunks = nav.part(id, part, cursor.snapshotSeq, budget);
11872
+ if (!chunks?.length)
11873
+ return;
11874
+ const bytes = this.files.readArtifact(chunks[0].artifact, budget);
11875
+ for (const c of chunks) {
11876
+ if (c.offset !== end || c.start + c.length > bytes.length)
11877
+ return;
11878
+ pieces.push(bytes.subarray(c.start, c.start + c.length));
11879
+ end += c.length;
11880
+ }
11881
+ }
11882
+ }
11883
+ const text2 = Buffer.concat(pieces);
11884
+ if (end !== detail.content.bytes || createHash14("sha256").update(text2).digest("hex") !== detail.content.sha256)
11885
+ return;
11886
+ return { detail, bytes: text2 };
11887
+ } catch {
11888
+ budget.reserve(0);
11889
+ return;
11890
+ }
11891
+ }
11440
11892
  *parts(detail, snapshotSeq, budget, range2) {
11441
11893
  if (budget)
11442
11894
  this.files.assertReadFormat(budget);
@@ -11464,7 +11916,7 @@ class HistoryReader {
11464
11916
  if (!current2 || current2.length < expected.bytes || hash(current2.subarray(0, expected.bytes)) !== expected.identity)
11465
11917
  throw new ContractError("corrupt-selected");
11466
11918
  }
11467
- const checksum = createHash13("sha256");
11919
+ const checksum = createHash14("sha256");
11468
11920
  let bytes = 0;
11469
11921
  let cached;
11470
11922
  for (const e of locator.chunks) {
@@ -11738,7 +12190,7 @@ class HistoryReader {
11738
12190
  }
11739
12191
 
11740
12192
  // src/history/writer.ts
11741
- import { createHash as createHash15, randomUUID as randomUUID6 } from "node:crypto";
12193
+ import { createHash as createHash16, randomUUID as randomUUID6 } from "node:crypto";
11742
12194
  import { join as join8 } from "node:path";
11743
12195
 
11744
12196
  // src/worker-redaction.mjs
@@ -12100,11 +12552,11 @@ class DetailSanitizer {
12100
12552
  }
12101
12553
 
12102
12554
  // src/history/plans.ts
12103
- import { createHash as createHash14, randomUUID as randomUUID5 } from "node:crypto";
12555
+ import { createHash as createHash15, randomUUID as randomUUID5 } from "node:crypto";
12104
12556
  function assertPlanContent(plan, reference, secrets) {
12105
12557
  text(plan, 32768);
12106
12558
  const ref = decodeContentRef(reference);
12107
- if (ref.field !== "plan" || ref.bytes !== Buffer.byteLength(plan) || ref.sha256 !== createHash14("sha256").update(plan).digest("hex") || sanitizeText(plan, secrets) !== plan)
12559
+ if (ref.field !== "plan" || ref.bytes !== Buffer.byteLength(plan) || ref.sha256 !== createHash15("sha256").update(plan).digest("hex") || sanitizeText(plan, secrets) !== plan)
12108
12560
  throw new ContractError("invalid");
12109
12561
  }
12110
12562
  function proposePlanContent(plan, version, secrets) {
@@ -12114,7 +12566,7 @@ function proposePlanContent(plan, version, secrets) {
12114
12566
  version,
12115
12567
  field: "plan",
12116
12568
  bytes: Buffer.byteLength(plan),
12117
- sha256: createHash14("sha256").update(plan).digest("hex")
12569
+ sha256: createHash15("sha256").update(plan).digest("hex")
12118
12570
  });
12119
12571
  assertPlanContent(plan, reference, secrets);
12120
12572
  return reference;
@@ -12833,9 +13285,9 @@ class HistoryWriter {
12833
13285
  const continuation = jobs.every((j) => j.safety || j.continuation);
12834
13286
  try {
12835
13287
  this.assertWritable(safety, continuation);
12836
- for (const job of jobs) {
13288
+ for (const job2 of jobs) {
12837
13289
  const seq2 = String(BigInt(this.state.committedSeq) + BigInt(rows.length) + 1n);
12838
- const value = await job.build(seq2);
13290
+ const value = await job2.build(seq2);
12839
13291
  if (value) {
12840
13292
  const row = {
12841
13293
  kind: "record",
@@ -12844,7 +13296,7 @@ class HistoryWriter {
12844
13296
  value
12845
13297
  };
12846
13298
  rows.push(row);
12847
- ownRows.set(job, row);
13299
+ ownRows.set(job2, row);
12848
13300
  }
12849
13301
  }
12850
13302
  if (rows.length) {
@@ -12900,8 +13352,8 @@ class HistoryWriter {
12900
13352
  this.assertReviews(rows);
12901
13353
  }, preparedIndex?.root, (encodedBatch) => {
12902
13354
  let markerBytes = encodedBatch.trailerBytes, index = 0;
12903
- for (const [job, row] of ownRows) {
12904
- this.charge(encodedBatch.rowBytes[index++] + markerBytes, this.rowRunId(row.value), job.control);
13355
+ for (const [job2, row] of ownRows) {
13356
+ this.charge(encodedBatch.rowBytes[index++] + markerBytes, this.rowRunId(row.value), job2.control);
12905
13357
  markerBytes = 0;
12906
13358
  }
12907
13359
  }, () => this.assertReviews(rows));
@@ -12916,6 +13368,7 @@ class HistoryWriter {
12916
13368
  seq: this.state.committedSeq
12917
13369
  }, rows, (bytes) => this.charge(bytes, null, true), preparedIndex);
12918
13370
  } catch {}
13371
+ this.rememberTaskFolds(rows, preparedIndex);
12919
13372
  }
12920
13373
  const batch = {
12921
13374
  committedSeq: this.state.committedSeq,
@@ -13040,14 +13493,14 @@ class HistoryWriter {
13040
13493
  this.fence();
13041
13494
  result = failure(true);
13042
13495
  }
13043
- for (const job of jobs) {
13496
+ for (const job2 of jobs) {
13044
13497
  if (result.status !== "committed") {
13045
- job.resolve(result);
13498
+ job2.resolve(result);
13046
13499
  continue;
13047
13500
  }
13048
- const v = ownRows.get(job)?.value;
13501
+ const v = ownRows.get(job2)?.value;
13049
13502
  const ends = v?.kind === "capture" || v?.kind === "owner" ? v.events.flatMap((e) => e.kind === "end" ? [e.detail] : []) : [];
13050
- job.resolve({
13503
+ job2.resolve({
13051
13504
  status: "committed",
13052
13505
  batch: {
13053
13506
  ...result.batch,
@@ -13093,7 +13546,7 @@ class HistoryWriter {
13093
13546
  reservedIdentity(commandId) {
13094
13547
  decodeId(commandId);
13095
13548
  try {
13096
- const bytes = this.files.readCommand(commandId, new HistoryReadBudget);
13549
+ const bytes = this.files.readCommand(commandId, HistoryReadBudget.authority());
13097
13550
  return bytes ? decodeAdmissionIntent(json(bytes, RECORD_BYTES)).identity : null;
13098
13551
  } catch {
13099
13552
  return null;
@@ -13111,7 +13564,7 @@ class HistoryWriter {
13111
13564
  async lookupCommitted(commandId) {
13112
13565
  decodeId(commandId);
13113
13566
  try {
13114
- const budget = new HistoryReadBudget;
13567
+ const budget = HistoryReadBudget.authority();
13115
13568
  const intent = this.files.readCommand(commandId, budget);
13116
13569
  const directoryExists = this.files.files.directory(join8(this.files.directory, "commands", commandId));
13117
13570
  const wanted = intent ? decodeAdmissionIntent(json(intent, RECORD_BYTES)) : null;
@@ -13381,6 +13834,29 @@ class HistoryWriter {
13381
13834
  return this.ownerValue(input, null, selected2.plan);
13382
13835
  }, lease.budget.retainedBytes + lease.budget.outputBytes, true, () => begun ? lease.settled() : lease.release());
13383
13836
  }
13837
+ rememberTaskFolds(rows, prepared) {
13838
+ if (!prepared)
13839
+ return;
13840
+ const tasks = new Set;
13841
+ for (const { value: v } of rows)
13842
+ if (v.kind === "owner" && v.events.some((e) => e.kind === "owner" && e.value.kind === "fact" && e.value.fact.kind === "publication-settled"))
13843
+ tasks.add(v.link.taskId);
13844
+ for (const taskId of tasks) {
13845
+ const head = prepared.heads.get(taskId);
13846
+ if (!head || head.seq !== this.state.committedSeq)
13847
+ continue;
13848
+ try {
13849
+ const checks = checkFold(this.files, taskId, head.seq, HistoryReadBudget.authority()).state();
13850
+ const run = latestTaskRun(this.files, taskId, head.seq, HistoryReadBudget.authority());
13851
+ const plan = planFold(this.files, taskId, head.seq, HistoryReadBudget.authority(), new HistoryNavigation(this.files), true).state();
13852
+ taskFoldMemory(this.files).remember(taskId, head, {
13853
+ checks,
13854
+ run,
13855
+ plan
13856
+ });
13857
+ } catch {}
13858
+ }
13859
+ }
13384
13860
  async publishTask(request) {
13385
13861
  request = structuredClone(request);
13386
13862
  decodeId(request.operationId);
@@ -13572,7 +14048,7 @@ class HistoryWriter {
13572
14048
  }
13573
14049
  }
13574
14050
  retainedRun(runId) {
13575
- const budget = new HistoryReadBudget;
14051
+ const budget = HistoryReadBudget.authority();
13576
14052
  const snapshot = this.state.committedSeq;
13577
14053
  this.navigation.snapshot(snapshot, budget);
13578
14054
  const batches = this.navigation.runBatches(runId, snapshot, budget);
@@ -13585,7 +14061,7 @@ class HistoryWriter {
13585
14061
  return state;
13586
14062
  }
13587
14063
  prior(matches) {
13588
- for (const batch of this.files.scan(emptyScan(), false, new HistoryReadBudget)) {
14064
+ for (const batch of this.files.scan(emptyScan(), false, HistoryReadBudget.authority())) {
13589
14065
  if (batch.rows.some((row) => matches(row.value)))
13590
14066
  return true;
13591
14067
  }
@@ -13757,7 +14233,7 @@ class HistoryWriter {
13757
14233
  recordId: randomUUID6(),
13758
14234
  version: 1,
13759
14235
  bytes: 0,
13760
- hash: createHash15("sha256"),
14236
+ hash: createHash16("sha256"),
13761
14237
  parts: 0,
13762
14238
  ended: null,
13763
14239
  committedSeq: null
@@ -13949,7 +14425,7 @@ class HistoryWriter {
13949
14425
  assertReview(input, fact) {
13950
14426
  let accepted2 = null;
13951
14427
  let changes = null;
13952
- for (const batch of this.files.scan(emptyScan(), false, new HistoryReadBudget))
14428
+ for (const batch of this.files.scan(emptyScan(), false, HistoryReadBudget.authority()))
13953
14429
  for (const row of batch.rows) {
13954
14430
  const v = row.value;
13955
14431
  if (v.kind === "admission" && v.intent.operationId === input.link.operationId)
@@ -14106,7 +14582,7 @@ function readOperationStateForPublication(files, operationId) {
14106
14582
  let publication = null;
14107
14583
  const state = new OperationState(operationId);
14108
14584
  const facts = [];
14109
- for (const batch of files.scan(emptyScan(), false, new HistoryReadBudget))
14585
+ for (const batch of files.scan(emptyScan(), false, HistoryReadBudget.authority()))
14110
14586
  for (const row of batch.rows) {
14111
14587
  const v = row.value;
14112
14588
  if (v.kind === "recovery" && intent)
@@ -14133,10 +14609,10 @@ function readOperationStateForPublication(files, operationId) {
14133
14609
  }
14134
14610
 
14135
14611
  // src/service-assets.ts
14136
- import { createHash as createHash16 } from "node:crypto";
14612
+ import { createHash as createHash17 } from "node:crypto";
14137
14613
  import { lstatSync as lstatSync7, readdirSync as readdirSync2 } from "node:fs";
14138
14614
  import { join as join9 } from "node:path";
14139
- import { fileURLToPath } from "node:url";
14615
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
14140
14616
  var ASSET_MEMBER_BYTES = 16 * 1024 * 1024;
14141
14617
  var ASSET_TOTAL_BYTES = 64 * 1024 * 1024;
14142
14618
  var fixed = new Set([
@@ -14209,7 +14685,7 @@ function decodeAssetManifest(input) {
14209
14685
  if ([...fixed].some((name) => !names.has(name)) || assets.filter((a) => font.test(a.name)).length !== 60 || assets.some((a) => new Set(a.references).size !== a.references.length || a.references.some((r) => !names.has(r))))
14210
14686
  throw new Error("asset-closure-refused");
14211
14687
  const sourceHash = hash2(o.sourceHash), contractHash = hash2(o.contractHash), buildId = hash2(o.buildId);
14212
- if (buildId !== createHash16("sha256").update(JSON.stringify({ sourceHash, contractHash })).digest("hex"))
14688
+ if (buildId !== createHash17("sha256").update(JSON.stringify({ sourceHash, contractHash })).digest("hex"))
14213
14689
  throw new Error("asset-identity-refused");
14214
14690
  return { schema: o.schema, sourceHash, contractHash, buildId, assets };
14215
14691
  }
@@ -14224,7 +14700,7 @@ function incarnation2(st) {
14224
14700
  st.ctimeMs
14225
14701
  ].join(":");
14226
14702
  }
14227
- function loadWebAssets(directory = fileURLToPath(new URL("../web/dist/", import.meta.url))) {
14703
+ function loadWebAssets(directory = fileURLToPath2(new URL("../web/dist/", import.meta.url))) {
14228
14704
  if (!safeDirectory(directory) || !safeDirectory(join9(directory, "assets")))
14229
14705
  throw new Error("assets-unavailable");
14230
14706
  const observed = new Map;
@@ -14232,7 +14708,7 @@ function loadWebAssets(directory = fileURLToPath(new URL("../web/dist/", import.
14232
14708
  observed.set(path, incarnation2(lstatSync7(path)));
14233
14709
  const read2 = (path, limit) => {
14234
14710
  const before = incarnation2(lstatSync7(path));
14235
- const body = readBounded(path, limit);
14711
+ const body = readBounded(path, limit, true);
14236
14712
  if (!body || before !== incarnation2(lstatSync7(path)))
14237
14713
  throw new Error("asset-read-changed");
14238
14714
  observed.set(path, before);
@@ -14248,7 +14724,7 @@ function loadWebAssets(directory = fileURLToPath(new URL("../web/dist/", import.
14248
14724
  throw new Error("asset-members-refused");
14249
14725
  const assets = manifest.assets.map((a) => {
14250
14726
  const body = read2(join9(directory, a.name), a.bytes);
14251
- if (!body || body.length !== a.bytes || createHash16("sha256").update(body).digest("hex") !== a.sha256)
14727
+ if (!body || body.length !== a.bytes || createHash17("sha256").update(body).digest("hex") !== a.sha256)
14252
14728
  throw new Error("asset-body-refused");
14253
14729
  return {
14254
14730
  path: a.name === "index.html" ? "/app" : `/${a.name}`,
@@ -14261,13 +14737,13 @@ function loadWebAssets(directory = fileURLToPath(new URL("../web/dist/", import.
14261
14737
  return {
14262
14738
  manifest,
14263
14739
  assets,
14264
- identity: createHash16("sha256").update(bytes).digest("hex")
14740
+ identity: createHash17("sha256").update(bytes).digest("hex")
14265
14741
  };
14266
14742
  }
14267
14743
 
14268
14744
  // src/service/auth.ts
14269
14745
  import {
14270
- createHash as createHash17,
14746
+ createHash as createHash18,
14271
14747
  randomBytes as randomBytes3,
14272
14748
  randomUUID as randomUUID7,
14273
14749
  timingSafeEqual
@@ -14331,8 +14807,8 @@ function token(value) {
14331
14807
  return result;
14332
14808
  }
14333
14809
  function sameSecret(candidate, expected) {
14334
- const a = createHash17("sha256").update(candidate).digest();
14335
- const b = createHash17("sha256").update(expected).digest();
14810
+ const a = createHash18("sha256").update(candidate).digest();
14811
+ const b = createHash18("sha256").update(expected).digest();
14336
14812
  return timingSafeEqual(a, b);
14337
14813
  }
14338
14814
  function buildTokenCookie(name, encodedValue, maxAge, secure) {
@@ -14652,7 +15128,10 @@ function safeChildEnv() {
14652
15128
  "TMPDIR",
14653
15129
  "LANG",
14654
15130
  "LC_ALL",
14655
- "PATHEXT"
15131
+ "PATHEXT",
15132
+ "ProgramFiles",
15133
+ "ProgramFiles(x86)",
15134
+ "ProgramW6432"
14656
15135
  ])
14657
15136
  if (process.env[key2] !== undefined)
14658
15137
  env[key2] = process.env[key2];
@@ -15014,8 +15493,8 @@ class ServiceCoordination {
15014
15493
  }
15015
15494
  };
15016
15495
  }
15017
- assertNamespace() {
15018
- if (!this.files.directory(this.directory))
15496
+ assertNamespace(walks) {
15497
+ if (!this.files.directory(this.directory, false, walks))
15019
15498
  refused();
15020
15499
  names(this.directory, [
15021
15500
  "format.json",
@@ -15028,14 +15507,15 @@ class ServiceCoordination {
15028
15507
  "host-config.json",
15029
15508
  "history-v2"
15030
15509
  ]);
15031
- this.files.directory(join11(this.directory, "history-v2"));
15510
+ this.files.directory(join11(this.directory, "history-v2"), false, walks);
15032
15511
  }
15033
15512
  selected() {
15034
- if (!this.files.directory(this.directory))
15513
+ const walks = new DirectoryWalks;
15514
+ if (!this.files.directory(this.directory, false, walks))
15035
15515
  return;
15036
- this.assertNamespace();
15037
- const manifest = this.files.read(this.path("format.json"));
15038
- const config = this.files.read(this.path("config.json"));
15516
+ this.assertNamespace(walks);
15517
+ const manifest = this.files.read(this.path("format.json"), undefined, walks);
15518
+ const config = this.files.read(this.path("config.json"), undefined, walks);
15039
15519
  if (!manifest || !config)
15040
15520
  refused();
15041
15521
  const f = closed(json(manifest), [
@@ -15049,13 +15529,13 @@ class ServiceCoordination {
15049
15529
  literal(f.stateHome, this.stateHome);
15050
15530
  literal(f.configIdentity, config.identity);
15051
15531
  const c = decodeOwnerConfig(json(config));
15052
- const hostConfig = this.files.read(this.path("host-config.json"));
15532
+ const hostConfig = this.files.read(this.path("host-config.json"), undefined, walks);
15053
15533
  if (hostConfig && hostConfig.identity !== c.policyIdentity)
15054
15534
  refused();
15055
- const registration = this.files.read(this.path("registration.json"));
15056
- const owner = this.files.read(this.path("owner.lock"));
15057
- const capability = this.files.read(this.path("local-capability.json"));
15058
- const checkout = this.files.read(this.store.lockPath, 8192);
15535
+ const registration = this.files.read(this.path("registration.json"), undefined, walks);
15536
+ const owner = this.files.read(this.path("owner.lock"), undefined, walks);
15537
+ const capability = this.files.read(this.path("local-capability.json"), undefined, walks);
15538
+ const checkout = this.files.read(this.store.lockPath, 8192, walks);
15059
15539
  const value = registration ? decodeRegistration(json(registration)) : undefined;
15060
15540
  if (value && (value.root !== this.store.root || value.stateHome !== this.stateHome || value.configIdentity !== config.identity || !endpointMatches(c.endpoint, value.endpoint)))
15061
15541
  refused();
@@ -15402,8 +15882,8 @@ class OwnerLease {
15402
15882
  assert() {
15403
15883
  if (this.released || this.selected.value.pid !== process.pid || this.selected.value.host !== hostname2())
15404
15884
  throw new ContractError("conflict");
15405
- const c = this.coordination;
15406
- c.assertNamespace();
15885
+ const c = this.coordination, walks = new DirectoryWalks;
15886
+ c.assertNamespace(walks);
15407
15887
  for (const [name, snapshot] of [
15408
15888
  ["config.json", this.selected.config],
15409
15889
  ["host-config.json", this.selected.hostConfig],
@@ -15412,8 +15892,8 @@ class OwnerLease {
15412
15892
  ["owner.lock", this.selected.owner],
15413
15893
  ["local-capability.json", this.selected.capability]
15414
15894
  ])
15415
- c.files.assert(c.path(name), snapshot);
15416
- c.files.assert(c.store.lockPath, this.selected.checkout, 8192);
15895
+ c.files.assert(c.path(name), snapshot, undefined, walks);
15896
+ c.files.assert(c.store.lockPath, this.selected.checkout, 8192, walks);
15417
15897
  c.store.assertOwner(this.checkout);
15418
15898
  }
15419
15899
  phase(phase) {
@@ -15448,7 +15928,7 @@ class OwnerLease {
15448
15928
  }
15449
15929
 
15450
15930
  // src/service/http.ts
15451
- import { createHash as createHash18, randomUUID as randomUUID9 } from "node:crypto";
15931
+ import { createHash as createHash19, randomUUID as randomUUID9 } from "node:crypto";
15452
15932
  import {
15453
15933
  createServer
15454
15934
  } from "node:http";
@@ -15732,7 +16212,7 @@ class ServiceHttpServer {
15732
16212
  this.assets.set(asset.path, {
15733
16213
  ...asset,
15734
16214
  bytes: Uint8Array.from(asset.bytes),
15735
- etag: `"${createHash18("sha256").update(asset.bytes).digest("hex")}"`
16215
+ etag: `"${createHash19("sha256").update(asset.bytes).digest("hex")}"`
15736
16216
  });
15737
16217
  }
15738
16218
  this.server = createServer({
@@ -16241,7 +16721,7 @@ Content-Length: 0\r
16241
16721
  res.setHeader("Content-Type", operation === "download" && request.format === "markdown" ? "text/markdown; charset=utf-8" : "application/x-ndjson; charset=utf-8");
16242
16722
  res.setHeader("Content-Disposition", `attachment; filename="cadence-export.${operation === "download" && request.format === "markdown" ? "md" : "ndjson"}"`);
16243
16723
  let trailer;
16244
- const digest = createHash18("sha256");
16724
+ const digest = createHash19("sha256");
16245
16725
  const write = async (frame) => {
16246
16726
  const data = Buffer.from(operation === "download" ? encodeExportFrame(frame, request.format) : `${canonicalJson(frame)}
16247
16727
  `);
@@ -16552,9 +17032,6 @@ class OwnerModelChoice {
16552
17032
  }
16553
17033
  }
16554
17034
 
16555
- // src/service/model-context.ts
16556
- import { randomUUID as randomUUID11 } from "node:crypto";
16557
-
16558
17035
  // src/service/model-config.ts
16559
17036
  import { lstatSync as lstatSync8, realpathSync as realpathSync4 } from "node:fs";
16560
17037
  import { dirname as dirname9, isAbsolute as isAbsolute6, join as join12, resolve as resolve3 } from "node:path";
@@ -16633,7 +17110,7 @@ function piSource(agentDir, home, cwd, env) {
16633
17110
  const directory = join12(realpathSync4(dirname9(selected2)), selected2.slice(dirname9(selected2).length + 1));
16634
17111
  if (!lstatSync8(directory).isDirectory() || lstatSync8(directory).isSymbolicLink())
16635
17112
  refuse2();
16636
- const files = new PrivateFiles(directory);
17113
+ const files = new PrivateFiles(directory, false);
16637
17114
  if (!files.directory(directory))
16638
17115
  refuse2();
16639
17116
  return {
@@ -16723,7 +17200,8 @@ function compat(value) {
16723
17200
  "forceAdaptiveThinking",
16724
17201
  "allowEmptySignature",
16725
17202
  "supportsStrictTools",
16726
- "supportsToolReferences"
17203
+ "supportsToolReferences",
17204
+ "supportsMidConvoSystemMessages"
16727
17205
  ];
16728
17206
  const enums = {
16729
17207
  maxTokensField: ["max_completion_tokens", "max_tokens"],
@@ -16921,6 +17399,11 @@ function modelJson(snapshot) {
16921
17399
  return parseWireJson(normalized, CONFIG_BYTES);
16922
17400
  }
16923
17401
  function readPiSnapshot(source) {
17402
+ const { auth, credentials } = readPiAuth(source);
17403
+ const { models, providers } = readPiModels(source);
17404
+ return { auth, models, credentials, providers };
17405
+ }
17406
+ function readPiAuth(source) {
16924
17407
  const auth = source.files.read(source.authPath, CONFIG_BYTES);
16925
17408
  if (!auth)
16926
17409
  refuse2();
@@ -16944,6 +17427,9 @@ function readPiSnapshot(source) {
16944
17427
  refuse2();
16945
17428
  credentials[provider] = credential;
16946
17429
  }
17430
+ return { auth, credentials };
17431
+ }
17432
+ function readPiModels(source) {
16947
17433
  const models = source.files.read(source.modelsPath, CONFIG_BYTES);
16948
17434
  const providers = Object.create(null);
16949
17435
  if (models) {
@@ -16987,7 +17473,7 @@ function readPiSnapshot(source) {
16987
17473
  providers[id] = provider;
16988
17474
  }
16989
17475
  }
16990
- return { auth, models, credentials, providers };
17476
+ return { models, providers };
16991
17477
  }
16992
17478
  function sourceIdentity(snapshot, provider) {
16993
17479
  const credential = snapshot.credentials[provider];
@@ -17074,6 +17560,7 @@ function clonePrivate(value) {
17074
17560
  }
17075
17561
 
17076
17562
  // src/service/model-context.ts
17563
+ import { randomUUID as randomUUID11 } from "node:crypto";
17077
17564
  function committed(result) {
17078
17565
  if (result.status !== "committed")
17079
17566
  throw new ContractError("recording-exhausted");
@@ -18117,6 +18604,28 @@ function decodeHostInput(value) {
18117
18604
  environmentToken
18118
18605
  };
18119
18606
  }
18607
+ class StartupRefusal extends ContractError {
18608
+ stage;
18609
+ quarantined;
18610
+ constructor(code, stage, quarantined = false) {
18611
+ super(code);
18612
+ this.stage = stage;
18613
+ this.quarantined = quarantined;
18614
+ }
18615
+ }
18616
+ function startupReport(error) {
18617
+ if (error instanceof StartupRefusal)
18618
+ return {
18619
+ status: error.quarantined ? "quarantined" : "refused",
18620
+ error: error.code,
18621
+ stage: error.stage
18622
+ };
18623
+ return {
18624
+ status: "refused",
18625
+ error: error instanceof ContractError ? error.code : "unavailable",
18626
+ stage: "service"
18627
+ };
18628
+ }
18120
18629
 
18121
18630
  // src/service/task-actions.ts
18122
18631
  import { randomUUID as randomUUID15 } from "node:crypto";
@@ -18124,13 +18633,13 @@ import { realpathSync as realpathSync5 } from "node:fs";
18124
18633
 
18125
18634
  // src/task-diff.ts
18126
18635
  import { spawnSync as spawnSync2 } from "node:child_process";
18127
- import { createHash as createHash19 } from "node:crypto";
18636
+ import { createHash as createHash20 } from "node:crypto";
18128
18637
  import { lstatSync as lstatSync9, readdirSync as readdirSync3, readlinkSync } from "node:fs";
18129
18638
  import { join as join14, relative as relative5 } from "node:path";
18130
18639
  var admin = (path) => path === ".cadence/assistant/tasks" || path.startsWith(".cadence/assistant/tasks/");
18131
18640
  function captureChanges(root) {
18132
18641
  const deadline = Date.now() + 3000;
18133
- const hash3 = createHash19("sha256");
18642
+ const hash3 = createHash20("sha256");
18134
18643
  const parts = [];
18135
18644
  let complete = true;
18136
18645
  const limits = [];
@@ -18187,7 +18696,7 @@ function captureChanges(root) {
18187
18696
  fileBytes += data2.length;
18188
18697
  add(name, data2);
18189
18698
  const binary = data2.includes(0);
18190
- parts.push(`${name} (${binary ? "binary" : "text"}, ${data2.length} bytes, sha256 ${createHash19("sha256").update(data2).digest("hex")})`);
18699
+ parts.push(`${name} (${binary ? "binary" : "text"}, ${data2.length} bytes, sha256 ${createHash20("sha256").update(data2).digest("hex")})`);
18191
18700
  } catch {
18192
18701
  complete = false;
18193
18702
  limits.push(`Unreadable/changing: ${name}`);
@@ -18867,33 +19376,6 @@ class ActionAdmission {
18867
19376
  // src/service/graph.ts
18868
19377
  import { createHash as createHash21 } from "node:crypto";
18869
19378
  import { join as join15 } from "node:path";
18870
-
18871
- // src/agent-registry.ts
18872
- import { createHash as createHash20 } from "node:crypto";
18873
- import { fileURLToPath as fileURLToPath2 } from "node:url";
18874
- var AGENT_FILES = [
18875
- "design-explorer.md",
18876
- "implementation-worker.md",
18877
- "diagnosis-worker.md"
18878
- ];
18879
- function loadAgentDefinitions() {
18880
- return AGENT_FILES.map((fileName) => {
18881
- const path = fileURLToPath2(new URL(`../agents/${fileName}`, import.meta.url));
18882
- const data2 = readBounded(path, 32768);
18883
- if (!data2)
18884
- throw new Error("Missing package Agent");
18885
- return {
18886
- role: fileName.replace(/\.md$/, ""),
18887
- fileName,
18888
- path,
18889
- sha256: createHash20("sha256").update(data2).digest("hex"),
18890
- bytes: data2.length,
18891
- content: data2.toString("utf8")
18892
- };
18893
- });
18894
- }
18895
-
18896
- // src/service/graph.ts
18897
19379
  var hash3 = (text2) => createHash21("sha256").update(text2).digest("hex");
18898
19380
 
18899
19381
  class GraphContext {
@@ -19040,6 +19522,7 @@ import { randomUUID as randomUUID14 } from "node:crypto";
19040
19522
 
19041
19523
  // src/subagent-process.ts
19042
19524
  import { spawn } from "node:child_process";
19525
+ import { join as join16 } from "node:path";
19043
19526
  import { StringDecoder } from "node:string_decoder";
19044
19527
  import { fileURLToPath as fileURLToPath3 } from "node:url";
19045
19528
  // src/worker-capture.mjs
@@ -19622,6 +20105,15 @@ class WorkerRecording {
19622
20105
  }
19623
20106
 
19624
20107
  // src/subagent-process.ts
20108
+ function terminateWindowsTree(pid, fallback) {
20109
+ const root = process.env.SystemRoot;
20110
+ const taskkill = spawn(root ? join16(root, "System32", "taskkill.exe") : "taskkill.exe", ["/F", "/T", "/PID", String(pid)], { stdio: "ignore", windowsHide: true });
20111
+ taskkill.once("error", fallback);
20112
+ taskkill.once("close", (code) => {
20113
+ if (code !== 0)
20114
+ fallback();
20115
+ });
20116
+ }
19625
20117
  function runProcess(spec) {
19626
20118
  const started = Date.now();
19627
20119
  const early = (outcome, text2) => ({
@@ -19739,6 +20231,8 @@ function runProcess(spec) {
19739
20231
  return;
19740
20232
  if (process.platform !== "win32" && child.pid)
19741
20233
  process.kill(-child.pid, signal);
20234
+ else if (child.pid && child.exitCode === null && child.signalCode === null)
20235
+ terminateWindowsTree(child.pid, () => child.kill(signal));
19742
20236
  else
19743
20237
  child.kill(signal);
19744
20238
  } catch {}
@@ -20706,8 +21200,8 @@ class TaskScheduler {
20706
21200
  this.pumping = false;
20707
21201
  }
20708
21202
  }
20709
- current(e, n) {
20710
- return !this.closed && !e.reason && !n.abort.signal.aborted && this.options.owner.state === "idle" && this.owned() && e.input.current() && e.input.sources.current() && canonicalJson(e.input.observe(false).observation) === canonicalJson(e.expectedObservation);
21203
+ current(e, n, changes = false) {
21204
+ return !this.closed && !e.reason && !n.abort.signal.aborted && this.options.owner.state === "idle" && this.owned() && e.input.current() && e.input.sources.current() && (!changes || canonicalJson(e.input.observe(false).observation) === canonicalJson(e.expectedObservation));
20711
21205
  }
20712
21206
  async execute(e, n) {
20713
21207
  if (!n.assignment || !n.result || e.input.kind === "check")
@@ -20734,7 +21228,7 @@ class TaskScheduler {
20734
21228
  reservationId: n.childRunId,
20735
21229
  lane,
20736
21230
  state: "held",
20737
- checkoutGeneration: e.input.observe(false).observation.checkoutGeneration
21231
+ checkoutGeneration: e.input.generation()
20738
21232
  });
20739
21233
  held = true;
20740
21234
  if (!this.current(e, n)) {
@@ -20744,7 +21238,7 @@ class TaskScheduler {
20744
21238
  const observed = e.input.observe(false), before = observed.observation;
20745
21239
  const baseline = await this.changes(e, "before", observed);
20746
21240
  e.baseline ??= baseline;
20747
- if (!this.current(e, n)) {
21241
+ if (!this.current(e, n) || canonicalJson(before) !== canonicalJson(e.expectedObservation)) {
20748
21242
  await this.close(e, e.reason ?? "stale-dependency");
20749
21243
  return;
20750
21244
  }
@@ -20790,7 +21284,7 @@ class TaskScheduler {
20790
21284
  });
20791
21285
  originalAuth = auth.settled;
20792
21286
  const prepared = await auth.result;
20793
- if (prepared.status !== "prepared" || !this.current(e, n)) {
21287
+ if (prepared.status !== "prepared" || !this.current(e, n, true)) {
20794
21288
  await this.close(e, e.reason ?? "failed");
20795
21289
  await this.fact(e, {
20796
21290
  kind: "assignment-state",
@@ -20860,7 +21354,7 @@ class TaskScheduler {
20860
21354
  reservationId: n.childRunId,
20861
21355
  lane,
20862
21356
  state: "released",
20863
- checkoutGeneration: e.input.observe(false).observation.checkoutGeneration
21357
+ checkoutGeneration: e.input.generation()
20864
21358
  });
20865
21359
  this.holders.delete(n.childRunId);
20866
21360
  n.state = "done";
@@ -20908,7 +21402,7 @@ class TaskScheduler {
20908
21402
  if (this.current(e, n) && input.definitionCurrent()) {
20909
21403
  const output = recording;
20910
21404
  const original = Promise.resolve().then(async () => {
20911
- if (!this.current(e, n) || !input.definitionCurrent())
21405
+ if (!this.current(e, n, true) || !input.definitionCurrent())
20912
21406
  return native;
20913
21407
  await runCheck(input.check, input.cwd, n.abort.signal, {
20914
21408
  environment: input.environment,
@@ -20940,7 +21434,7 @@ class TaskScheduler {
20940
21434
  }
20941
21435
  };
20942
21436
  const observed = input.observe(false).observation;
20943
- const after = held ? input.observe(true).observation : observed;
21437
+ const after = held ? { ...observed, checkoutGeneration: input.advance() } : observed;
20944
21438
  const ownAdvance = held && observed.checkoutGeneration === before.checkoutGeneration ? {
20945
21439
  operationId: e.link.operationId,
20946
21440
  reservationId: checkId,
@@ -21107,6 +21601,14 @@ class TaskScheduler {
21107
21601
 
21108
21602
  // src/service/task-actions.ts
21109
21603
  var actionOwners = new WeakSet;
21604
+ function snapshotReads() {
21605
+ const kept = new Map;
21606
+ return (key2, read2) => {
21607
+ if (!kept.has(key2))
21608
+ kept.set(key2, read2());
21609
+ return kept.get(key2);
21610
+ };
21611
+ }
21110
21612
 
21111
21613
  class TaskActions {
21112
21614
  options;
@@ -21586,14 +22088,16 @@ class TaskActions {
21586
22088
  assertCurrent();
21587
22089
  return checks;
21588
22090
  }
21589
- obligations(version, snapshot) {
22091
+ obligations(version, snapshot, reads) {
21590
22092
  if (!version.task)
21591
22093
  throw new ContractError("stale");
21592
- return this.obligationsFor(version.task.id, version.task.kind, { status: "available", version }, snapshot);
22094
+ return this.obligationsFor(version.task.id, version.task.kind, { status: "available", version }, snapshot, reads);
21593
22095
  }
21594
- obligationsFor(taskId, kind, context, snapshot) {
22096
+ obligationsFor(taskId, kind, context, snapshot, reads) {
21595
22097
  const configured = this.checks(taskId);
21596
- const state = currentChecks(this.options.host.reader.files, taskId, kind === "code" ? configured : { revision: configured.revision, selections: [] }, context, snapshot);
22098
+ const selections = kind === "code" ? configured : { revision: configured.revision, selections: [] };
22099
+ const fold = () => currentChecks(this.options.host.reader.files, taskId, selections, context, snapshot);
22100
+ const state = reads ? reads(`checks:${canonicalJson({ taskId, selections, context, snapshot })}`, fold) : fold();
21597
22101
  if (canonicalJson(configured) !== canonicalJson(this.checks(taskId)))
21598
22102
  throw new ContractError("stale");
21599
22103
  if (context.status !== "available")
@@ -21805,6 +22309,8 @@ class TaskActions {
21805
22309
  return false;
21806
22310
  }
21807
22311
  },
22312
+ generation: () => this.generation,
22313
+ advance: () => ++this.generation,
21808
22314
  observe: (advance) => {
21809
22315
  if (advance)
21810
22316
  this.generation++;
@@ -21831,13 +22337,14 @@ class TaskActions {
21831
22337
  });
21832
22338
  accepted2.activate(work);
21833
22339
  }
21834
- reviewObservation(runId, version, snapshot = this.options.host.writer.committedSeq) {
22340
+ reviewObservation(runId, version, snapshot = this.options.host.writer.committedSeq, reads) {
21835
22341
  if (!version.task)
21836
22342
  throw new ContractError("stale");
21837
22343
  const h = this.options.host, taskId = version.task.id;
21838
- const selection = h.reader.reviewSelection(taskId, runId, snapshot);
22344
+ const select = () => h.reader.reviewSelection(taskId, runId, snapshot);
22345
+ const selection = reads ? reads(`selection:${taskId}:${runId}:${snapshot}`, select) : select();
21839
22346
  const { run } = selection;
21840
- const state = this.obligations(version, snapshot);
22347
+ const state = this.obligations(version, snapshot, reads);
21841
22348
  const current2 = {
21842
22349
  ...state,
21843
22350
  checks: state.checks,
@@ -21869,6 +22376,7 @@ class TaskActions {
21869
22376
  const currentGraph = graph?.graph ?? (this.options.choice ? this.defaultGraph(selected2.record, selected2.identity, expected.model) : null);
21870
22377
  if ((currentGraph ? hash(canonicalJson(currentGraph)) : null) !== task.graphIdentity || (currentGraph?.revision ?? null) !== task.graphRevision)
21871
22378
  throw new ContractError("stale");
22379
+ const reads = snapshotReads();
21872
22380
  const validate = (published) => {
21873
22381
  this.assertChoice(expected);
21874
22382
  held.assertHeld();
@@ -21878,14 +22386,14 @@ class TaskActions {
21878
22386
  const changes2 = captureChanges(h.coordination.store.root);
21879
22387
  if (changes2.fingerprint !== expected.observation.identity || changes2.complete !== expected.observation.complete)
21880
22388
  throw new ContractError("stale");
21881
- const proven = h.reader.publishedPlan(task.id, snapshot, {
22389
+ const proven = reads(`plan:${task.id}:${snapshot}`, () => h.reader.publishedPlan(task.id, snapshot, {
21882
22390
  revision: selected2.record.revision,
21883
22391
  identity: selected2.identity,
21884
22392
  plan: selected2.record.plan
21885
- });
22393
+ }));
21886
22394
  if ((proven?.reference.sha256 ?? (currentGraph ? hash(canonicalJson(currentGraph)) : null)) !== task.graphIdentity)
21887
22395
  throw new ContractError("stale");
21888
- const current3 = this.reviewObservation(action.runId, expected, snapshot);
22396
+ const current3 = this.reviewObservation(action.runId, expected, snapshot, reads);
21889
22397
  if (current3.checks.revision !== action.checksRevision || current3.issues.length && !action.acceptIssues)
21890
22398
  throw new ContractError("stale");
21891
22399
  return current3;
@@ -22077,7 +22585,7 @@ function legacy(store, budget, reader) {
22077
22585
  decodeId(entry.name);
22078
22586
  if (created.has(entry.name))
22079
22587
  continue;
22080
- const path = join16(store.tasks, entry.name, "task.json");
22588
+ const path = join17(store.tasks, entry.name, "task.json");
22081
22589
  const size = lstatSync10(path).size;
22082
22590
  if (size > 128 * 1024)
22083
22591
  throw new ContractError("corrupt-selected");
@@ -22092,12 +22600,23 @@ function legacy(store, budget, reader) {
22092
22600
  }
22093
22601
  return records;
22094
22602
  }
22603
+ function refuseAt(stage, step) {
22604
+ try {
22605
+ return step();
22606
+ } catch (error2) {
22607
+ throw new StartupRefusal(error2 instanceof ContractError ? error2.code : "unavailable", stage);
22608
+ }
22609
+ }
22095
22610
  async function createServiceHost(value, sourceBuild) {
22096
22611
  const embedded = "e7e5248f8273a373827ac70d2d8be1d5aaea1e329379e59e4947fc10f82be818";
22097
22612
  if (embedded && sourceBuild)
22098
22613
  throw new ContractError("unsupported-context");
22099
22614
  const contractHash = decodeHash(embedded ?? sourceBuild?.contractHash);
22100
- const web = loadWebAssets(sourceBuild?.webDirectory);
22615
+ const web = refuseAt("package", () => {
22616
+ if (!sourceBuild?.deferActions)
22617
+ loadAgentDefinitions();
22618
+ return loadWebAssets(sourceBuild?.webDirectory);
22619
+ });
22101
22620
  const build2 = {
22102
22621
  contractHash,
22103
22622
  assets: {
@@ -22124,12 +22643,17 @@ async function createServiceHost(value, sourceBuild) {
22124
22643
  throw new ContractError("corrupt-selected");
22125
22644
  history.assertWritable();
22126
22645
  }
22646
+ const home = launch.HOME ?? launch.USERPROFILE ?? input.config.agentDir;
22647
+ const pi = refuseAt("pi-agent-directory", () => piSource(input.config.agentDir, home, input.config.cwd, launch));
22648
+ refuseAt("pi-auth", () => readPiAuth(pi));
22649
+ refuseAt("pi-models", () => readPiModels(pi));
22127
22650
  const http = new ServiceHttpServer({
22128
22651
  policy: input.config.http,
22129
22652
  assets: web.assets
22130
22653
  });
22131
22654
  const endpoint2 = await http.listen();
22132
22655
  let owner;
22656
+ let stage = "service";
22133
22657
  try {
22134
22658
  const resolved = {
22135
22659
  ...input.config,
@@ -22179,7 +22703,7 @@ async function createServiceHost(value, sourceBuild) {
22179
22703
  owner,
22180
22704
  accounting: writer.auth,
22181
22705
  network,
22182
- home: launch.HOME ?? launch.USERPROFILE ?? input.config.agentDir,
22706
+ home,
22183
22707
  agentDir: input.config.agentDir,
22184
22708
  cwd: input.config.cwd,
22185
22709
  environment: launch
@@ -22320,9 +22844,11 @@ async function createServiceHost(value, sourceBuild) {
22320
22844
  owner.quarantine();
22321
22845
  throw new ContractError("quarantine");
22322
22846
  }
22847
+ stage = "pi-runtime";
22323
22848
  const loading = models.load();
22324
22849
  owner.trackAuth(loading);
22325
22850
  await loading;
22851
+ stage = "service";
22326
22852
  lease.assert();
22327
22853
  if (owner.state !== "idle")
22328
22854
  throw new ContractError("quarantine");
@@ -22353,7 +22879,7 @@ async function createServiceHost(value, sourceBuild) {
22353
22879
  } catch {}
22354
22880
  } else
22355
22881
  await http.close();
22356
- throw new ContractError(error2 instanceof ContractError ? error2.code : "unavailable");
22882
+ throw new StartupRefusal(error2 instanceof ContractError ? error2.code : "unavailable", stage, !!owner);
22357
22883
  }
22358
22884
  }
22359
22885
 
@@ -22375,7 +22901,9 @@ try {
22375
22901
  const host = await createServiceHost(input);
22376
22902
  process.stdout.write(`${JSON.stringify({ status: "ready", epoch: host.owner.lease.registration.epoch })}
22377
22903
  `);
22378
- } catch {
22904
+ } catch (error2) {
22905
+ process.stdout.write(`${JSON.stringify(startupReport(error2))}
22906
+ `);
22379
22907
  process.stderr.write(`{"error":"service-host-refused"}
22380
22908
  `);
22381
22909
  process.exitCode = 1;