@githolon/dsl 0.112.7 → 0.112.11

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.
@@ -6873,10 +6873,10 @@ function nodeCustodyFilesystem(directory2) {
6873
6873
  init_define_NOMOS_KERNEL_RELEASE();
6874
6874
  import { TextDecoder as TextDecoder2 } from "node:util";
6875
6875
  function kernelWasiStdio(memory, stderr, writeFile2, stderrPartial) {
6876
- const decoder4 = new TextDecoder2();
6876
+ const decoder5 = new TextDecoder2();
6877
6877
  let pending = "";
6878
6878
  const accept = (bytes) => {
6879
- pending += decoder4.decode(bytes, { stream: true });
6879
+ pending += decoder5.decode(bytes, { stream: true });
6880
6880
  for (; ; ) {
6881
6881
  const newline = pending.indexOf("\n");
6882
6882
  if (newline >= 0 && newline < 16384) {
@@ -10307,13 +10307,13 @@ var Expression = class {
10307
10307
  }
10308
10308
  }
10309
10309
  }
10310
- let namespace = void 0;
10310
+ let namespace2 = void 0;
10311
10311
  let tagAndPosition = withoutBrackets;
10312
10312
  if (withoutBrackets.includes("::")) {
10313
10313
  const nsIndex = withoutBrackets.indexOf("::");
10314
- namespace = withoutBrackets.substring(0, nsIndex).trim();
10314
+ namespace2 = withoutBrackets.substring(0, nsIndex).trim();
10315
10315
  tagAndPosition = withoutBrackets.substring(nsIndex + 2).trim();
10316
- if (!namespace) {
10316
+ if (!namespace2) {
10317
10317
  throw new Error(`Invalid namespace in pattern: ${part}`);
10318
10318
  }
10319
10319
  }
@@ -10337,8 +10337,8 @@ var Expression = class {
10337
10337
  throw new Error(`Invalid segment pattern: ${part}`);
10338
10338
  }
10339
10339
  segment.tag = tag;
10340
- if (namespace) {
10341
- segment.namespace = namespace;
10340
+ if (namespace2) {
10341
+ segment.namespace = namespace2;
10342
10342
  }
10343
10343
  if (bracketContent) {
10344
10344
  if (bracketContent.includes("=")) {
@@ -10728,7 +10728,7 @@ var Matcher = class {
10728
10728
  * @param {Object|null} [options=null]
10729
10729
  * @param {string[]} [options.keep] - Names of attributes (from attrValues)
10730
10730
  */
10731
- push(tagName, attrValues = null, namespace = null, options = null) {
10731
+ push(tagName, attrValues = null, namespace2 = null, options = null) {
10732
10732
  this._pathStringCache = null;
10733
10733
  if (this.path.length > 0) {
10734
10734
  this.path[this.path.length - 1].values = void 0;
@@ -10739,7 +10739,7 @@ var Matcher = class {
10739
10739
  level = { counts: /* @__PURE__ */ new Map(), total: 0 };
10740
10740
  this.siblingStacks[currentLevel] = level;
10741
10741
  }
10742
- const siblingKey = namespace ? `${namespace}:${tagName}` : tagName;
10742
+ const siblingKey = namespace2 ? `${namespace2}:${tagName}` : tagName;
10743
10743
  const counter = level.counts.get(siblingKey) || 0;
10744
10744
  const position = level.total;
10745
10745
  level.counts.set(siblingKey, counter + 1);
@@ -10749,8 +10749,8 @@ var Matcher = class {
10749
10749
  position,
10750
10750
  counter
10751
10751
  };
10752
- if (namespace !== null && namespace !== void 0) {
10753
- node.namespace = namespace;
10752
+ if (namespace2 !== null && namespace2 !== void 0) {
10753
+ node.namespace = namespace2;
10754
10754
  }
10755
10755
  if (attrValues !== null && attrValues !== void 0) {
10756
10756
  node.values = attrValues;
@@ -12133,10 +12133,10 @@ var parseXml = function(xmlData) {
12133
12133
  }
12134
12134
  let prefixedAttrs = null;
12135
12135
  let rawAttrs = {};
12136
- let namespace = void 0;
12137
- namespace = extractNamespace(rawTagName);
12136
+ let namespace2 = void 0;
12137
+ namespace2 = extractNamespace(rawTagName);
12138
12138
  if (tagName !== xmlObj.tagname) {
12139
- this.matcher.push(tagName, {}, namespace);
12139
+ this.matcher.push(tagName, {}, namespace2);
12140
12140
  }
12141
12141
  if (tagName !== tagExp && attrExpPresent) {
12142
12142
  prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);
@@ -13361,17 +13361,17 @@ function attributes(capability) {
13361
13361
  }
13362
13362
  return result;
13363
13363
  }
13364
- function capabilityByNamespace(capabilities, namespace) {
13365
- const matches = capabilities.filter((capability) => capability.namespace === namespace);
13364
+ function capabilityByNamespace(capabilities, namespace2) {
13365
+ const matches = capabilities.filter((capability) => capability.namespace === namespace2);
13366
13366
  if (matches.length !== 1) {
13367
- throw new Error(`OSGi resource needs exactly one ${namespace} capability`);
13367
+ throw new Error(`OSGi resource needs exactly one ${namespace2} capability`);
13368
13368
  }
13369
13369
  return matches[0];
13370
13370
  }
13371
- function optionalCapabilityByNamespace(capabilities, namespace) {
13372
- const matches = capabilities.filter((capability) => capability.namespace === namespace);
13371
+ function optionalCapabilityByNamespace(capabilities, namespace2) {
13372
+ const matches = capabilities.filter((capability) => capability.namespace === namespace2);
13373
13373
  if (matches.length > 1) {
13374
- throw new Error(`OSGi resource needs at most one ${namespace} capability`);
13374
+ throw new Error(`OSGi resource needs at most one ${namespace2} capability`);
13375
13375
  }
13376
13376
  return matches[0];
13377
13377
  }
@@ -14154,7 +14154,8 @@ var KernelPort = class {
14154
14154
  }
14155
14155
  }));
14156
14156
  if (response.outcome.case === "failure") {
14157
- throw new Error(`${response.outcome.value.code}: ${response.outcome.value.message}`);
14157
+ throwUnnamedHistoryRequirement(response.outcome.value, mount.workspace);
14158
+ throwKernelFailure(response.outcome.value);
14158
14159
  }
14159
14160
  if (response.outcome.case !== "outbox") {
14160
14161
  throw new Error(`kernel returned '${response.outcome.case || "empty"}' while reading correspondence`);
@@ -14183,7 +14184,8 @@ var KernelPort = class {
14183
14184
  }
14184
14185
  }));
14185
14186
  if (response.outcome.case === "failure") {
14186
- throw new Error(`${response.outcome.value.code}: ${response.outcome.value.message}`);
14187
+ throwUnnamedHistoryRequirement(response.outcome.value, mount.workspace);
14188
+ throwKernelFailure(response.outcome.value);
14187
14189
  }
14188
14190
  if (response.outcome.case !== "outbox") {
14189
14191
  throw new Error(
@@ -17222,19 +17224,19 @@ function base64(bytes) {
17222
17224
  // ../src/runtime/workspace_naming.ts
17223
17225
  init_define_NOMOS_KERNEL_RELEASE();
17224
17226
  var WORKSPACE_NAME_SEP = "";
17225
- var keyedBirthNameOf = (namespace, key) => `${namespace}-${sha256Hex(new TextEncoder().encode(namespace + WORKSPACE_NAME_SEP + key)).slice(0, 40)}`;
17227
+ var keyedBirthNameOf = (namespace2, key) => `${namespace2}-${sha256Hex(new TextEncoder().encode(namespace2 + WORKSPACE_NAME_SEP + key)).slice(0, 40)}`;
17226
17228
  var RECEIVING_GROUP_NAME_PREFIX = "nomos-rg-";
17227
17229
  var receivingGroupNameOf = (principal) => `${RECEIVING_GROUP_NAME_PREFIX}${sha256Hex(new TextEncoder().encode(principal)).slice(0, 40)}`;
17228
- var isKeyedBirthOf = (namespace, workspace) => workspace.startsWith(`${namespace}-`);
17230
+ var isKeyedBirthOf = (namespace2, workspace) => workspace.startsWith(`${namespace2}-`);
17229
17231
  var WORKSPACE_NAME_REQUEST_INVALID = "nomos.runtime.workspace_name_request_invalid";
17230
17232
  function resolveWorkspaceName(command) {
17231
17233
  switch (command.birth.case) {
17232
17234
  case "keyed": {
17233
- const { namespace, key } = command.birth.value;
17234
- if (namespace.length === 0 || key.length === 0) {
17235
+ const { namespace: namespace2, key } = command.birth.value;
17236
+ if (namespace2.length === 0 || key.length === 0) {
17235
17237
  throw new Error(`${WORKSPACE_NAME_REQUEST_INVALID}: a keyed birth name needs both its namespace and its key`);
17236
17238
  }
17237
- return create(ResolvedWorkspaceNameSchema, { workspace: keyedBirthNameOf(namespace, key) });
17239
+ return create(ResolvedWorkspaceNameSchema, { workspace: keyedBirthNameOf(namespace2, key) });
17238
17240
  }
17239
17241
  case "receivingGroup": {
17240
17242
  const { principal } = command.birth.value;
@@ -19285,6 +19287,136 @@ var ModelAdoptionDesk = class {
19285
19287
  }
19286
19288
  };
19287
19289
 
19290
+ // ../src/runtime/custody_receipts.ts
19291
+ init_define_NOMOS_KERNEL_RELEASE();
19292
+
19293
+ // ../src/runtime/peer_desk_store.ts
19294
+ init_define_NOMOS_KERNEL_RELEASE();
19295
+ var recordId = (namespace2, key) => `${namespace2.length}:${namespace2}${key}`;
19296
+ var copy2 = (record) => ({
19297
+ ...record,
19298
+ value: record.value.slice()
19299
+ });
19300
+ var MemoryPeerDeskStore = class {
19301
+ #records = /* @__PURE__ */ new Map();
19302
+ list(namespace2) {
19303
+ return [...this.#records.values()].filter((record) => record.namespace === namespace2).sort((left, right) => left.key.localeCompare(right.key)).map(copy2);
19304
+ }
19305
+ compareExchange(namespace2, key, expectedRevision, value) {
19306
+ const id = recordId(namespace2, key);
19307
+ const current = this.#records.get(id);
19308
+ if (current?.revision !== expectedRevision) {
19309
+ throw new Error(
19310
+ `peer desk record '${namespace2}/${key}' changed at revision ${current?.revision ?? "absent"}`
19311
+ );
19312
+ }
19313
+ if (value === void 0) {
19314
+ this.#records.delete(id);
19315
+ return void 0;
19316
+ }
19317
+ const next = Object.freeze({
19318
+ namespace: namespace2,
19319
+ key,
19320
+ revision: (current?.revision ?? 0n) + 1n,
19321
+ value: value.slice()
19322
+ });
19323
+ this.#records.set(id, next);
19324
+ return copy2(next);
19325
+ }
19326
+ };
19327
+
19328
+ // ../src/runtime/custody_receipts.ts
19329
+ var namespace = "nomos.peer-desk.custody-receipts/v1";
19330
+ var encoder = new TextEncoder();
19331
+ var decoder = new TextDecoder();
19332
+ var MAX_BASES = 256;
19333
+ var MAX_RECEIPTS = 4096;
19334
+ var CustodyReceipts = class {
19335
+ #store;
19336
+ #records = /* @__PURE__ */ new Map();
19337
+ #journals = /* @__PURE__ */ new Map();
19338
+ #receipts = /* @__PURE__ */ new Map();
19339
+ constructor(store = new MemoryPeerDeskStore()) {
19340
+ this.#store = store;
19341
+ for (const record of store.list(namespace)) {
19342
+ this.#records.set(record.key, record);
19343
+ try {
19344
+ if (record.key.startsWith("receipt:")) {
19345
+ const newline = record.value.indexOf(10);
19346
+ if (newline < 0) continue;
19347
+ const receipt = fromBinary(CustodyIntentAdmissionSchema, record.value.subarray(newline + 1));
19348
+ const header2 = JSON.parse(decoder.decode(record.value.subarray(0, newline)));
19349
+ if (receipt.outcome.case === "admitted" && typeof header2.generation === "string" && typeof header2.head === "string") this.#receipts.set(record.key, { ...header2, receipt });
19350
+ continue;
19351
+ }
19352
+ if (!record.key.startsWith("head:")) continue;
19353
+ const value = JSON.parse(decoder.decode(record.value));
19354
+ if (typeof value.head !== "string" || typeof value.generation !== "string" || !Array.isArray(value.bases) || !Array.isArray(value.keys) || value.bases.length > MAX_BASES || value.keys.length > MAX_RECEIPTS) continue;
19355
+ if (!value.bases.every((base) => typeof base === "string") || !value.keys.every((key) => typeof key === "string")) continue;
19356
+ this.#journals.set(record.key.slice(5), value);
19357
+ } catch {
19358
+ }
19359
+ }
19360
+ }
19361
+ covers(workspace, current, base) {
19362
+ const value = this.#journals.get(workspace);
19363
+ return value?.head === current && value.bases.includes(base);
19364
+ }
19365
+ receipt(workspace, current, change) {
19366
+ const value = this.#journals.get(workspace);
19367
+ if (value?.head !== current) return void 0;
19368
+ const stored = this.#receipts.get(`receipt:${workspace}:${sha256Hex(change)}`);
19369
+ if (stored?.generation !== value.generation) return void 0;
19370
+ if (stored.head !== value.head && !value.bases.includes(stored.head)) return void 0;
19371
+ return fromBinary(CustodyIntentAdmissionSchema, toBinary(CustodyIntentAdmissionSchema, stored.receipt));
19372
+ }
19373
+ record(workspace, before, after, receipts) {
19374
+ const old = this.#journals.get(workspace);
19375
+ const journalKey = `head:${workspace}`;
19376
+ const value = old?.head === before ? { ...old, head: after, bases: [...old.bases], keys: [...old.keys] } : { head: after, bases: [], keys: [], generation: String((this.#records.get(journalKey)?.revision ?? 0n) + 1n) };
19377
+ if (before && before !== after) value.bases.push(before);
19378
+ value.bases = [...new Set(value.bases)].slice(-MAX_BASES);
19379
+ const keys = new Set(value.keys);
19380
+ const discarded = new Set(old?.generation !== value.generation ? old?.keys : []);
19381
+ for (const { change, receipt } of receipts) {
19382
+ if (receipt.outcome.case !== "admitted") continue;
19383
+ const key = `receipt:${workspace}:${sha256Hex(change)}`;
19384
+ keys.delete(key);
19385
+ keys.add(key);
19386
+ discarded.delete(key);
19387
+ const header2 = encoder.encode(`${JSON.stringify({ generation: value.generation, head: after })}
19388
+ `);
19389
+ const body = toBinary(CustodyIntentAdmissionSchema, receipt);
19390
+ const bytes = new Uint8Array(header2.length + body.length);
19391
+ bytes.set(header2);
19392
+ bytes.set(body, header2.length);
19393
+ const record2 = this.#store.compareExchange(namespace, key, this.#records.get(key)?.revision, bytes);
19394
+ if (record2) this.#records.set(key, record2);
19395
+ this.#receipts.set(key, {
19396
+ generation: value.generation,
19397
+ head: after,
19398
+ receipt: fromBinary(CustodyIntentAdmissionSchema, body)
19399
+ });
19400
+ }
19401
+ const ordered = [...keys];
19402
+ for (const key of ordered.slice(0, -MAX_RECEIPTS)) discarded.add(key);
19403
+ value.keys = ordered.slice(-MAX_RECEIPTS);
19404
+ for (const key of discarded) {
19405
+ this.#store.compareExchange(namespace, key, this.#records.get(key)?.revision, void 0);
19406
+ this.#records.delete(key);
19407
+ this.#receipts.delete(key);
19408
+ }
19409
+ const record = this.#store.compareExchange(
19410
+ namespace,
19411
+ journalKey,
19412
+ this.#records.get(journalKey)?.revision,
19413
+ encoder.encode(JSON.stringify(value))
19414
+ );
19415
+ if (record) this.#records.set(journalKey, record);
19416
+ this.#journals.set(workspace, value);
19417
+ }
19418
+ };
19419
+
19288
19420
  // ../src/runtime/custodian_placement.ts
19289
19421
  init_define_NOMOS_KERNEL_RELEASE();
19290
19422
  var CustodianPlacement = class {
@@ -19390,14 +19522,16 @@ var CandidateCustodyRequiredError = class extends Error {
19390
19522
  var CustodyAdmissionDesk = class {
19391
19523
  #kernel;
19392
19524
  #custody;
19393
- constructor(kernel, custody) {
19525
+ #receipts;
19526
+ constructor(kernel, custody, receipts = new CustodyReceipts()) {
19394
19527
  this.#kernel = kernel;
19395
19528
  this.#custody = custody;
19529
+ this.#receipts = receipts;
19396
19530
  }
19397
19531
  admit(request, command) {
19398
- const session = request.target?.session;
19399
- if (!session) throw new Error("custody admission requires an addressed runtime session");
19400
- const mount = this.#custody.mountForSession(session);
19532
+ const workspace = request.target?.workspace;
19533
+ if (!workspace) throw new Error("custody admission requires an addressed workspace");
19534
+ const mount = this.#custody.mountForWorkspace(workspace);
19401
19535
  if (command.evidencePack.byteLength > 0) {
19402
19536
  this.#kernel.applyPack(mount, request.requestId, command.evidencePack);
19403
19537
  }
@@ -19408,21 +19542,42 @@ var CustodyAdmissionDesk = class {
19408
19542
  const births = [];
19409
19543
  const admittedBase = this.#kernel.head(mount, request.requestId) ?? "";
19410
19544
  if (command.fastForwardOnly && offers.length > 0 && (command.baseHead ?? "") !== admittedBase) {
19545
+ for (const offer of offers) {
19546
+ const receipt = this.#receipts.receipt(mount.workspace, admittedBase, offer.changeUsda);
19547
+ if (!receipt) continue;
19548
+ receipt.commitOid = offer.commitOid;
19549
+ const records = sessions.get(offer.session) ?? [];
19550
+ records.push(receipt);
19551
+ sessions.set(offer.session, records);
19552
+ }
19553
+ let delta = custodyReturnDelta(
19554
+ this.#kernel,
19555
+ mount,
19556
+ request.requestId,
19557
+ command.baseHead,
19558
+ admittedBase,
19559
+ admittedBase,
19560
+ command.observedHead
19561
+ );
19562
+ if (command.baseHead && command.observedHead !== admittedBase && this.#receipts.covers(mount.workspace, admittedBase, command.baseHead)) {
19563
+ try {
19564
+ delta = {
19565
+ resultBase: command.baseHead,
19566
+ resultPack: this.#kernel.packDelta(mount, request.requestId, admittedBase, command.baseHead),
19567
+ recoveryRequired: false
19568
+ };
19569
+ } catch {
19570
+ }
19571
+ }
19411
19572
  return create(CustodyAdmissionSchema, {
19412
19573
  main: admittedBase,
19413
- ...custodyReturnDelta(
19414
- this.#kernel,
19415
- mount,
19416
- request.requestId,
19417
- command.baseHead,
19418
- admittedBase,
19419
- admittedBase,
19420
- command.observedHead
19421
- )
19574
+ sessions: [...sessions].map(([session, intents]) => create(CustodySessionAdmissionSchema, { session, intents })),
19575
+ ...delta
19422
19576
  });
19423
19577
  }
19424
19578
  let main = admittedBase;
19425
19579
  const heldSessions = /* @__PURE__ */ new Map();
19580
+ const receipts = [];
19426
19581
  for (const offer of offers) {
19427
19582
  const heldBehind = heldSessions.get(offer.session);
19428
19583
  const verdict = heldBehind === void 0 ? this.#offer(request, mount, offer) : create(OfferResponseSchema, { outcome: { case: "blockedOnDependency", value: {
@@ -19432,7 +19587,9 @@ var CustodyAdmissionDesk = class {
19432
19587
  heldSessions.set(offer.session, verdict.outcome.value.offerId || offer.commitOid);
19433
19588
  }
19434
19589
  const records = sessions.get(offer.session) ?? [];
19435
- records.push(this.#record(offer, verdict));
19590
+ const receipt = this.#record(offer, verdict);
19591
+ records.push(receipt);
19592
+ receipts.push({ change: offer.changeUsda, receipt });
19436
19593
  sessions.set(offer.session, records);
19437
19594
  if (verdict.outcome.case === "admitted") {
19438
19595
  main = verdict.outcome.value.head;
@@ -19448,6 +19605,9 @@ var CustodyAdmissionDesk = class {
19448
19605
  }
19449
19606
  }
19450
19607
  }
19608
+ if (receipts.some((entry) => entry.receipt.outcome.case === "admitted")) {
19609
+ this.#receipts.record(mount.workspace, admittedBase, main, receipts);
19610
+ }
19451
19611
  return create(CustodyAdmissionSchema, {
19452
19612
  main,
19453
19613
  sessions: [...sessions].map(
@@ -22419,8 +22579,8 @@ var short = (oid) => oid.slice(0, 12);
22419
22579
  // ../src/runtime/preserved_copies_desk.ts
22420
22580
  init_define_NOMOS_KERNEL_RELEASE();
22421
22581
  var PRESERVED_COPIES_NAMESPACE = "nomos.peer-desk.preserved-local-copies/v1";
22422
- var encoder = new TextEncoder();
22423
- var decoder = new TextDecoder();
22582
+ var encoder2 = new TextEncoder();
22583
+ var decoder2 = new TextDecoder();
22424
22584
  var PreservedCopiesDesk = class {
22425
22585
  #store;
22426
22586
  constructor(store) {
@@ -22429,7 +22589,7 @@ var PreservedCopiesDesk = class {
22429
22589
  list(workspace) {
22430
22590
  const record = this.#store.list(PRESERVED_COPIES_NAMESPACE).find((entry) => entry.key === workspace);
22431
22591
  if (!record) return [];
22432
- const values = JSON.parse(decoder.decode(record.value));
22592
+ const values = JSON.parse(decoder2.decode(record.value));
22433
22593
  if (!Array.isArray(values)) {
22434
22594
  throw new Error(`corrupt preserved-copy record for '${workspace}' at revision ${record.revision}`);
22435
22595
  }
@@ -22443,7 +22603,7 @@ var PreservedCopiesDesk = class {
22443
22603
  PRESERVED_COPIES_NAMESPACE,
22444
22604
  copy3.workspace,
22445
22605
  current?.revision,
22446
- encoder.encode(`[${next.map((entry) => toJsonString(PreservedLocalCopySchema, entry)).join(",")}]`)
22606
+ encoder2.encode(`[${next.map((entry) => toJsonString(PreservedLocalCopySchema, entry)).join(",")}]`)
22447
22607
  );
22448
22608
  if (!stored) throw new Error(`preserved copy of '${copy3.workspace}' was not recorded`);
22449
22609
  }
@@ -22486,9 +22646,9 @@ var LOCAL_ONLY_NAMESPACES = /* @__PURE__ */ new Set([
22486
22646
  ]);
22487
22647
  var SEAT_PREFIX = "nomos-office-seat/v1:";
22488
22648
  var SEAT_DIGEST_HEX = 64;
22489
- var encoder2 = new TextEncoder();
22490
- var decoder2 = new TextDecoder();
22491
- var header = (digest) => encoder2.encode(`${SEAT_PREFIX}${digest}
22649
+ var encoder3 = new TextEncoder();
22650
+ var decoder3 = new TextDecoder();
22651
+ var header = (digest) => encoder3.encode(`${SEAT_PREFIX}${digest}
22492
22652
  `);
22493
22653
  var QuarantineReason = Object.freeze({
22494
22654
  /** Another office's seat: a copied state directory, or a store lifted from another peer. */
@@ -22499,7 +22659,7 @@ var QuarantineReason = Object.freeze({
22499
22659
  function readSeal(value) {
22500
22660
  const headerLength = SEAT_PREFIX.length + SEAT_DIGEST_HEX + 1;
22501
22661
  if (value.byteLength < headerLength) return void 0;
22502
- const text = decoder2.decode(value.subarray(0, headerLength));
22662
+ const text = decoder3.decode(value.subarray(0, headerLength));
22503
22663
  if (!text.startsWith(SEAT_PREFIX) || !text.endsWith("\n")) return void 0;
22504
22664
  const seat = text.slice(SEAT_PREFIX.length, headerLength - 1);
22505
22665
  if (!/^[0-9a-f]{64}$/.test(seat)) return void 0;
@@ -22523,44 +22683,44 @@ var SeatedPeerDeskStore = class {
22523
22683
  this.#seat = seat;
22524
22684
  this.#report = report;
22525
22685
  }
22526
- list(namespace) {
22527
- if (LOCAL_ONLY_NAMESPACES.has(namespace)) return this.#inner.list(namespace);
22686
+ list(namespace2) {
22687
+ if (LOCAL_ONLY_NAMESPACES.has(namespace2)) return this.#inner.list(namespace2);
22528
22688
  const kept = [];
22529
- for (const record of this.#inner.list(namespace)) {
22689
+ for (const record of this.#inner.list(namespace2)) {
22530
22690
  const sealed = readSeal(record.value);
22531
22691
  if (sealed?.seat === this.#seat.digest) {
22532
22692
  kept.push({ ...record, value: sealed.value.slice() });
22533
22693
  continue;
22534
22694
  }
22535
- this.#quarantine(namespace, record, sealed?.seat);
22695
+ this.#quarantine(namespace2, record, sealed?.seat);
22536
22696
  }
22537
22697
  return kept;
22538
22698
  }
22539
- compareExchange(namespace, key, expectedRevision, value) {
22540
- if (LOCAL_ONLY_NAMESPACES.has(namespace)) {
22541
- return this.#inner.compareExchange(namespace, key, expectedRevision, value);
22699
+ compareExchange(namespace2, key, expectedRevision, value) {
22700
+ if (LOCAL_ONLY_NAMESPACES.has(namespace2)) {
22701
+ return this.#inner.compareExchange(namespace2, key, expectedRevision, value);
22542
22702
  }
22543
22703
  const stored = this.#inner.compareExchange(
22544
- namespace,
22704
+ namespace2,
22545
22705
  key,
22546
22706
  expectedRevision,
22547
22707
  value === void 0 ? void 0 : seal(this.#seat.digest, value)
22548
22708
  );
22549
22709
  if (stored === void 0) return void 0;
22550
22710
  const sealed = readSeal(stored.value);
22551
- if (!sealed) throw new Error(`peer desk record '${namespace}/${key}' lost its office seat`);
22711
+ if (!sealed) throw new Error(`peer desk record '${namespace2}/${key}' lost its office seat`);
22552
22712
  return { ...stored, value: sealed.value.slice() };
22553
22713
  }
22554
22714
  /** Preserve the foreign record under the quarantine namespace, then free the slot. */
22555
- #quarantine(namespace, record, recordedSeat) {
22556
- const key = `${namespace}\0${record.key}\0${record.revision}`;
22715
+ #quarantine(namespace2, record, recordedSeat) {
22716
+ const key = `${namespace2}\0${record.key}\0${record.revision}`;
22557
22717
  try {
22558
22718
  this.#inner.compareExchange(QUARANTINED_BINDINGS_NAMESPACE, key, void 0, record.value);
22559
22719
  } catch {
22560
22720
  }
22561
- this.#inner.compareExchange(namespace, record.key, record.revision, void 0);
22721
+ this.#inner.compareExchange(namespace2, record.key, record.revision, void 0);
22562
22722
  this.#report(create(QuarantinedBindingSchema, {
22563
- namespace,
22723
+ namespace: namespace2,
22564
22724
  key: record.key,
22565
22725
  reason: recordedSeat === void 0 ? QuarantineReason.Unseated : QuarantineReason.ForeignSeat,
22566
22726
  recordedSeat: recordedSeat ?? "",
@@ -22611,6 +22771,7 @@ var ReplicaInventoryDesk = class _ReplicaInventoryDesk {
22611
22771
  const undiscovered = /* @__PURE__ */ new Set();
22612
22772
  for (const restored of this.#follows.recover()) {
22613
22773
  following.set(restored.workspace, restored.delayMillis);
22774
+ followed.add(restored.workspace);
22614
22775
  }
22615
22776
  if (!this.#bootstrapped) {
22616
22777
  this.#bootstrapped = true;
@@ -22648,6 +22809,7 @@ var ReplicaInventoryDesk = class _ReplicaInventoryDesk {
22648
22809
  stopped.delete(workspace);
22649
22810
  } else if (reconciliation.changed) {
22650
22811
  following.delete(workspace);
22812
+ followed.delete(workspace);
22651
22813
  stopped.add(workspace);
22652
22814
  }
22653
22815
  } catch (error) {
@@ -22981,7 +23143,7 @@ var LiveCorrespondenceDesk = class {
22981
23143
  wasResident: this.kernel.isResident(binding.workspace) !== false
22982
23144
  };
22983
23145
  this.#links.set(binding.workspace, link);
22984
- if (link.wasResident) this.#open(link);
23146
+ if (!link.publisher || link.wasResident) this.#open(link);
22985
23147
  }
22986
23148
  changed(workspace) {
22987
23149
  const link = this.#links.get(workspace);
@@ -23008,6 +23170,9 @@ var LiveCorrespondenceDesk = class {
23008
23170
  this.#effect(link, { case: "send", value: bytes });
23009
23171
  }
23010
23172
  export(binding, base, requestId) {
23173
+ if (this.kernel.isResident(binding.workspace) === false) {
23174
+ return create(CustodyOfferBatchRequestSchema, { protocolVersion: 2 });
23175
+ }
23011
23176
  const mount = workspaceMount(binding.workspace);
23012
23177
  const head = this.kernel.head(mount, requestId);
23013
23178
  if (!base || !head || base === head) return create(CustodyOfferBatchRequestSchema, { protocolVersion: 2 });
@@ -23068,6 +23233,7 @@ var LiveCorrespondenceDesk = class {
23068
23233
  }
23069
23234
  if (frame.message.case === "frontier") {
23070
23235
  this.#announced(link, frame.message.value.head);
23236
+ if (this.kernel.isResident(link.binding.workspace) === false) return;
23071
23237
  if (!frame.message.value.response) {
23072
23238
  this.#send(link, { case: "frontier", value: {
23073
23239
  head: this.kernel.head(workspaceMount(link.binding.workspace), ++this.#requestId) ?? "",
@@ -23084,6 +23250,10 @@ var LiveCorrespondenceDesk = class {
23084
23250
  if (frame.message.case !== "offers") return;
23085
23251
  const batch = frame.message.value;
23086
23252
  if (batch.protocolVersion !== 2 || !batch.requestId || batch.source.case !== "inline" || !batch.source.value.offers.length) return;
23253
+ if (this.kernel.isResident(link.binding.workspace) === false) {
23254
+ this.remoteAdvanced?.(link.binding.workspace);
23255
+ return;
23256
+ }
23087
23257
  if ([...this.#pending.values()].some((pending) => pending.link === link && pending.bytes.length === bytes.length && pending.bytes.every((byte, index) => byte === bytes[index]))) return;
23088
23258
  if (this.#pending.size >= 32) return;
23089
23259
  this.#pending.set(request.requestId, { link, bytes });
@@ -23091,7 +23261,16 @@ var LiveCorrespondenceDesk = class {
23091
23261
  protocolVersion: 2,
23092
23262
  requestId: request.requestId,
23093
23263
  target: { workspace: link.binding.workspace, session: link.binding.replicaId },
23094
- command: { case: "admitCustodyOffers", value: create(CustodyOfferBatchRequestSchema, { ...batch, baseHead: void 0 }) }
23264
+ // HTTP sender reconciliation controls are not part of a live proposal.
23265
+ // Keeping fastForwardOnly while removing baseHead silently skips every
23266
+ // offer against a nonempty local head. The Kernel judges sealed causality;
23267
+ // live receivers neither ask for a return archive nor confirm the sender.
23268
+ command: { case: "admitCustodyOffers", value: create(CustodyOfferBatchRequestSchema, {
23269
+ ...batch,
23270
+ baseHead: void 0,
23271
+ observedHead: void 0,
23272
+ fastForwardOnly: false
23273
+ }) }
23095
23274
  });
23096
23275
  }
23097
23276
  /** Upstream news is a frontier this replica has NEVER seen: neither its own head nor the
@@ -23100,6 +23279,10 @@ var LiveCorrespondenceDesk = class {
23100
23279
  #announced(link, head) {
23101
23280
  if (!head || link.lastAnnounced === head) return;
23102
23281
  link.lastAnnounced = head;
23282
+ if (this.kernel.isResident(link.binding.workspace) === false) {
23283
+ this.remoteAdvanced?.(link.binding.workspace);
23284
+ return;
23285
+ }
23103
23286
  const mount = workspaceMount(link.binding.workspace);
23104
23287
  if (head === this.kernel.head(mount, ++this.#requestId) || head === this.kernel.resolveRef(mount, ++this.#requestId, "refs/remotes/nomos/main")) return;
23105
23288
  this.remoteAdvanced?.(link.binding.workspace);
@@ -23109,6 +23292,10 @@ var LiveCorrespondenceDesk = class {
23109
23292
  #placementAnnounced(link, frontier) {
23110
23293
  if (!frontier || link.lastPlacementAnnounced === frontier) return;
23111
23294
  link.lastPlacementAnnounced = frontier;
23295
+ if (this.kernel.isResident(link.binding.workspace) === false) {
23296
+ this.placementAdvanced?.(link.binding.workspace);
23297
+ return;
23298
+ }
23112
23299
  const mount = workspaceMount(link.binding.workspace);
23113
23300
  if (frontier === this.kernel.resolveRef(mount, ++this.#requestId, PLACEMENT_REF2)) return;
23114
23301
  this.placementAdvanced?.(link.binding.workspace);
@@ -23125,11 +23312,13 @@ var LiveCorrespondenceDesk = class {
23125
23312
  disconnect(workspace) {
23126
23313
  const link = this.#links.get(workspace);
23127
23314
  if (!link) return;
23315
+ if (link.state === "open") this.linkClosed?.(workspace);
23128
23316
  this.#effect(link, { case: "close", value: true });
23129
23317
  this.#links.delete(workspace);
23130
23318
  for (const [id, pending] of this.#pending) if (pending.link === link) this.#pending.delete(id);
23131
23319
  }
23132
23320
  #hello(link) {
23321
+ if (this.kernel.isResident(link.binding.workspace) === false) return;
23133
23322
  const head = this.kernel.head(workspaceMount(link.binding.workspace), ++this.#requestId);
23134
23323
  this.#send(link, { case: "frontier", value: { head: head ?? "" } });
23135
23324
  }
@@ -23137,19 +23326,20 @@ var LiveCorrespondenceDesk = class {
23137
23326
  const now = this.clock.monotonicNanoseconds();
23138
23327
  let wake;
23139
23328
  for (const link of this.#links.values()) {
23140
- if (this.kernel.isResident(link.binding.workspace) === false) {
23329
+ const resident = this.kernel.isResident(link.binding.workspace) !== false;
23330
+ if (link.publisher && !resident) {
23141
23331
  if (link.wasResident) this.disconnect(link.binding.workspace);
23142
23332
  else wake = wake === void 0 || wake > 250n ? 250n : wake;
23143
23333
  continue;
23144
23334
  }
23145
- link.wasResident = true;
23335
+ if (resident) link.wasResident = true;
23146
23336
  if (link.state === "opening" && now >= link.due) {
23147
23337
  this.#effect(link, { case: "close", value: true });
23148
23338
  link.state = "closed";
23149
23339
  }
23150
23340
  if (link.state === "closed" && now >= link.due) this.#open(link);
23151
23341
  if (link.state === "open") {
23152
- if (link.dirty) {
23342
+ if (link.dirty && resident) {
23153
23343
  link.dirty = false;
23154
23344
  try {
23155
23345
  link.published ??= link.publisher ? this.kernel.head(workspaceMount(link.binding.workspace), ++this.#requestId) : this.kernel.resolveRef(workspaceMount(link.binding.workspace), ++this.#requestId, "refs/remotes/nomos/main");
@@ -23168,8 +23358,7 @@ var LiveCorrespondenceDesk = class {
23168
23358
  return wake;
23169
23359
  }
23170
23360
  close() {
23171
- for (const link of this.#links.values()) this.#effect(link, { case: "close", value: true });
23172
- this.#links.clear();
23361
+ for (const workspace of [...this.#links.keys()]) this.disconnect(workspace);
23173
23362
  this.#pending.clear();
23174
23363
  }
23175
23364
  };
@@ -23300,9 +23489,9 @@ var RecordsDesk = class {
23300
23489
  command.namespace === void 0 ? {} : { "custody.children_namespace": command.namespace },
23301
23490
  () => this.#kernel.childWorkspaces(mount, request.requestId)
23302
23491
  );
23303
- const namespace = command.namespace;
23304
- const selected = namespace === void 0 ? result.children : result.children.filter(
23305
- (child) => child.status === ChildWorkspaceLifecycleStatus.ACTIVE && isKeyedBirthOf(namespace, child.workspace)
23492
+ const namespace2 = command.namespace;
23493
+ const selected = namespace2 === void 0 ? result.children : result.children.filter(
23494
+ (child) => child.status === ChildWorkspaceLifecycleStatus.ACTIVE && isKeyedBirthOf(namespace2, child.workspace)
23306
23495
  );
23307
23496
  return create(ChildWorkspaceListSchema, {
23308
23497
  children: selected.map((child) => create(ChildWorkspaceSchema, {
@@ -23670,41 +23859,6 @@ var RecoveryDesk = class {
23670
23859
  // ../src/runtime/correspondence_desk.ts
23671
23860
  init_define_NOMOS_KERNEL_RELEASE();
23672
23861
 
23673
- // ../src/runtime/peer_desk_store.ts
23674
- init_define_NOMOS_KERNEL_RELEASE();
23675
- var recordId = (namespace, key) => `${namespace.length}:${namespace}${key}`;
23676
- var copy2 = (record) => ({
23677
- ...record,
23678
- value: record.value.slice()
23679
- });
23680
- var MemoryPeerDeskStore = class {
23681
- #records = /* @__PURE__ */ new Map();
23682
- list(namespace) {
23683
- return [...this.#records.values()].filter((record) => record.namespace === namespace).sort((left, right) => left.key.localeCompare(right.key)).map(copy2);
23684
- }
23685
- compareExchange(namespace, key, expectedRevision, value) {
23686
- const id = recordId(namespace, key);
23687
- const current = this.#records.get(id);
23688
- if (current?.revision !== expectedRevision) {
23689
- throw new Error(
23690
- `peer desk record '${namespace}/${key}' changed at revision ${current?.revision ?? "absent"}`
23691
- );
23692
- }
23693
- if (value === void 0) {
23694
- this.#records.delete(id);
23695
- return void 0;
23696
- }
23697
- const next = Object.freeze({
23698
- namespace,
23699
- key,
23700
- revision: (current?.revision ?? 0n) + 1n,
23701
- value: value.slice()
23702
- });
23703
- this.#records.set(id, next);
23704
- return copy2(next);
23705
- }
23706
- };
23707
-
23708
23862
  // ../src/runtime/correspondence_recovery.ts
23709
23863
  init_define_NOMOS_KERNEL_RELEASE();
23710
23864
  var CorrespondenceRecoveryWork = class {
@@ -24090,6 +24244,7 @@ var WorkspaceSyncWork = class {
24090
24244
  #offerSequence = 0;
24091
24245
  #lastStep;
24092
24246
  #probeOnly;
24247
+ #liveConnected;
24093
24248
  /** Set when this round adopted the upstream's own head as its base (see #establishBase). */
24094
24249
  #establishedBase;
24095
24250
  #receiveState;
@@ -24122,6 +24277,7 @@ var WorkspaceSyncWork = class {
24122
24277
  this.#directoryAnswered = args.directoryAnswered;
24123
24278
  this.#placementFetched = args.placementFetched;
24124
24279
  this.#probeOnly = args.probeOnly ?? false;
24280
+ this.#liveConnected = args.liveConnected === true;
24125
24281
  this.#receiveState = args.receiveState;
24126
24282
  this.#completeHistory = args.completeHistory === true;
24127
24283
  this.#repairMissingCommit = args.repairMissingCommit === true;
@@ -24192,6 +24348,9 @@ var WorkspaceSyncWork = class {
24192
24348
  #openRound() {
24193
24349
  if (this.#receiveState || this.#replaceLocalHead !== void 0) return this.#leaseEffect();
24194
24350
  if (!this.#localHead) return this.#leaseEffect();
24351
+ if (this.#liveConnected && !this.#probeOnly && this.#holdsLocalWork() && this.#previousHead) {
24352
+ return this.#offerOrReconcile();
24353
+ }
24195
24354
  const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:frontier`;
24196
24355
  return this.#beginOffer(encodeInlineCustodyOffer({
24197
24356
  session: this.#binding.replicaId,
@@ -24423,6 +24582,18 @@ var WorkspaceSyncWork = class {
24423
24582
  return this.#finishOfferOutcome(false);
24424
24583
  }
24425
24584
  const admission = outcome.value;
24585
+ globalThis.__nomosSyncDiagnostic?.("sync.admission", {
24586
+ workspace: this.#mount.workspace,
24587
+ phase: state.phase,
24588
+ local: this.#localHead,
24589
+ tracked: this.#previousHead,
24590
+ remote: admission.main,
24591
+ sealed: admission.sealedMain,
24592
+ resultBase: admission.resultBase,
24593
+ resultBytes: admission.resultPack.byteLength,
24594
+ recoveryRequired: admission.recoveryRequired,
24595
+ summary: summarizeCustodyAdmission(admission)
24596
+ });
24426
24597
  this.#desiredHead = admission.main || void 0;
24427
24598
  if (!this.#desiredHead) return this.#transportFailure("resident peer returned no admitted frontier", false);
24428
24599
  this.#candidateDurable = admission.sealedMain === this.#desiredHead;
@@ -25039,6 +25210,19 @@ var WorkspaceSyncWork = class {
25039
25210
  return step;
25040
25211
  }
25041
25212
  #finish(value) {
25213
+ globalThis.__nomosSyncDiagnostic?.("sync.finished", {
25214
+ workspace: value.workspace,
25215
+ previous: value.previousHead,
25216
+ current: value.currentHead,
25217
+ stillAhead: value.stillAhead,
25218
+ deferred: String(value.deferred),
25219
+ admitted: String(value.admitted),
25220
+ reconnect: value.reconnectRequired,
25221
+ directOfferDeferred: value.directOfferDeferred,
25222
+ pushFailed: value.pushFailed,
25223
+ failure: value.pushError?.message,
25224
+ wallMs: elapsedMillis(this.#clock, this.#started)
25225
+ });
25042
25226
  this.#state = { phase: WorkspaceSyncPhase.Done };
25043
25227
  this.#current = void 0;
25044
25228
  return this.#remember({ case: "synced", value });
@@ -25095,6 +25279,10 @@ var WorkspaceDependencySyncWork = class {
25095
25279
  this.#plan = [this.#args.root];
25096
25280
  return this.#advance();
25097
25281
  }
25282
+ if (this.#args.liveConnected && this.#args.kernel.resolveRef(this.#args.root.mount, this.#args.requestId, trackedMain2)) {
25283
+ this.#plan = this.#planDependencies();
25284
+ return this.#advance();
25285
+ }
25098
25286
  this.#current = this.#work(this.#args.root, `${this.workId}:frontier`, true);
25099
25287
  return this.#accept(this.#current.start());
25100
25288
  }
@@ -26288,6 +26476,7 @@ var CorrespondenceDesk = class {
26288
26476
  #onSyncCompleted;
26289
26477
  #onPlacementFetched;
26290
26478
  #onShallowMount;
26479
+ #onOutboxHistoryRequired;
26291
26480
  #remoteOffers = new CorrespondenceWorkRegistry();
26292
26481
  #recoveries = new CorrespondenceWorkRegistry();
26293
26482
  #deliveries = new CorrespondenceWorkRegistry();
@@ -26334,6 +26523,7 @@ var CorrespondenceDesk = class {
26334
26523
  this.#onSyncCompleted = args.onSyncCompleted;
26335
26524
  this.#onPlacementFetched = args.onPlacementFetched;
26336
26525
  this.#onShallowMount = args.onShallowMount;
26526
+ this.#onOutboxHistoryRequired = args.onOutboxHistoryRequired;
26337
26527
  this.#clock = args.clock;
26338
26528
  this.#outboxClock = new OutboxObservationClock(args.clock, args.peerIdentity);
26339
26529
  this.#executesCustodianObligations = args.executesCustodianObligations ?? false;
@@ -26497,6 +26687,15 @@ var CorrespondenceDesk = class {
26497
26687
  this.#followLane.dependencyAdvanced(workspace);
26498
26688
  this.reportFollows();
26499
26689
  }
26690
+ /** Received custody can release correspondence and foreign-read dependencies,
26691
+ * but is not newly authored work to send back to its upstream. Recovery is
26692
+ * driven separately by missing history, reconnects and provider announcements.
26693
+ * In particular an optimistic relay is not a durable upstream receipt. */
26694
+ receivedFrontierChanged(workspace) {
26695
+ this.wakeOutbox(workspace);
26696
+ this.#followLane.dependencyAdvanced(workspace);
26697
+ this.reportFollows();
26698
+ }
26500
26699
  /**
26501
26700
  * Tell a workspace's sessions what its durable follow ALREADY knows.
26502
26701
  *
@@ -26733,8 +26932,16 @@ var CorrespondenceDesk = class {
26733
26932
  )
26734
26933
  );
26735
26934
  } catch (error) {
26935
+ if (error instanceof KernelHistoryRequired && this.#onOutboxHistoryRequired) {
26936
+ const retry = this.#onOutboxHistoryRequired(workspace, binding.credentialCapability);
26937
+ this.#outboxDueAt.set(workspace, now + Math.max(250, retry));
26938
+ return { nextAfterMillis: 0 };
26939
+ }
26736
26940
  const format = localCopyFormatRefusal(error);
26737
- if (format === void 0) throw error;
26941
+ if (format === void 0) {
26942
+ this.#outboxDueAt.set(workspace, now + 3e4);
26943
+ return { nextAfterMillis: 0 };
26944
+ }
26738
26945
  this.#readability.mark(workspace, void 0, format);
26739
26946
  this.#outboxDueAt.delete(workspace);
26740
26947
  return { nextAfterMillis: 0 };
@@ -27014,6 +27221,7 @@ var CorrespondenceDesk = class {
27014
27221
  requestId: request.requestId,
27015
27222
  workId: id,
27016
27223
  probeOnly,
27224
+ liveConnected: this.#openLinks.has(target3.mount.workspace),
27017
27225
  ...beforeConverge === void 0 ? {} : { beforeConverge },
27018
27226
  ...target3.receiveState === void 0 ? {} : { receiveState: target3.receiveState },
27019
27227
  ...target3.offerHead === void 0 ? {} : { offerHead: target3.offerHead },
@@ -27041,6 +27249,7 @@ var CorrespondenceDesk = class {
27041
27249
  root,
27042
27250
  work,
27043
27251
  outbound: command.admit,
27252
+ liveConnected: this.#openLinks.has(mount.workspace),
27044
27253
  dependency: (workspace, source, acquire2 = false) => {
27045
27254
  if (this.#roundInFlight(workspace, workId2)) return void 0;
27046
27255
  const existing = this.#follows.bindingForWorkspace(workspace);
@@ -28580,15 +28789,15 @@ function reduceHistoryAcquisition(state, event) {
28580
28789
  var HISTORY_ACQUISITION_NAMESPACE = "nomos.peer-desk.history-acquisition/v1";
28581
28790
  var HISTORY_BACKGROUND_START_MILLIS = 1e4;
28582
28791
  var SLICE_ANSWER_MILLIS = 5 * 6e4;
28583
- var encoder3 = new TextEncoder();
28584
- var decoder3 = new TextDecoder();
28792
+ var encoder4 = new TextEncoder();
28793
+ var decoder4 = new TextDecoder();
28585
28794
  var workId = (workspace) => `history-acquisition:${workspace}`;
28586
28795
  var effectId = (workspace, attempt) => `history-acquisition:${workspace}:${HistoryAcquisitionPhase.Acquiring}:${attempt}`;
28587
28796
  var encode2 = (work) => {
28588
28797
  if (!work.credentialCapability) {
28589
28798
  throw new Error(`history acquisition for '${work.workspace}' has not restored its credential capability`);
28590
28799
  }
28591
- return encoder3.encode(JSON.stringify({
28800
+ return encoder4.encode(JSON.stringify({
28592
28801
  version: 2,
28593
28802
  workspace: work.workspace,
28594
28803
  credentialCapability: work.credentialCapability,
@@ -28597,7 +28806,7 @@ var encode2 = (work) => {
28597
28806
  };
28598
28807
  var upgraded = (state) => state.case === "backoff" ? { case: HistoryAcquisitionPhase.Paced, attempt: state.attempt, delayMillis: state.delayMillis, failed: true } : state;
28599
28808
  var decode2 = (record) => {
28600
- const value = JSON.parse(decoder3.decode(record.value));
28809
+ const value = JSON.parse(decoder4.decode(record.value));
28601
28810
  if (!value || typeof value !== "object") throw corrupt2(record, "not an object");
28602
28811
  const candidate = value;
28603
28812
  if (candidate.version !== 1 && candidate.version !== 2 || candidate.workspace !== record.key || !candidate.state) {
@@ -28660,6 +28869,21 @@ var HistoryAcquisitionDesk = class {
28660
28869
  this.#ensure(workspace, credentialCapability);
28661
28870
  this.#dueAt.set(workspace, this.#now() + HISTORY_BACKGROUND_START_MILLIS);
28662
28871
  }
28872
+ /** A background kernel read needs an omitted ancestor now. Queue one bounded
28873
+ * slice on the existing history lane; a failed slice keeps its retry pace. */
28874
+ requestFromBackground(workspace, credentialCapability) {
28875
+ if (!workspace) throw new Error("kernel history requirement names no workspace");
28876
+ this.#ensure(workspace, credentialCapability);
28877
+ const work = this.#get(workspace);
28878
+ const now = this.#now();
28879
+ if (work.state.case === HistoryAcquisitionPhase.Paced && work.state.failed) {
28880
+ const due2 = this.#dueAt.get(workspace) ?? now + work.state.delayMillis;
28881
+ this.#dueAt.set(workspace, due2);
28882
+ if (due2 > now) return due2 - now;
28883
+ }
28884
+ this.#dueAt.set(workspace, now);
28885
+ return 250;
28886
+ }
28663
28887
  /** An operation needs history this custody lacks: its next slice now, joining one in flight. */
28664
28888
  begin(workspace, credentialCapability) {
28665
28889
  if (!workspace) throw new Error("kernel history requirement names no workspace");
@@ -28968,7 +29192,7 @@ var PeerOffice = class {
28968
29192
  this.#trace,
28969
29193
  this.#readability
28970
29194
  );
28971
- this.#custodyAdmission = new CustodyAdmissionDesk(this.#kernel, this.#custody);
29195
+ this.#custodyAdmission = new CustodyAdmissionDesk(this.#kernel, this.#custody, new CustodyReceipts(seatedStore));
28972
29196
  this.#queries = new KernelQueries(this.#kernel, this.#credentials);
28973
29197
  this.#follows = new ReplicaFollowController(
28974
29198
  new ReplicaFollowDesk(seatedStore),
@@ -29130,6 +29354,7 @@ var PeerOffice = class {
29130
29354
  // Lazy history: a head-only mount materialises its history in the background, never on
29131
29355
  // the path of an operation (architecture/lazy_history.md).
29132
29356
  onShallowMount: (workspace, credentialCapability) => this.#historyAcquisition.materialise(workspace, credentialCapability),
29357
+ onOutboxHistoryRequired: (workspace, credentialCapability) => this.#historyAcquisition.requestFromBackground(workspace, credentialCapability),
29133
29358
  onSyncCompleted: (session, event) => {
29134
29359
  if (event.result?.workspace) {
29135
29360
  this.#replicaInventory.placementMayHaveChanged(event.result.workspace);
@@ -29596,8 +29821,8 @@ var PeerOffice = class {
29596
29821
  this.#replicaInventory.placementMayHaveChanged(workspace);
29597
29822
  }
29598
29823
  }
29599
- /** Execute a typed paper after the access desk; never skip lifecycle or
29600
- * session-binding checks merely because its caller is another local desk. */
29824
+ /** Execute a typed paper after the access desk. Custody admission addresses a
29825
+ * workspace by replica identity; application calls retain session binding. */
29601
29826
  #dispatchRequest(request) {
29602
29827
  validateRuntimeV2Request(request);
29603
29828
  try {
@@ -29611,18 +29836,20 @@ var PeerOffice = class {
29611
29836
  if (!request.target.workspace || !request.target.session) {
29612
29837
  throw new Error("addressed runtime request requires workspace and session");
29613
29838
  }
29614
- const binding = this.#sessions.bindingIfPresent(request.target.session);
29615
- if (binding !== void 0) {
29616
- if (binding.workspace !== request.target.workspace) {
29617
- throw new Error(
29618
- `session '${request.target.session}' is bound to workspace '${binding.workspace}', not '${request.target.workspace}'`
29619
- );
29839
+ if (request.command.case !== "admitCustodyOffers") {
29840
+ const binding = this.#sessions.bindingIfPresent(request.target.session);
29841
+ if (binding !== void 0) {
29842
+ if (binding.workspace !== request.target.workspace) {
29843
+ throw new Error(
29844
+ `session '${request.target.session}' is bound to workspace '${binding.workspace}', not '${request.target.workspace}'`
29845
+ );
29846
+ }
29847
+ } else {
29848
+ this.#bindInteraction(create(BindSessionRequestSchema, {
29849
+ session: request.target.session,
29850
+ workspace: request.target.workspace
29851
+ }));
29620
29852
  }
29621
- } else {
29622
- this.#bindInteraction(create(BindSessionRequestSchema, {
29623
- session: request.target.session,
29624
- workspace: request.target.workspace
29625
- }));
29626
29853
  }
29627
29854
  }
29628
29855
  switch (request.command.case) {
@@ -29819,7 +30046,7 @@ var PeerOffice = class {
29819
30046
  ReadMaintenanceCause.ADMITTED_OFFER
29820
30047
  );
29821
30048
  this.#subscriptions.refreshWorkspace(request.target.workspace);
29822
- this.#correspondence.workspaceFrontierChanged(request.target.workspace);
30049
+ this.#correspondence.receivedFrontierChanged(request.target.workspace);
29823
30050
  }
29824
30051
  return this.#birthCustodyResponse(
29825
30052
  request,
@@ -30403,6 +30630,15 @@ function createBackgroundLanes(options) {
30403
30630
  const correspondenceNotBefore = /* @__PURE__ */ new Map();
30404
30631
  let retention = 0;
30405
30632
  let closed = false;
30633
+ let selectionFailures = 0;
30634
+ const selectionFault = (error) => {
30635
+ try {
30636
+ options.onSelectionError?.(error);
30637
+ } catch {
30638
+ }
30639
+ selectionFailures += 1;
30640
+ wake(Math.min(3e4, 250 * 2 ** Math.min(selectionFailures - 1, 7)));
30641
+ };
30406
30642
  const wake = (delayMillis = 0) => {
30407
30643
  if (closed) return;
30408
30644
  const delay = Math.max(0, Math.ceil(delayMillis));
@@ -30413,53 +30649,58 @@ function createBackgroundLanes(options) {
30413
30649
  }
30414
30650
  wakeAt = at;
30415
30651
  timer = setTimer(() => {
30416
- timer = void 0;
30417
- wakeAt = void 0;
30418
- if (closed) return;
30419
- const now = options.monotonicNanoseconds();
30420
- for (const [workspace2, at2] of correspondenceNotBefore) {
30421
- if (at2 <= now) correspondenceNotBefore.delete(workspace2);
30422
- }
30423
- const backoffMillis = correspondenceNotBefore.size ? Math.min(...[...correspondenceNotBefore.values()].map((at2) => Number((at2 - now + 999999n) / 1000000n))) : void 0;
30424
- const retentionOnly = correspondenceInFlight;
30425
- const next = options.nextPaper(retentionOnly, new Set(correspondenceNotBefore.keys()));
30426
- if (next.frame === void 0) {
30427
- const delays = [next.nextAfterMillis, backoffMillis].filter((delay2) => delay2 !== void 0);
30428
- if (delays.length) wake(Math.min(...delays));
30429
- return;
30430
- }
30431
- if (options.isRetention(next.frame)) {
30432
- retention += 1;
30652
+ try {
30653
+ timer = void 0;
30654
+ wakeAt = void 0;
30655
+ if (closed) return;
30656
+ const now = options.monotonicNanoseconds();
30657
+ for (const [workspace2, at2] of correspondenceNotBefore) {
30658
+ if (at2 <= now) correspondenceNotBefore.delete(workspace2);
30659
+ }
30660
+ const backoffMillis = correspondenceNotBefore.size ? Math.min(...[...correspondenceNotBefore.values()].map((at2) => Number((at2 - now + 999999n) / 1000000n))) : void 0;
30661
+ const retentionOnly = correspondenceInFlight;
30662
+ const next = options.nextPaper(retentionOnly, new Set(correspondenceNotBefore.keys()));
30663
+ selectionFailures = 0;
30664
+ if (next.frame === void 0) {
30665
+ const delays = [next.nextAfterMillis, backoffMillis].filter((delay2) => delay2 !== void 0);
30666
+ if (delays.length) wake(Math.min(...delays));
30667
+ return;
30668
+ }
30669
+ if (options.isRetention(next.frame)) {
30670
+ retention += 1;
30671
+ void options.drive(next.frame).then(
30672
+ () => {
30673
+ retention -= 1;
30674
+ wake(0);
30675
+ },
30676
+ () => {
30677
+ retention -= 1;
30678
+ wake(0);
30679
+ }
30680
+ );
30681
+ wake(next.nextAfterMillis ?? 0);
30682
+ return;
30683
+ }
30684
+ if (retentionOnly) throw new Error("background desk reserved correspondence while its lane was unavailable");
30685
+ const workspace = next.workspace;
30686
+ if (!workspace) throw new Error("background correspondence has no source workspace");
30687
+ if (correspondenceNotBefore.has(workspace)) throw new Error("background desk bypassed a workspace's physical backoff");
30688
+ correspondenceInFlight = true;
30433
30689
  void options.drive(next.frame).then(
30434
30690
  () => {
30435
- retention -= 1;
30436
- wake(0);
30691
+ correspondenceInFlight = false;
30692
+ wake(next.nextAfterMillis ?? 0);
30437
30693
  },
30438
30694
  () => {
30439
- retention -= 1;
30695
+ correspondenceInFlight = false;
30696
+ correspondenceNotBefore.set(workspace, options.monotonicNanoseconds() + BigInt(failureBackoffMillis) * 1000000n);
30440
30697
  wake(0);
30441
30698
  }
30442
30699
  );
30443
- wake(next.nextAfterMillis ?? 0);
30444
- return;
30700
+ wake(0);
30701
+ } catch (error) {
30702
+ selectionFault(error);
30445
30703
  }
30446
- if (retentionOnly) throw new Error("background desk reserved correspondence while its lane was unavailable");
30447
- const workspace = next.workspace;
30448
- if (!workspace) throw new Error("background correspondence has no source workspace");
30449
- if (correspondenceNotBefore.has(workspace)) throw new Error("background desk bypassed a workspace's physical backoff");
30450
- correspondenceInFlight = true;
30451
- void options.drive(next.frame).then(
30452
- () => {
30453
- correspondenceInFlight = false;
30454
- wake(next.nextAfterMillis ?? 0);
30455
- },
30456
- () => {
30457
- correspondenceInFlight = false;
30458
- correspondenceNotBefore.set(workspace, options.monotonicNanoseconds() + BigInt(failureBackoffMillis) * 1000000n);
30459
- wake(0);
30460
- }
30461
- );
30462
- wake(0);
30463
30704
  }, delay);
30464
30705
  };
30465
30706
  return {
@@ -30534,7 +30775,8 @@ function createPeerOfficeFrameRunner(kernel, credentials, profile, observations,
30534
30775
  throw error;
30535
30776
  }),
30536
30777
  isRetention: (frame) => fromBinary(RuntimeRequestSchema, frame).command.case === "retainFrontier",
30537
- monotonicNanoseconds: () => observations.monotonicNanoseconds()
30778
+ monotonicNanoseconds: () => observations.monotonicNanoseconds(),
30779
+ onSelectionError: (error) => console.error("Nomos background paper selection failed", error)
30538
30780
  });
30539
30781
  const scheduleCorrespondence = (delayMillis = 0) => {
30540
30782
  lanes?.wake(delayMillis);
@@ -30765,13 +31007,19 @@ var candidateSessionReference = async (identity) => {
30765
31007
  };
30766
31008
  var executeGitFetchWithAlongside = async (effect, fetchImpl) => {
30767
31009
  if (!effect.alongside) return executeGitFetchEffect(effect, fetchImpl);
30768
- const [fetched, alongside] = await Promise.all([
30769
- executeGitFetchEffect(effect, fetchImpl),
30770
- executeHttpEffect(effect.alongside, fetchImpl)
30771
- ]);
30772
- if (fetched.outcome.case === "gitFetch" && alongside.outcome.case === "response") {
31010
+ const cancelAlongside = new AbortController();
31011
+ let alongside;
31012
+ void executeHttpEffect(effect.alongside, (input, init) => fetchImpl(input, {
31013
+ ...init,
31014
+ signal: init?.signal ? AbortSignal.any([init.signal, cancelAlongside.signal]) : cancelAlongside.signal
31015
+ })).then((receipt) => {
31016
+ alongside = receipt;
31017
+ });
31018
+ const fetched = await executeGitFetchEffect(effect, fetchImpl);
31019
+ if (fetched.outcome.case === "gitFetch" && alongside?.outcome.case === "response") {
30773
31020
  fetched.outcome.value.alongside = alongside.outcome.value;
30774
31021
  }
31022
+ cancelAlongside.abort();
30775
31023
  return fetched;
30776
31024
  };
30777
31025
  var executeGitPushEffect = async (effect, fetchImpl) => {
@@ -31139,7 +31387,8 @@ function socketEffects(invoke2, permits = () => true) {
31139
31387
  requestId: ++sequence,
31140
31388
  command: { case: "socketReceipt", value: create(PeerSocketReceiptSchema, { connectionId, observation }) }
31141
31389
  }));
31142
- void Promise.resolve().then(() => invoke2(frame)).catch(() => {
31390
+ void Promise.resolve().then(() => invoke2(frame)).catch((error) => {
31391
+ console.warn("[nomos-socket] receipt failed", { connectionId, error });
31143
31392
  sockets.get(connectionId)?.close();
31144
31393
  });
31145
31394
  };
@@ -31161,6 +31410,7 @@ function socketEffects(invoke2, permits = () => true) {
31161
31410
  socket.onopen = () => receipt(id, { case: "opened", value: true });
31162
31411
  socket.onmessage = (message) => {
31163
31412
  if (message.data instanceof ArrayBuffer) receipt(id, { case: "received", value: new Uint8Array(message.data) });
31413
+ else if (typeof message.data === "string") receipt(id, { case: "received", value: new TextEncoder().encode(message.data) });
31164
31414
  };
31165
31415
  socket.onclose = () => {
31166
31416
  if (sockets.get(id) === socket) sockets.delete(id);
@@ -31374,21 +31624,21 @@ var SqlitePeerDeskStore = class {
31374
31624
  }
31375
31625
  return this.#opened = database;
31376
31626
  }
31377
- list(namespace) {
31627
+ list(namespace2) {
31378
31628
  const rows = this.#database().prepare(`
31379
31629
  SELECT record_key AS key, revision, value
31380
31630
  FROM peer_desk_record
31381
31631
  WHERE namespace = ?
31382
31632
  ORDER BY record_key
31383
- `).all(namespace);
31633
+ `).all(namespace2);
31384
31634
  return rows.map((row) => ({
31385
- namespace,
31635
+ namespace: namespace2,
31386
31636
  key: row.key,
31387
31637
  revision: BigInt(row.revision),
31388
31638
  value: Uint8Array.from(row.value)
31389
31639
  }));
31390
31640
  }
31391
- compareExchange(namespace, key, expectedRevision, value) {
31641
+ compareExchange(namespace2, key, expectedRevision, value) {
31392
31642
  const expected = expectedRevision === void 0 ? void 0 : Number(expectedRevision);
31393
31643
  if (expected !== void 0 && (!Number.isSafeInteger(expected) || expected <= 0)) {
31394
31644
  throw new Error(`peer desk revision ${expectedRevision} cannot be represented by SQLite`);
@@ -31401,8 +31651,8 @@ var SqlitePeerDeskStore = class {
31401
31651
  const removed = database.prepare(`
31402
31652
  DELETE FROM peer_desk_record
31403
31653
  WHERE namespace = ? AND record_key = ? AND revision = ?
31404
- `).run(namespace, key, expected);
31405
- if (removed.changes !== 1) this.#conflict(namespace, key);
31654
+ `).run(namespace2, key, expected);
31655
+ if (removed.changes !== 1) this.#conflict(namespace2, key);
31406
31656
  return this.#commit(void 0);
31407
31657
  }
31408
31658
  const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
@@ -31414,14 +31664,14 @@ var SqlitePeerDeskStore = class {
31414
31664
  INSERT INTO peer_desk_record(namespace, record_key, revision, value)
31415
31665
  VALUES (?, ?, 1, ?)
31416
31666
  ON CONFLICT(namespace, record_key) DO NOTHING
31417
- `).run(namespace, key, bytes) : database.prepare(`
31667
+ `).run(namespace2, key, bytes) : database.prepare(`
31418
31668
  UPDATE peer_desk_record
31419
31669
  SET revision = ?, value = ?
31420
31670
  WHERE namespace = ? AND record_key = ? AND revision = ?
31421
- `).run(nextRevision, bytes, namespace, key, expected);
31422
- if (changed.changes !== 1) this.#conflict(namespace, key);
31671
+ `).run(nextRevision, bytes, namespace2, key, expected);
31672
+ if (changed.changes !== 1) this.#conflict(namespace2, key);
31423
31673
  return this.#commit({
31424
- namespace,
31674
+ namespace: namespace2,
31425
31675
  key,
31426
31676
  revision: BigInt(nextRevision),
31427
31677
  value: value.slice()
@@ -31439,12 +31689,12 @@ var SqlitePeerDeskStore = class {
31439
31689
  this.#database().exec("COMMIT");
31440
31690
  return value;
31441
31691
  }
31442
- #conflict(namespace, key) {
31692
+ #conflict(namespace2, key) {
31443
31693
  const current = this.#database().prepare(`
31444
31694
  SELECT revision FROM peer_desk_record WHERE namespace = ? AND record_key = ?
31445
- `).get(namespace, key);
31695
+ `).get(namespace2, key);
31446
31696
  throw new Error(
31447
- `peer desk record '${namespace}/${key}' changed at revision ${current?.revision ?? "absent"}`
31697
+ `peer desk record '${namespace2}/${key}' changed at revision ${current?.revision ?? "absent"}`
31448
31698
  );
31449
31699
  }
31450
31700
  };
@@ -31757,7 +32007,7 @@ var okBytes = async (fetchImpl, url) => {
31757
32007
  async function selectReleaseKernel(args) {
31758
32008
  const fetchImpl = args.fetchImpl ?? ((input, init) => globalThis.fetch(input, init));
31759
32009
  const base = args.base.replace(/\/+$/, "");
31760
- const repositoryUrl = `${base}/v1/runtime/repository.xml`;
32010
+ const repositoryUrl = args.repositoryUrl ?? `${base}/v1/runtime/repository.xml`;
31761
32011
  const repositoryResponse = await fetchImpl(repositoryUrl);
31762
32012
  if (!repositoryResponse.ok) {
31763
32013
  throw new Error(`custodian kernel repository ${repositoryUrl}: HTTP ${repositoryResponse.status}`);