@githolon/runtime 0.115.0 → 0.116.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RUNTIME-SOURCES.sha256 +29 -27
- package/dist/nomos-native-runtime.js +594 -199
- package/dist/nomos-runner-web.js +5 -1
- package/dist/nomos-runner-worker.js +922 -443
- package/dist/nomos-runner.js +921 -442
- package/dist/remote-reception.cjs +1 -1
- package/dist/remote-reception.js +1 -1
- package/dist/typescript-application-desk.js +1 -1
- package/package.json +1 -1
- package/runner/build.ts +6 -1
- package/runner/node-entry.mjs +701 -207
- package/src/protocol/nomos/runtime/v2/observability_desk_pb.d.ts +11 -0
- package/src/protocol/nomos/runtime/v2/observability_desk_pb.js +1 -1
package/runner/node-entry.mjs
CHANGED
|
@@ -1056,7 +1056,7 @@ var require_uri_all = __commonJS({
|
|
|
1056
1056
|
|
|
1057
1057
|
// ../../platform-shells/node/stdio/entry.ts
|
|
1058
1058
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
1059
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1059
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
1060
1060
|
import { existsSync as existsSync5, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync4, readFileSync as readFileSync3, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1061
1061
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
1062
1062
|
import { join as join5 } from "node:path";
|
|
@@ -5747,8 +5747,10 @@ var PrepareRemoteOfferResponseSchema = /* @__PURE__ */ messageDesc(file_nomos_ke
|
|
|
5747
5747
|
var SignOfferPreparationRequestSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 37);
|
|
5748
5748
|
var SigningIdentityRequestSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 39);
|
|
5749
5749
|
var KernelRefusalSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 55);
|
|
5750
|
+
var QueryResponseSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 56);
|
|
5750
5751
|
var ApplicationResultDeltaSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 58);
|
|
5751
5752
|
var InterfaceCompatibilityQuerySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 60);
|
|
5753
|
+
var ReadIndexPendingSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 64);
|
|
5752
5754
|
var MaintenanceQuantumQuerySchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 65);
|
|
5753
5755
|
var CarriedWorkspaceOfferSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 80);
|
|
5754
5756
|
var PrepareOutboundOfferSchema = /* @__PURE__ */ messageDesc(file_nomos_kernel_v1_kernel, 84);
|
|
@@ -6017,7 +6019,7 @@ var CustodyBreakPolicySchema = /* @__PURE__ */ enumDesc(file_nomos_runtime_v2_re
|
|
|
6017
6019
|
var CustodyBreakPolicy = /* @__PURE__ */ tsEnum(CustodyBreakPolicySchema);
|
|
6018
6020
|
|
|
6019
6021
|
// ../../adapter/proto/nomos/runtime/v2/observability_desk_pb.js
|
|
6020
|
-
var file_nomos_runtime_v2_observability_desk = /* @__PURE__ */ fileDesc("Cilub21vcy9ydW50aW1lL3YyL29ic2VydmFiaWxpdHlfZGVzay5wcm90bxIQbm9tb3MucnVudGltZS52MiJCCgxUcmFjZUNvbnRleHQSEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIPCgdzYW1wbGVkGAMgASgIIp0CCg5SdW50aW1lRmFpbHVyZRIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJY29tcG9uZW50GAMgASgJEhYKCXdvcmtzcGFjZRgEIAEoCUgAiAEBEhMKBmRvbWFpbhgFIAEoCUgBiAEBEhYKCWRpcmVjdGl2ZRgGIAEoCUgCiAEBEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGUSMgoEa2luZBgIIAEoDjIkLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVLaW5kQgwKCl93b3Jrc3BhY2VCCQoHX2RvbWFpbkIMCgpfZGlyZWN0aXZlIr4BChJSdW50aW1lVW5hdmFpbGFibGUSOgoGcmVhc29uGAEgASgOMioubm9tb3MucnVudGltZS52Mi5SdW50aW1lVW5hdmFpbGFibGVSZWFzb24SEQoJd29ya3NwYWNlGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIfChJyZXRyeV9hZnRlcl9taWxsaXMYBCABKA1IAIgBARIOCgZkZXRhaWwYBSABKAlCFQoTX3JldHJ5X2FmdGVyX21pbGxpcyJ+Cg5SdW50aW1lUmVmdXNhbBI2CgZyZWFzb24YASABKA4yJi5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVSZWZ1c2FsUmVhc29uEhEKCXdvcmtzcGFjZRgCIAEoCRIRCglyZXRyeWFibGUYAyABKAgSDgoGZGV0YWlsGAQgASgJIqUBChNEaWFnbm9zdGljQXR0cmlidXRlEgwKBG5hbWUYASABKAkSFgoMc3RyaW5nX3ZhbHVlGAIgASgJSAASFwoNaW50ZWdlcl92YWx1ZRgDIAEoEkgAEhYKDGRvdWJsZV92YWx1ZRgEIAEoAUgAEhcKDWJvb2xlYW5fdmFsdWUYBSABKAhIABIVCgtieXRlc192YWx1ZRgGIAEoDEgAQgcKBXZhbHVlIvALCgxSdW50aW1lRXZlbnQSEAoIc2VxdWVuY2UYASABKAQSFAoHc2Vzc2lvbhgCIAEoCUgBiAEBEi8KBXJlYWR5GAogASgLMh4ubm9tb3MucnVudGltZS52Mi5SdW50aW1lUmVhZHlIABI3Cg13YXRjaF9jaGFuZ2VkGAsgASgLMh4ubm9tb3MucnVudGltZS52Mi5XYXRjaENoYW5nZWRIABI5Cg5zeW5jX2NvbXBsZXRlZBgMIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU3luY0NvbXBsZXRlZEgAEjUKDGxpbmtfY2hhbmdlZBgNIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuTGlua0NoYW5nZWRIABI5Cg5zaWduYWxfY2hhbmdlZBgOIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU2lnbmFsQ2hhbmdlZEgAEjsKD2N1c3RvZHlfY2hhbmdlZBgPIAEoCzIgLm5vbW9zLnJ1bnRpbWUudjIuQ3VzdG9keUNoYW5nZWRIABJCChNsb2NhbF93b3JrX3JlamVjdGVkGBAgASgLMiMubm9tb3MucnVudGltZS52Mi5Mb2NhbFdvcmtSZWplY3RlZEgAEjMKC2xhd19jaGFuZ2VkGBEgASgLMhwubm9tb3MucnVudGltZS52Mi5MYXdDaGFuZ2VkSAASQwoPa2VybmVsX3Byb2dyZXNzGBIgASgLMigubm9tb3MucnVudGltZS52Mi5LZXJuZWxQcm9ncmVzc09ic2VydmVkSAASLQoEc3BhbhgTIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZVNwYW5IABI/
|
|
6022
|
+
var file_nomos_runtime_v2_observability_desk = /* @__PURE__ */ fileDesc("Cilub21vcy9ydW50aW1lL3YyL29ic2VydmFiaWxpdHlfZGVzay5wcm90bxIQbm9tb3MucnVudGltZS52MiJCCgxUcmFjZUNvbnRleHQSEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIPCgdzYW1wbGVkGAMgASgIIp0CCg5SdW50aW1lRmFpbHVyZRIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJY29tcG9uZW50GAMgASgJEhYKCXdvcmtzcGFjZRgEIAEoCUgAiAEBEhMKBmRvbWFpbhgFIAEoCUgBiAEBEhYKCWRpcmVjdGl2ZRgGIAEoCUgCiAEBEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGUSMgoEa2luZBgIIAEoDjIkLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVLaW5kQgwKCl93b3Jrc3BhY2VCCQoHX2RvbWFpbkIMCgpfZGlyZWN0aXZlIr4BChJSdW50aW1lVW5hdmFpbGFibGUSOgoGcmVhc29uGAEgASgOMioubm9tb3MucnVudGltZS52Mi5SdW50aW1lVW5hdmFpbGFibGVSZWFzb24SEQoJd29ya3NwYWNlGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIfChJyZXRyeV9hZnRlcl9taWxsaXMYBCABKA1IAIgBARIOCgZkZXRhaWwYBSABKAlCFQoTX3JldHJ5X2FmdGVyX21pbGxpcyJ+Cg5SdW50aW1lUmVmdXNhbBI2CgZyZWFzb24YASABKA4yJi5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVSZWZ1c2FsUmVhc29uEhEKCXdvcmtzcGFjZRgCIAEoCRIRCglyZXRyeWFibGUYAyABKAgSDgoGZGV0YWlsGAQgASgJIqUBChNEaWFnbm9zdGljQXR0cmlidXRlEgwKBG5hbWUYASABKAkSFgoMc3RyaW5nX3ZhbHVlGAIgASgJSAASFwoNaW50ZWdlcl92YWx1ZRgDIAEoEkgAEhYKDGRvdWJsZV92YWx1ZRgEIAEoAUgAEhcKDWJvb2xlYW5fdmFsdWUYBSABKAhIABIVCgtieXRlc192YWx1ZRgGIAEoDEgAQgcKBXZhbHVlIvALCgxSdW50aW1lRXZlbnQSEAoIc2VxdWVuY2UYASABKAQSFAoHc2Vzc2lvbhgCIAEoCUgBiAEBEi8KBXJlYWR5GAogASgLMh4ubm9tb3MucnVudGltZS52Mi5SdW50aW1lUmVhZHlIABI3Cg13YXRjaF9jaGFuZ2VkGAsgASgLMh4ubm9tb3MucnVudGltZS52Mi5XYXRjaENoYW5nZWRIABI5Cg5zeW5jX2NvbXBsZXRlZBgMIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU3luY0NvbXBsZXRlZEgAEjUKDGxpbmtfY2hhbmdlZBgNIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuTGlua0NoYW5nZWRIABI5Cg5zaWduYWxfY2hhbmdlZBgOIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU2lnbmFsQ2hhbmdlZEgAEjsKD2N1c3RvZHlfY2hhbmdlZBgPIAEoCzIgLm5vbW9zLnJ1bnRpbWUudjIuQ3VzdG9keUNoYW5nZWRIABJCChNsb2NhbF93b3JrX3JlamVjdGVkGBAgASgLMiMubm9tb3MucnVudGltZS52Mi5Mb2NhbFdvcmtSZWplY3RlZEgAEjMKC2xhd19jaGFuZ2VkGBEgASgLMhwubm9tb3MucnVudGltZS52Mi5MYXdDaGFuZ2VkSAASQwoPa2VybmVsX3Byb2dyZXNzGBIgASgLMigubm9tb3MucnVudGltZS52Mi5LZXJuZWxQcm9ncmVzc09ic2VydmVkSAASLQoEc3BhbhgTIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZVNwYW5IABI/ChF0cmFuc2Zlcl9wcm9ncmVzcxgUIAEoCzIiLm5vbW9zLnJ1bnRpbWUudjIuVHJhbnNmZXJQcm9ncmVzc0gAEkkKFG1haW50ZW5hbmNlX3Byb2dyZXNzGBUgASgLMikubm9tb3MucnVudGltZS52Mi5SZWFkTWFpbnRlbmFuY2VQcm9ncmVzc0gAEjsKDXNvY2tldF9lZmZlY3QYFiABKAsyIi5ub21vcy5ydW50aW1lLnYyLlBlZXJTb2NrZXRFZmZlY3RIABJUChxjcmVkZW50aWFsX3JlZnJlc2hfcmVxdWVzdGVkGBcgASgLMiwubm9tb3MucnVudGltZS52Mi5DcmVkZW50aWFsUmVmcmVzaFJlcXVlc3RlZEgAElIKG3JlY2VpdmluZ19ncm91cF9jb252ZXJnZW5jZRgYIAEoCzIrLm5vbW9zLnJ1bnRpbWUudjIuUmVjZWl2aW5nR3JvdXBDb252ZXJnZW5jZUgAEkoKF21vZGVsX2Fkb3B0aW9uX3Byb2dyZXNzGBkgASgLMicubm9tb3MucnVudGltZS52Mi5Nb2RlbEFkb3B0aW9uUHJvZ3Jlc3NIABI7Cg9mb2xsb3dzX3N1bW1hcnkYGiABKAsyIC5ub21vcy5ydW50aW1lLnYyLkZvbGxvd3NTdW1tYXJ5SAASRAoUbG9jYWxfY29weV9wcmVzZXJ2ZWQYGyABKAsyJC5ub21vcy5ydW50aW1lLnYyLlByZXNlcnZlZExvY2FsQ29weUgAEk4KGWZvcmVpZ25fcGxhY2VtZW50X2lnbm9yZWQYHCABKAsyKS5ub21vcy5ydW50aW1lLnYyLkZvcmVpZ25QbGFjZW1lbnRJZ25vcmVkSAASQwoTYmluZGluZ19xdWFyYW50aW5lZBgdIAEoCzIkLm5vbW9zLnJ1bnRpbWUudjIuUXVhcmFudGluZWRCaW5kaW5nSAASPwoRd3JpdGVfcHJlcGFyYXRpb24YHiABKAsyIi5ub21vcy5ydW50aW1lLnYyLldyaXRlUHJlcGFyYXRpb25IABJKChdyZWFkX2FjY2Vzc19wcmVwYXJhdGlvbhgfIAEoCzInLm5vbW9zLnJ1bnRpbWUudjIuUmVhZEFjY2Vzc1ByZXBhcmF0aW9uSABCBwoFZXZlbnRCCgoIX3Nlc3Npb24ivAEKDkZvbGxvd3NTdW1tYXJ5EhAKCGZvbGxvd2VkGAEgASgNEg8KB3NldHRsZWQYAiABKA0SDAoEbGl2ZRgDIAEoDRI3Cgp3b3Jrc3BhY2VzGAQgAygLMiMubm9tb3MucnVudGltZS52Mi5Gb2xsb3dPYnNlcnZhdGlvbhIkChdvYnNlcnZlZF9hdF91bml4X21pbGxpcxgFIAEoBEgAiAEBQhoKGF9vYnNlcnZlZF9hdF91bml4X21pbGxpcyL7AwoRRm9sbG93T2JzZXJ2YXRpb24SEQoJd29ya3NwYWNlGAEgASgJEjcKBXBoYXNlGAIgASgOMigubm9tb3MucnVudGltZS52Mi5Gb2xsb3dPYnNlcnZhdGlvblBoYXNlEgwKBGxpdmUYAyABKAgSEwoGZGV0YWlsGAQgASgJSACIAQESIQoUcmV0cnlfYXRfdW5peF9taWxsaXMYBSABKARIAYgBARIfChJkdWVfYXRfdW5peF9taWxsaXMYBiABKARIAogBARIUCgdhdHRlbXB0GAcgASgNSAOIAQESGgoNYWN0aXZlX2VmZmVjdBgIIAEoCUgEiAEBEhMKBnVuc2VudBgJIAEoDUgFiAEBEhsKDnVuYWNrbm93bGVkZ2VkGAogASgNSAaIAQESQgoRY3VzdG9kaWFuX3dhcm1pbmcYCyABKAsyIi5ub21vcy5ydW50aW1lLnYyLkN1c3RvZGlhbldhcm1pbmdIB4gBAUIJCgdfZGV0YWlsQhcKFV9yZXRyeV9hdF91bml4X21pbGxpc0IVChNfZHVlX2F0X3VuaXhfbWlsbGlzQgoKCF9hdHRlbXB0QhAKDl9hY3RpdmVfZWZmZWN0QgkKB191bnNlbnRCEQoPX3VuYWNrbm93bGVkZ2VkQhQKEl9jdXN0b2RpYW5fd2FybWluZyKFAQoQQ3VzdG9kaWFuV2FybWluZxIZChFzaW5jZV91bml4X21pbGxpcxgBIAEoBBIiChpleHBlY3RlZF9yZWFkeV91bml4X21pbGxpcxgCIAEoBBIhChlzdGFsbGVkX2FmdGVyX3VuaXhfbWlsbGlzGAMgASgEEg8KB3N0YWxsZWQYBCABKAgiVgoaQ3JlZGVudGlhbFJlZnJlc2hSZXF1ZXN0ZWQSOAoHcGVuZGluZxgBIAEoCzInLm5vbW9zLnJ1bnRpbWUudjIuQ29ycmVzcG9uZGVuY2VQZW5kaW5nIj4KDFJ1bnRpbWVSZWFkeRIXCg9ydW50aW1lX3ZlcnNpb24YASABKAkSFQoNa2VybmVsX3NoYTI1NhgCIAEoDCKNAwoMV2F0Y2hDaGFuZ2VkEhcKD3N1YnNjcmlwdGlvbl9pZBgBIAEoCRITCgtyZXN1bHRfdXNkYRgCIAEoDBI2CgdmYWlsdXJlGAMgASgLMiAubm9tb3MucnVudGltZS52Mi5SdW50aW1lRmFpbHVyZUgAiAEBEjUKEmFwcGxpY2F0aW9uX3Jlc3VsdBgEIAEoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnlIAYgBARIaCg1pbnRlZ2VyX3ZhbHVlGAUgASgDSAKIAQESRwoRYXBwbGljYXRpb25fZGVsdGEYBiABKAsyJy5ub21vcy5rZXJuZWwudjEuQXBwbGljYXRpb25SZXN1bHREZWx0YUgDiAEBEhwKD3Jlc3VsdF9mcm9udGllchgHIAEoCUgEiAEBQgoKCF9mYWlsdXJlQhUKE19hcHBsaWNhdGlvbl9yZXN1bHRCEAoOX2ludGVnZXJfdmFsdWVCFAoSX2FwcGxpY2F0aW9uX2RlbHRhQhIKEF9yZXN1bHRfZnJvbnRpZXIinAEKDVN5bmNDb21wbGV0ZWQSDgoGcmVhc29uGAEgASgJEjcKBnJlc3VsdBgCIAEoCzInLm5vbW9zLnJ1bnRpbWUudjIuU3luY1dvcmtzcGFjZVJlc3BvbnNlEjYKB2ZhaWx1cmUYAyABKAsyIC5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVGYWlsdXJlSACIAQFCCgoIX2ZhaWx1cmUigAEKC0xpbmtDaGFuZ2VkEiwKBnN0YXR1cxgBIAEoDjIcLm5vbW9zLnJ1bnRpbWUudjIuTGlua1N0YXR1cxI3CgdjbG9zdXJlGAIgASgLMiEubm9tb3MucnVudGltZS52Mi5MaXZlTGlua0Nsb3N1cmVIAIgBAUIKCghfY2xvc3VyZSI4Cg1TaWduYWxDaGFuZ2VkEg4KBmRvbWFpbhgBIAEoCRIXCg9wcm9qZWN0aW9uX3VzZGEYAiABKAwiUAoKTGF3Q2hhbmdlZBIOCgZkb21haW4YASABKAkSFgoOcGFja2FnZV9zaGEyNTYYAiABKAkSGgoSY29tcGF0aWJpbGl0eV91c2RhGAMgASgMIt0BCgtSdW50aW1lU3BhbhIQCgh0cmFjZV9pZBgBIAEoDBIPCgdzcGFuX2lkGAIgASgMEhsKDnBhcmVudF9zcGFuX2lkGAMgASgMSACIAQESDAoEbmFtZRgEIAEoCRIaChJzdGFydGVkX3VuaXhfbmFub3MYBSABKAQSFgoOZHVyYXRpb25fbmFub3MYBiABKAQSOQoKYXR0cmlidXRlcxgHIAMoCzIlLm5vbW9zLnJ1bnRpbWUudjIuRGlhZ25vc3RpY0F0dHJpYnV0ZUIRCg9fcGFyZW50X3NwYW5faWQi6gEKFktlcm5lbFByb2dyZXNzT2JzZXJ2ZWQSOQoIc25hcHNob3QYASABKAsyJy5ub21vcy5rZXJuZWwudjEuS2VybmVsUHJvZ3Jlc3NTbmFwc2hvdBIkChxob3N0X29ic2VydmVkX2VsYXBzZWRfbWljcm9zGAIgASgEEioKImhvc3Rfb2JzZXJ2ZWRfcGhhc2VfZWxhcHNlZF9taWNyb3MYAyABKAQSHgoWcHJvY2Vzc19yZXNpZGVudF9ieXRlcxgEIAEoBBIjChtwcm9jZXNzX3BlYWtfcmVzaWRlbnRfYnl0ZXMYBSABKAQiggEKEFRyYW5zZmVyUHJvZ3Jlc3MSFgoOcmVjZWl2ZWRfYnl0ZXMYASABKAQSGAoLdG90YWxfYnl0ZXMYAiABKARIAIgBARIsCgRraW5kGAMgASgOMh4ubm9tb3MucnVudGltZS52Mi5UcmFuc2ZlcktpbmRCDgoMX3RvdGFsX2J5dGVzIjkKEFdyaXRlUHJlcGFyYXRpb24SEgoKcmVxdWVzdF9pZBgBIAEoCRIRCgl3b3Jrc3BhY2UYAiABKAkiPgoVUmVhZEFjY2Vzc1ByZXBhcmF0aW9uEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJd29ya3NwYWNlGAIgASgJKsoBChJSdW50aW1lRmFpbHVyZUtpbmQSJAogUlVOVElNRV9GQUlMVVJFX0tJTkRfVU5TUEVDSUZJRUQQABIhCh1SVU5USU1FX0ZBSUxVUkVfS0lORF9JTlRFUk5BTBABEiEKHVJVTlRJTUVfRkFJTFVSRV9LSU5EX1BST1RPQ09MEAISIgoeUlVOVElNRV9GQUlMVVJFX0tJTkRfSU5URUdSSVRZEAMSJAogUlVOVElNRV9GQUlMVVJFX0tJTkRfVU5TVVBQT1JURUQQBCqnAwoYUnVudGltZVVuYXZhaWxhYmxlUmVhc29uEioKJlJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1VOU1BFQ0lGSUVEEAASMgouUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fUkVNT1RFX09OTFlfT0ZGTElORRABEjEKLVJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1JFTU9URV9VTlJFQUNIQUJMRRACEi0KKVJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1JFTU9URV9USU1FT1VUEAMSNgoyUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fTE9DQUxfUkVQTElDQV9OT1RfUkVBRFkQBBI1CjFSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9DUkVERU5USUFMX1VOQVZBSUxBQkxFEAUSMAosUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fV09SS1NQQUNFX09QRU5JTkcQBhIoCiRSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9QRUVSX0JVU1kQByrwAQoUUnVudGltZVJlZnVzYWxSZWFzb24SJgoiUlVOVElNRV9SRUZVU0FMX1JFQVNPTl9VTlNQRUNJRklFRBAAEi0KKVJVTlRJTUVfUkVGVVNBTF9SRUFTT05fSU5WQUxJRF9DUkVERU5USUFMEAESKAokUlVOVElNRV9SRUZVU0FMX1JFQVNPTl9BQ0NFU1NfREVOSUVEEAISKwonUlVOVElNRV9SRUZVU0FMX1JFQVNPTl9QTEFDRU1FTlRfUE9MSUNZEAMSKgomUlVOVElNRV9SRUZVU0FMX1JFQVNPTl9TRVNTSU9OX1VOQk9VTkQQBCrCBAoWRm9sbG93T2JzZXJ2YXRpb25QaGFzZRIoCiRGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfVU5TUEVDSUZJRUQQABIkCiBGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfREVTSVJFRBABEiAKHEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9EVUUQAhIkCiBGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfUlVOTklORxADEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9TRVRUTEVEEAQSMAosRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0FXQUlUSU5HX0RFUEVOREVOQ1kQBRIkCiBGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfT0ZGTElORRAGEi8KK0ZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9JTlZBTElEX0NSRURFTlRJQUwQBxIkCiBGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfQkFDS09GRhAIEiwKKEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9SRUJPUk5fUkVNT1RFTFkQCRIsCihGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfUkVQTEFDSU5HX0xPQ0FMEAoSMgouRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0xPQ0FMX0NPUFlfVU5SRUFEQUJMRRALEisKJ0ZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9LRVJORUxfUkVGVVNFRBAMKlcKCkxpbmtTdGF0dXMSGwoXTElOS19TVEFUVVNfVU5TUEVDSUZJRUQQABIUChBMSU5LX1NUQVRVU19PUEVOEAESFgoSTElOS19TVEFUVVNfQ0xPU0VEEAIqdgoMVHJhbnNmZXJLaW5kEh0KGVRSQU5TRkVSX0tJTkRfVU5TUEVDSUZJRUQQABIXChNUUkFOU0ZFUl9LSU5EX0RFTFRBEAESFgoSVFJBTlNGRVJfS0lORF9GVUxMEAISFgoSVFJBTlNGRVJfS0lORF9QVVNIEANiBnByb3RvMw", [file_google_protobuf_any, file_nomos_kernel_v1_kernel, file_nomos_kernel_v1_progress, file_nomos_runtime_v2_correspondence_desk, file_nomos_runtime_v2_custody_desk, file_nomos_runtime_v2_placement_desk, file_nomos_runtime_v2_maintenance_desk, file_nomos_runtime_v2_model_adoption_desk, file_nomos_runtime_v2_receiving_group_desk, file_nomos_runtime_v2_recovery_desk]);
|
|
6021
6023
|
var RuntimeFailureSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 1);
|
|
6022
6024
|
var RuntimeUnavailableSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 2);
|
|
6023
6025
|
var RuntimeRefusalSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 3);
|
|
@@ -8032,6 +8034,11 @@ var kernelReadinessPending = (message) => {
|
|
|
8032
8034
|
if (!READINESS_DEBT.test(message)) return void 0;
|
|
8033
8035
|
return new KernelReadinessPending(/source workspace '([^']+)'/.exec(message)?.[1]?.trim(), message);
|
|
8034
8036
|
};
|
|
8037
|
+
var READ_INDEX_BUILDING = /the read index of (.+?) is (?:being built \(\d+ of \d+ documents\)|built \(\d+ documents\) and is being bound); read maintenance (?:continues|finishes) it/;
|
|
8038
|
+
var kernelReadIndexBuilding = (message) => {
|
|
8039
|
+
const workspace = READ_INDEX_BUILDING.exec(message)?.[1]?.trim();
|
|
8040
|
+
return workspace === void 0 ? void 0 : { workspace };
|
|
8041
|
+
};
|
|
8035
8042
|
var requiredForeignWorkspace = (message) => {
|
|
8036
8043
|
const patterns = [
|
|
8037
8044
|
/workspace '([^']+)' must be resident in the kernel/,
|
|
@@ -8055,9 +8062,31 @@ function throwIfKernelControlRequired(failure) {
|
|
|
8055
8062
|
throw new KernelWorkspaceResidencyRequired(workspace, failure.message);
|
|
8056
8063
|
}
|
|
8057
8064
|
}
|
|
8065
|
+
var KernelRefusal = class extends Error {
|
|
8066
|
+
code;
|
|
8067
|
+
failure;
|
|
8068
|
+
constructor(failure) {
|
|
8069
|
+
super(`${failure.code}: ${failure.message}`);
|
|
8070
|
+
this.name = "KernelRefusal";
|
|
8071
|
+
this.code = failure.code;
|
|
8072
|
+
this.failure = failure;
|
|
8073
|
+
}
|
|
8074
|
+
};
|
|
8058
8075
|
function throwKernelFailure(failure) {
|
|
8059
8076
|
throwIfKernelControlRequired(failure);
|
|
8060
|
-
throw new
|
|
8077
|
+
throw new KernelRefusal(failure);
|
|
8078
|
+
}
|
|
8079
|
+
var KERNEL_FAILURE_TEXT = /^(NMS-[A-Z0-9]+(?:-[A-Z0-9]+)*): ([\s\S]*)$/;
|
|
8080
|
+
var ENVIRONMENTAL = /NMS-KERNEL-TRAP|\btrap\b|unreachable|out of bounds|out of memory|memory allocation|\block\b|File exists|No such file|os error|I\/O|Permission denied|No space|\bE(?:EXIST|NOENT|ACCES|NOSPC|IO|AGAIN|BUSY)\b/i;
|
|
8081
|
+
function deterministicKernelRefusal(error) {
|
|
8082
|
+
if (error instanceof KernelWorkspaceResidencyRequired || error instanceof KernelHistoryRequired || error instanceof KernelReadinessPending) return void 0;
|
|
8083
|
+
if (error instanceof KernelRefusal && (error.failure.historyRequired !== void 0 || error.failure.identityProviderWitnessRequired !== void 0)) return void 0;
|
|
8084
|
+
const text = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
8085
|
+
const match = KERNEL_FAILURE_TEXT.exec(text);
|
|
8086
|
+
if (!match) return void 0;
|
|
8087
|
+
const [, code, message] = match;
|
|
8088
|
+
if (ENVIRONMENTAL.test(text) || READINESS_DEBT.test(message) || requiredForeignWorkspace(message) !== void 0) return void 0;
|
|
8089
|
+
return { code, message };
|
|
8061
8090
|
}
|
|
8062
8091
|
function rethrowTypedKernelFailure(error) {
|
|
8063
8092
|
if (error instanceof KernelWorkspaceResidencyRequired || error instanceof KernelHistoryRequired) throw error;
|
|
@@ -14096,8 +14125,9 @@ function memoryCheckpointStaging() {
|
|
|
14096
14125
|
|
|
14097
14126
|
// ../src/runtime/live_inbound_stream.ts
|
|
14098
14127
|
var LEVEL_BATCH = "level:";
|
|
14128
|
+
var HISTORY_REQUIRED = "nomos.stream.history_required";
|
|
14129
|
+
var CONTAINMENT_ASK_SUFFIX = ":contain";
|
|
14099
14130
|
var STREAM_WINDOW_CHUNKS = 8;
|
|
14100
|
-
var ASK_AGAIN_NANOS = 15000000000n;
|
|
14101
14131
|
var historySlot = (workspace) => `${workspace}#history`;
|
|
14102
14132
|
var LiveInboundStream = class {
|
|
14103
14133
|
#ports;
|
|
@@ -14115,7 +14145,10 @@ var LiveInboundStream = class {
|
|
|
14115
14145
|
}
|
|
14116
14146
|
return inbound;
|
|
14117
14147
|
}
|
|
14118
|
-
/** When this workspace's stream last delivered
|
|
14148
|
+
/** When this workspace's stream last delivered data to an ask (a waiting procedure is progressing).
|
|
14149
|
+
* A refusal, a frontier, unrelated traffic on the socket and this copy's own re-asking are not
|
|
14150
|
+
* progress: a procedure whose stream only ever says "ask again" gives up and its round fails over
|
|
14151
|
+
* (CAP-838). */
|
|
14119
14152
|
lastHeard(workspace) {
|
|
14120
14153
|
return this.#by.get(workspace)?.lastHeard;
|
|
14121
14154
|
}
|
|
@@ -14138,7 +14171,8 @@ var LiveInboundStream = class {
|
|
|
14138
14171
|
inbound.wants.level = {
|
|
14139
14172
|
effectId: effect.effectId,
|
|
14140
14173
|
maxOffers: Math.max(1, request.value.maxOffers),
|
|
14141
|
-
remountAfterOffers: request.value.remountAfterOffers
|
|
14174
|
+
remountAfterOffers: request.value.remountAfterOffers,
|
|
14175
|
+
contain: effect.effectId.endsWith(CONTAINMENT_ASK_SUFFIX)
|
|
14142
14176
|
};
|
|
14143
14177
|
this.#askLevel(effect.workspace, inbound);
|
|
14144
14178
|
}
|
|
@@ -14198,7 +14232,7 @@ var LiveInboundStream = class {
|
|
|
14198
14232
|
const batch = inbound.levelBatch;
|
|
14199
14233
|
if (batch) {
|
|
14200
14234
|
inbound.levelBatch = void 0;
|
|
14201
|
-
if (batch.base === this.#ports.head(effect.workspace)) {
|
|
14235
|
+
if (batch.base === this.#ports.head(effect.workspace) || batch.base === request.value.head) {
|
|
14202
14236
|
inbound.wants.level = void 0;
|
|
14203
14237
|
return create(PeerStreamReceiptSchema, {
|
|
14204
14238
|
outcome: { case: "pack", value: batch },
|
|
@@ -14209,14 +14243,14 @@ var LiveInboundStream = class {
|
|
|
14209
14243
|
this.#askLevel(effect.workspace, inbound);
|
|
14210
14244
|
}
|
|
14211
14245
|
const head = this.#ports.head(effect.workspace);
|
|
14212
|
-
if (
|
|
14246
|
+
if (inbound.announced && (head === inbound.announced || inbound.askedFrom === inbound.announced)) {
|
|
14213
14247
|
inbound.wants.level = void 0;
|
|
14214
14248
|
return create(PeerStreamReceiptSchema, {
|
|
14215
|
-
outcome: { case: "level", value:
|
|
14249
|
+
outcome: { case: "level", value: inbound.announced },
|
|
14216
14250
|
...inbound.sealed ? { sealed: inbound.sealed } : {}
|
|
14217
14251
|
});
|
|
14218
14252
|
}
|
|
14219
|
-
return this.#refusalReceipt(inbound);
|
|
14253
|
+
return this.#refusalReceipt(inbound, inbound.wants.level?.effectId === effect.effectId && inbound.wants.level.contain);
|
|
14220
14254
|
}
|
|
14221
14255
|
if (request.case === "history" || request.case === "ref") {
|
|
14222
14256
|
const want = inbound.wants.packs.get(request.value.requestId);
|
|
@@ -14231,10 +14265,16 @@ var LiveInboundStream = class {
|
|
|
14231
14265
|
return void 0;
|
|
14232
14266
|
}
|
|
14233
14267
|
/** A terminal refusal (not one that only says when to ask again) ends the procedure's wait. */
|
|
14234
|
-
#refusalReceipt(inbound) {
|
|
14268
|
+
#refusalReceipt(inbound, contain = false) {
|
|
14235
14269
|
const refused = inbound.refused;
|
|
14236
|
-
if (!refused
|
|
14270
|
+
if (!refused) return void 0;
|
|
14271
|
+
const answersContainment = contain && refused.code === HISTORY_REQUIRED;
|
|
14272
|
+
if (refused.retryAfterMillis !== void 0 && !answersContainment) return void 0;
|
|
14237
14273
|
inbound.refused = void 0;
|
|
14274
|
+
if (answersContainment) {
|
|
14275
|
+
inbound.askAgainAt = void 0;
|
|
14276
|
+
inbound.wants.level = void 0;
|
|
14277
|
+
}
|
|
14238
14278
|
return create(PeerStreamReceiptSchema, { outcome: { case: "refusal", value: refused } });
|
|
14239
14279
|
}
|
|
14240
14280
|
/** The procedure gave up (it timed out, or its work ended). */
|
|
@@ -14265,7 +14305,9 @@ var LiveInboundStream = class {
|
|
|
14265
14305
|
/** One frame from the peer. True when it was the stream's (the live desk handles the rest). */
|
|
14266
14306
|
frame(workspace, message) {
|
|
14267
14307
|
const inbound = this.#of(workspace);
|
|
14268
|
-
|
|
14308
|
+
if (message.case === "checkpointOffer" || message.case === "checkpointChunk" || message.case === "pack") {
|
|
14309
|
+
inbound.lastHeard = this.#ports.monotonicNanoseconds();
|
|
14310
|
+
}
|
|
14269
14311
|
switch (message.case) {
|
|
14270
14312
|
case "frontier":
|
|
14271
14313
|
if (message.value.response && message.value.head) {
|
|
@@ -14320,18 +14362,26 @@ var LiveInboundStream = class {
|
|
|
14320
14362
|
return false;
|
|
14321
14363
|
}
|
|
14322
14364
|
}
|
|
14323
|
-
/**
|
|
14365
|
+
/**
|
|
14366
|
+
* Ask again when a refusal said when to (the peer was opening, or acquiring history). Nothing else
|
|
14367
|
+
* here runs on a timer (CAP-838): the socket is ordered and reliable, every ask is answered on it,
|
|
14368
|
+
* and an ask is re-sent only when its socket re-opens (`opened`) or its refusal's time comes. The
|
|
14369
|
+
* stream used to re-ask whatever had heard nothing for 15 s, and that resend - itself counted as
|
|
14370
|
+
* hearing - hid an ask answered "come back" for ever.
|
|
14371
|
+
*/
|
|
14324
14372
|
drive(workspace) {
|
|
14325
14373
|
const inbound = this.#by.get(workspace);
|
|
14326
|
-
if (!inbound ||
|
|
14374
|
+
if (!inbound || inbound.askAgainAt === void 0) return void 0;
|
|
14375
|
+
if (!this.wanted(workspace)) {
|
|
14376
|
+
inbound.askAgainAt = void 0;
|
|
14377
|
+
return void 0;
|
|
14378
|
+
}
|
|
14327
14379
|
const now2 = this.#ports.monotonicNanoseconds();
|
|
14328
|
-
|
|
14329
|
-
if (now2 < due2) return due2 - now2;
|
|
14380
|
+
if (now2 < inbound.askAgainAt) return inbound.askAgainAt - now2;
|
|
14330
14381
|
inbound.askAgainAt = void 0;
|
|
14331
14382
|
inbound.refused = void 0;
|
|
14332
|
-
inbound.lastHeard = now2;
|
|
14333
14383
|
this.opened(workspace);
|
|
14334
|
-
return
|
|
14384
|
+
return void 0;
|
|
14335
14385
|
}
|
|
14336
14386
|
#offered(workspace, inbound, offer) {
|
|
14337
14387
|
inbound.refused = void 0;
|
|
@@ -14374,6 +14424,7 @@ var LiveInboundStream = class {
|
|
|
14374
14424
|
const held = !taken ? -1 : "pack" in taken ? taken.pack.byteLength : taken.bytes;
|
|
14375
14425
|
if (held !== transfer.bytes || transfer.received !== transfer.bytes) {
|
|
14376
14426
|
if (taken && "stagedPackFile" in taken) this.#staging.clear(workspace);
|
|
14427
|
+
if (inbound.wants.checkpoint) this.#askCheckpoint(workspace, inbound);
|
|
14377
14428
|
return;
|
|
14378
14429
|
}
|
|
14379
14430
|
inbound.complete = {
|
|
@@ -14491,8 +14542,12 @@ var LiveInboundStream = class {
|
|
|
14491
14542
|
this.#askCheckpoint(workspace, inbound);
|
|
14492
14543
|
return;
|
|
14493
14544
|
}
|
|
14545
|
+
inbound.announced = void 0;
|
|
14546
|
+
inbound.sealed = void 0;
|
|
14547
|
+
const head = this.#ports.head(workspace) ?? "";
|
|
14548
|
+
inbound.askedFrom = head;
|
|
14494
14549
|
this.#ports.send(workspace, { case: "frontier", value: create(LiveFrontierSchema, {
|
|
14495
|
-
head
|
|
14550
|
+
head,
|
|
14496
14551
|
response: false,
|
|
14497
14552
|
maxOffers: level.maxOffers,
|
|
14498
14553
|
remountAfterOffers: level.remountAfterOffers,
|
|
@@ -15011,6 +15066,24 @@ function placementMovedBy(request, response) {
|
|
|
15011
15066
|
return void 0;
|
|
15012
15067
|
}
|
|
15013
15068
|
var PLACEMENT_REFERENCE = "refs/nomos/placement";
|
|
15069
|
+
function readIndexPendingAnswer(request, message) {
|
|
15070
|
+
if (request.operation.case !== "query" || request.operation.value.query.case === "maintenanceQuantum") return void 0;
|
|
15071
|
+
const building = kernelReadIndexBuilding(message);
|
|
15072
|
+
if (building === void 0) return void 0;
|
|
15073
|
+
const target3 = request.operation.value.target;
|
|
15074
|
+
return create(CallResponseSchema, {
|
|
15075
|
+
protocolVersion: request.protocolVersion,
|
|
15076
|
+
requestId: request.requestId,
|
|
15077
|
+
outcome: { case: "query", value: create(QueryResponseSchema, {
|
|
15078
|
+
result: { case: "readIndexPending", value: create(ReadIndexPendingSchema, {
|
|
15079
|
+
workspace: target3?.workspace || building.workspace,
|
|
15080
|
+
branch: target3?.branch ?? "",
|
|
15081
|
+
// The sliced build names no frontier: it restarts at whatever head main holds when it continues.
|
|
15082
|
+
requestedFrontier: ""
|
|
15083
|
+
}) }
|
|
15084
|
+
}) }
|
|
15085
|
+
});
|
|
15086
|
+
}
|
|
15014
15087
|
var KernelPort = class {
|
|
15015
15088
|
#kernel;
|
|
15016
15089
|
#activeOperation;
|
|
@@ -16035,6 +16108,8 @@ var KernelPort = class {
|
|
|
16035
16108
|
const nested = request.operation.case === "custody" ? request.operation.value.operation.case ?? "empty" : request.operation.case === "query" ? request.operation.value.query.case === "named" ? `named:${request.operation.value.query.value.operation}` : request.operation.value.query.case ?? "empty" : "";
|
|
16036
16109
|
error.message = `kernel-operation=${top}/${nested}: ${error.message}`;
|
|
16037
16110
|
}
|
|
16111
|
+
const pending = readIndexPendingAnswer(request, error instanceof Error ? error.message : String(error));
|
|
16112
|
+
if (pending !== void 0) return pending;
|
|
16038
16113
|
rethrowTypedKernelFailure(error);
|
|
16039
16114
|
}
|
|
16040
16115
|
if (response.requestId !== request.requestId) {
|
|
@@ -16045,6 +16120,9 @@ var KernelPort = class {
|
|
|
16045
16120
|
this.#residency.after(request, response);
|
|
16046
16121
|
const placementMoved = placementMovedBy(request, response);
|
|
16047
16122
|
if (placementMoved !== void 0) this.#onPlacementMoved(placementMoved);
|
|
16123
|
+
if (response.outcome.case === "failure") {
|
|
16124
|
+
return readIndexPendingAnswer(request, response.outcome.value.message) ?? response;
|
|
16125
|
+
}
|
|
16048
16126
|
return response;
|
|
16049
16127
|
}
|
|
16050
16128
|
/** [#call] with the opt-in timeline trace: encode, kernel and decode timed separately. */
|
|
@@ -22674,6 +22752,7 @@ var ReadMaintenanceDesk = class {
|
|
|
22674
22752
|
for (const [key, value] of entries.filter(([key2]) => key2 !== workspace)) this.#pending.set(key, value);
|
|
22675
22753
|
}
|
|
22676
22754
|
this.#revision += 1;
|
|
22755
|
+
if (read && (!existing?.read || isReadDemand)) this.#observer.readinessMayChange?.(workspace);
|
|
22677
22756
|
if (!existing || existing.read?.cause !== read?.cause || existing.background?.cause !== background?.cause) {
|
|
22678
22757
|
this.#trace.emit(session ?? existing?.session, {
|
|
22679
22758
|
case: "maintenanceProgress",
|
|
@@ -22690,32 +22769,65 @@ var ReadMaintenanceDesk = class {
|
|
|
22690
22769
|
* (a waiting read's own turn advances read readiness only).
|
|
22691
22770
|
*/
|
|
22692
22771
|
tick(request, sessionlessNotBeforeNanoseconds = 0n, backgroundNotBeforeNanoseconds = 0n, durability = true) {
|
|
22772
|
+
const turn = this.#turn(
|
|
22773
|
+
request,
|
|
22774
|
+
sessionlessNotBeforeNanoseconds,
|
|
22775
|
+
{ case: "until", atNanoseconds: backgroundNotBeforeNanoseconds },
|
|
22776
|
+
durability
|
|
22777
|
+
);
|
|
22778
|
+
return turn.case === "ran" ? turn.ticked : create(MaintenanceTickedSchema, {
|
|
22779
|
+
pending: turn.pending,
|
|
22780
|
+
nextTickAfterMillis: turn.nextDelayMillis ?? 0
|
|
22781
|
+
});
|
|
22782
|
+
}
|
|
22783
|
+
/**
|
|
22784
|
+
* A waiting read's turn: advance read readiness only. It never runs a background command, closure
|
|
22785
|
+
* warm, pack import or durability flush on the foreground's turn, and never reports a wake for
|
|
22786
|
+
* that background work: with only background work owed its wake is `none`.
|
|
22787
|
+
*/
|
|
22788
|
+
tickReadReadiness() {
|
|
22789
|
+
const turn = this.#turn(void 0, 0n, { case: "readReadinessOnly" }, false);
|
|
22790
|
+
if (turn.case === "ran") {
|
|
22791
|
+
return { ticked: turn.ticked, wake: { case: "after", millis: turn.ticked.nextTickAfterMillis } };
|
|
22792
|
+
}
|
|
22793
|
+
return {
|
|
22794
|
+
ticked: create(MaintenanceTickedSchema, { pending: turn.pending, nextTickAfterMillis: turn.nextDelayMillis ?? 0 }),
|
|
22795
|
+
wake: turn.nextDelayMillis === void 0 ? { case: "none" } : { case: "after", millis: turn.nextDelayMillis }
|
|
22796
|
+
};
|
|
22797
|
+
}
|
|
22798
|
+
#turn(request, sessionlessNotBeforeNanoseconds, backgroundHold, durability) {
|
|
22693
22799
|
const now2 = this.#clock.monotonicNanoseconds();
|
|
22800
|
+
const backgroundNotBeforeNanoseconds = backgroundHold.case === "until" ? backgroundHold.atNanoseconds : void 0;
|
|
22694
22801
|
const custodySlice = (workspace2) => this.#custodyBurstSlice(now2, workspace2);
|
|
22695
22802
|
const due2 = (demand2) => {
|
|
22696
22803
|
const held = demand2.session || demand2.awaited || demand2.retryNotBeforeNanoseconds > sessionlessNotBeforeNanoseconds ? demand2.retryNotBeforeNanoseconds : sessionlessNotBeforeNanoseconds;
|
|
22697
|
-
|
|
22804
|
+
if (!demand2.read && !demand2.awaited && backgroundNotBeforeNanoseconds === void 0) return void 0;
|
|
22805
|
+
const ordinary = demand2.read || demand2.awaited || backgroundNotBeforeNanoseconds === void 0 || held > backgroundNotBeforeNanoseconds ? held : backgroundNotBeforeNanoseconds;
|
|
22698
22806
|
const hold = demand2.read || demand2.awaited || !demand2.background?.maintainGitCustody ? void 0 : custodySlice(demand2.workspace);
|
|
22699
22807
|
if (!hold) return ordinary;
|
|
22700
22808
|
if (hold.slice && durability) return held > hold.at ? held : hold.at;
|
|
22701
22809
|
return ordinary > hold.at ? ordinary : hold.at;
|
|
22702
22810
|
};
|
|
22703
|
-
const entry = [...this.#pending.entries()].find(([, demand2]) =>
|
|
22811
|
+
const entry = [...this.#pending.entries()].find(([, demand2]) => {
|
|
22812
|
+
const at = due2(demand2);
|
|
22813
|
+
return at !== void 0 && at <= now2;
|
|
22814
|
+
});
|
|
22704
22815
|
const flush = this.#flush;
|
|
22705
22816
|
if (durability && flush && this.#flushDueAt(flush) <= now2 && !entry?.[1].read && (flush.local || !entry || flush.owedSinceNanoseconds + DURABILITY_FLUSH_MAX_DELAY_NANOSECONDS <= now2)) {
|
|
22706
|
-
return this.#tickFlush(flush, request);
|
|
22817
|
+
return { case: "ran", ticked: this.#tickFlush(flush, request) };
|
|
22707
22818
|
}
|
|
22708
22819
|
const pinDue = (pin) => {
|
|
22820
|
+
if (backgroundNotBeforeNanoseconds === void 0) return void 0;
|
|
22709
22821
|
const held = pin.session ? 0n : sessionlessNotBeforeNanoseconds;
|
|
22710
22822
|
return [pin.notBeforeNanoseconds, held, backgroundNotBeforeNanoseconds].reduce((latest, at) => at > latest ? at : latest, 0n);
|
|
22711
22823
|
};
|
|
22712
22824
|
if (!entry) {
|
|
22713
|
-
const pin = [...this.#pins.entries()].find(([, idle]) =>
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
pending: this.hasPending(),
|
|
22717
|
-
nextTickAfterMillis: this.#nextDelay(now2, due2, pinDue)
|
|
22825
|
+
const pin = [...this.#pins.entries()].find(([, idle]) => {
|
|
22826
|
+
const at = pinDue(idle);
|
|
22827
|
+
return at !== void 0 && at <= now2;
|
|
22718
22828
|
});
|
|
22829
|
+
if (pin) return { case: "ran", ticked: this.#tickPin(pin[0], pin[1], request) };
|
|
22830
|
+
return { case: "idle", pending: this.hasPending(), nextDelayMillis: this.#nextDelay(now2, due2, pinDue, durability) };
|
|
22719
22831
|
}
|
|
22720
22832
|
const [workspace, demand] = entry;
|
|
22721
22833
|
const mount = demand.session ? this.#sessions.mountForSession(demand.session) : this.#sessions.mountForWorkspace(workspace);
|
|
@@ -22764,7 +22876,7 @@ var ReadMaintenanceDesk = class {
|
|
|
22764
22876
|
read: read !== void 0,
|
|
22765
22877
|
awaited: demand.awaited === true,
|
|
22766
22878
|
session: demand.session !== void 0,
|
|
22767
|
-
backgroundHoldMs: Number((backgroundNotBeforeNanoseconds - now2) / 1000000n),
|
|
22879
|
+
backgroundHoldMs: backgroundNotBeforeNanoseconds === void 0 ? "read-readiness-only" : Number((backgroundNotBeforeNanoseconds - now2) / 1000000n),
|
|
22768
22880
|
sessionlessHoldMs: Number((sessionlessNotBeforeNanoseconds - now2) / 1000000n),
|
|
22769
22881
|
caller: (new Error().stack ?? "").split("\n").slice(2, 6).map((frame2) => frame2.trim()).join(" < ")
|
|
22770
22882
|
})}`);
|
|
@@ -22813,12 +22925,13 @@ var ReadMaintenanceDesk = class {
|
|
|
22813
22925
|
this.#observer.frontierAdvanced?.(workspace);
|
|
22814
22926
|
}
|
|
22815
22927
|
}
|
|
22816
|
-
|
|
22928
|
+
this.#observer.readinessMayChange?.(workspace);
|
|
22929
|
+
return { case: "ran", ticked: create(MaintenanceTickedSchema, {
|
|
22817
22930
|
pending: this.#pending.size > 0,
|
|
22818
22931
|
progress: observed,
|
|
22819
22932
|
// With no debt left, an armed idle pin names when the host should wake next.
|
|
22820
|
-
nextTickAfterMillis: progress.maintenancePending ? 20 : this.#pending.size === 0 && this.hasIdleWork() ? this.#nextDelay(this.#clock.monotonicNanoseconds()) : 0
|
|
22821
|
-
});
|
|
22933
|
+
nextTickAfterMillis: progress.maintenancePending ? 20 : this.#pending.size === 0 && this.hasIdleWork() ? this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0 : 0
|
|
22934
|
+
}) };
|
|
22822
22935
|
} catch (error) {
|
|
22823
22936
|
const format = localCopyFormatRefusal(error);
|
|
22824
22937
|
if (format !== void 0) {
|
|
@@ -22834,10 +22947,10 @@ var ReadMaintenanceDesk = class {
|
|
|
22834
22947
|
stopped: true
|
|
22835
22948
|
})
|
|
22836
22949
|
});
|
|
22837
|
-
return create(MaintenanceTickedSchema, {
|
|
22950
|
+
return { case: "ran", ticked: create(MaintenanceTickedSchema, {
|
|
22838
22951
|
pending: this.#pending.size > 0,
|
|
22839
|
-
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds())
|
|
22840
|
-
});
|
|
22952
|
+
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0
|
|
22953
|
+
}) };
|
|
22841
22954
|
}
|
|
22842
22955
|
const failures = demand.failures + 1;
|
|
22843
22956
|
this.#pending.delete(workspace);
|
|
@@ -22865,12 +22978,12 @@ var ReadMaintenanceDesk = class {
|
|
|
22865
22978
|
retryAfterMillis: retryMillis
|
|
22866
22979
|
})
|
|
22867
22980
|
});
|
|
22868
|
-
return create(MaintenanceTickedSchema, {
|
|
22981
|
+
return { case: "ran", ticked: create(MaintenanceTickedSchema, {
|
|
22869
22982
|
pending: true,
|
|
22870
22983
|
// Backoff belongs to the failed workspace. Another workspace's ready
|
|
22871
22984
|
// index or changed watches must not wait for this retry deadline.
|
|
22872
|
-
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds())
|
|
22873
|
-
});
|
|
22985
|
+
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0
|
|
22986
|
+
}) };
|
|
22874
22987
|
}
|
|
22875
22988
|
}
|
|
22876
22989
|
/** Owe the kernel its ordered flush. The first write it covers fixes `owedSince`; the shared
|
|
@@ -22935,7 +23048,7 @@ var ReadMaintenanceDesk = class {
|
|
|
22935
23048
|
}
|
|
22936
23049
|
return create(MaintenanceTickedSchema, {
|
|
22937
23050
|
pending: this.hasPending(),
|
|
22938
|
-
nextTickAfterMillis: progress.maintenancePending ? 20 : this.#nextDelay(this.#clock.monotonicNanoseconds())
|
|
23051
|
+
nextTickAfterMillis: progress.maintenancePending ? 20 : this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0
|
|
22939
23052
|
});
|
|
22940
23053
|
} catch (error) {
|
|
22941
23054
|
const failures = flush.failures + 1;
|
|
@@ -22959,7 +23072,7 @@ var ReadMaintenanceDesk = class {
|
|
|
22959
23072
|
};
|
|
22960
23073
|
return create(MaintenanceTickedSchema, {
|
|
22961
23074
|
pending: this.hasPending(),
|
|
22962
|
-
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds())
|
|
23075
|
+
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0
|
|
22963
23076
|
});
|
|
22964
23077
|
}
|
|
22965
23078
|
}
|
|
@@ -22990,7 +23103,7 @@ var ReadMaintenanceDesk = class {
|
|
|
22990
23103
|
}
|
|
22991
23104
|
return create(MaintenanceTickedSchema, {
|
|
22992
23105
|
pending: this.hasPending(),
|
|
22993
|
-
nextTickAfterMillis: progress.maintenancePending ? 20 : this.#nextDelay(this.#clock.monotonicNanoseconds())
|
|
23106
|
+
nextTickAfterMillis: progress.maintenancePending ? 20 : this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0
|
|
22994
23107
|
});
|
|
22995
23108
|
} catch (error) {
|
|
22996
23109
|
slowQuantumObserver?.(`[nomos-kernel-perf] ${JSON.stringify({
|
|
@@ -23002,28 +23115,31 @@ var ReadMaintenanceDesk = class {
|
|
|
23002
23115
|
})}`);
|
|
23003
23116
|
return create(MaintenanceTickedSchema, {
|
|
23004
23117
|
pending: this.hasPending(),
|
|
23005
|
-
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds())
|
|
23118
|
+
nextTickAfterMillis: this.#nextDelay(this.#clock.monotonicNanoseconds()) ?? 0
|
|
23006
23119
|
});
|
|
23007
23120
|
}
|
|
23008
23121
|
}
|
|
23009
|
-
|
|
23122
|
+
/** Milliseconds until the earliest owed work this turn serves; `undefined` when none is owed. */
|
|
23123
|
+
#nextDelay(now2, due2 = (demand) => demand.retryNotBeforeNanoseconds, pinDue = (pin) => pin.notBeforeNanoseconds, flushes = true) {
|
|
23010
23124
|
let earliest;
|
|
23011
23125
|
for (const demand of this.#pending.values()) {
|
|
23012
23126
|
const at = due2(demand);
|
|
23127
|
+
if (at === void 0) continue;
|
|
23013
23128
|
if (at <= now2) return 0;
|
|
23014
23129
|
if (earliest === void 0 || at < earliest) earliest = at;
|
|
23015
23130
|
}
|
|
23016
23131
|
for (const pin of this.#pins.values()) {
|
|
23017
23132
|
const at = pinDue(pin);
|
|
23133
|
+
if (at === void 0) continue;
|
|
23018
23134
|
if (at <= now2) return 0;
|
|
23019
23135
|
if (earliest === void 0 || at < earliest) earliest = at;
|
|
23020
23136
|
}
|
|
23021
|
-
if (this.#flush) {
|
|
23137
|
+
if (flushes && this.#flush) {
|
|
23022
23138
|
const at = this.#flushDueAt(this.#flush);
|
|
23023
23139
|
if (at <= now2) return 0;
|
|
23024
23140
|
if (earliest === void 0 || at < earliest) earliest = at;
|
|
23025
23141
|
}
|
|
23026
|
-
return earliest === void 0 ? 0 : Number((earliest - now2 + 999999n) / 1000000n);
|
|
23142
|
+
return earliest === void 0 ? void 0 : Number((earliest - now2 + 999999n) / 1000000n);
|
|
23027
23143
|
}
|
|
23028
23144
|
/** Drop any demand for a workspace no maintenance can serve (an unreadable copy). */
|
|
23029
23145
|
forget(workspace) {
|
|
@@ -23064,15 +23180,19 @@ var ReadMaintenanceDesk = class {
|
|
|
23064
23180
|
|
|
23065
23181
|
// ../src/runtime/read_response_desk.ts
|
|
23066
23182
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
23183
|
+
var READ_RETENTION_MILLIS = 12e4;
|
|
23184
|
+
var READ_RESUME_MAX_MILLIS = 1e3;
|
|
23067
23185
|
var ReadResponseDesk = class {
|
|
23068
23186
|
#waiting = /* @__PURE__ */ new Map();
|
|
23069
23187
|
#clock;
|
|
23070
23188
|
#tick;
|
|
23189
|
+
#owe;
|
|
23071
23190
|
#read;
|
|
23072
23191
|
#sequence = 0;
|
|
23073
|
-
constructor(clock2, tick, read) {
|
|
23192
|
+
constructor(clock2, tick, owe, read) {
|
|
23074
23193
|
this.#clock = clock2;
|
|
23075
23194
|
this.#tick = tick;
|
|
23195
|
+
this.#owe = owe;
|
|
23076
23196
|
this.#read = read;
|
|
23077
23197
|
}
|
|
23078
23198
|
#now() {
|
|
@@ -23085,18 +23205,27 @@ var ReadResponseDesk = class {
|
|
|
23085
23205
|
const now2 = this.#now();
|
|
23086
23206
|
for (const [id, work] of this.#waiting) if (work.deadline <= now2) this.#waiting.delete(id);
|
|
23087
23207
|
const workId2 = `local-read-${++this.#sequence}`;
|
|
23088
|
-
this.#waiting.set(workId2, {
|
|
23208
|
+
this.#waiting.set(workId2, {
|
|
23209
|
+
request,
|
|
23210
|
+
dependency,
|
|
23211
|
+
deadline: now2 + READ_RETENTION_MILLIS,
|
|
23212
|
+
notBefore: now2,
|
|
23213
|
+
idle: false,
|
|
23214
|
+
// The office owed this frontier's debt when the kernel first answered pending.
|
|
23215
|
+
owedFor: dependency.requestedFrontier,
|
|
23216
|
+
waiters: /* @__PURE__ */ new Set()
|
|
23217
|
+
});
|
|
23089
23218
|
return create(LocalReadPendingSchema, { workId: workId2 });
|
|
23090
23219
|
}
|
|
23091
23220
|
resume(workId2, requestId) {
|
|
23092
23221
|
const work = this.#waiting.get(workId2);
|
|
23093
23222
|
if (!work || work.request.requestId !== requestId) throw new Error(`unknown local read continuation '${workId2}'`);
|
|
23094
23223
|
const now2 = this.#now();
|
|
23224
|
+
work.waiters.clear();
|
|
23095
23225
|
if (now2 >= work.deadline) {
|
|
23096
23226
|
this.#waiting.delete(workId2);
|
|
23097
23227
|
throw new Error(`Local read index for '${work.dependency.workspace}' did not catch up to ${work.dependency.requestedFrontier} within its retained maintenance window`);
|
|
23098
23228
|
}
|
|
23099
|
-
if (now2 < work.notBefore) return this.#pending(workId2, work, now2);
|
|
23100
23229
|
try {
|
|
23101
23230
|
const response = this.#read(work.request);
|
|
23102
23231
|
if (response.outcome.case !== "query" || response.outcome.value.result.case !== "readIndexPending") {
|
|
@@ -23104,24 +23233,65 @@ var ReadResponseDesk = class {
|
|
|
23104
23233
|
return { case: "ready", request: work.request, response };
|
|
23105
23234
|
}
|
|
23106
23235
|
work.dependency = response.outcome.value.result.value;
|
|
23107
|
-
|
|
23236
|
+
if (now2 < work.notBefore) return this.#pending(workId2, work, now2);
|
|
23237
|
+
let { ticked, wake } = this.#tick();
|
|
23238
|
+
if (wake.case === "none" && ticked.progress === void 0 && work.owedFor !== work.dependency.requestedFrontier) {
|
|
23239
|
+
work.owedFor = work.dependency.requestedFrontier;
|
|
23240
|
+
this.#owe(work.request);
|
|
23241
|
+
({ ticked, wake } = this.#tick());
|
|
23242
|
+
}
|
|
23108
23243
|
const afterTick = this.#now();
|
|
23109
|
-
work.
|
|
23110
|
-
|
|
23244
|
+
work.idle = wake.case === "none";
|
|
23245
|
+
const backoff = wake.case === "after" ? wake.millis : 0;
|
|
23246
|
+
work.notBefore = afterTick + backoff;
|
|
23247
|
+
work.deadline = afterTick + Math.max(READ_RETENTION_MILLIS, backoff + 6e4);
|
|
23111
23248
|
return this.#pending(workId2, work, afterTick);
|
|
23112
23249
|
} catch (error) {
|
|
23113
23250
|
this.#waiting.delete(workId2);
|
|
23114
23251
|
throw error;
|
|
23115
23252
|
}
|
|
23116
23253
|
}
|
|
23254
|
+
/**
|
|
23255
|
+
* Wait in process for a held read to become worth continuing. `notify` is called once, when read
|
|
23256
|
+
* readiness for its workspace changes (a slice or binding finished, a frontier advanced, readiness
|
|
23257
|
+
* newly owed). `afterMillis` is the only timer the shell needs: the read's own scheduled quantum
|
|
23258
|
+
* when it has one, otherwise its deadline. Undefined for an unknown or finished read.
|
|
23259
|
+
*/
|
|
23260
|
+
onResumable(workId2, notify) {
|
|
23261
|
+
const work = this.#waiting.get(workId2);
|
|
23262
|
+
if (!work) return void 0;
|
|
23263
|
+
const now2 = this.#now();
|
|
23264
|
+
const due2 = work.idle ? work.deadline : Math.min(work.notBefore, work.deadline);
|
|
23265
|
+
work.waiters.add(notify);
|
|
23266
|
+
return { afterMillis: Math.max(0, due2 - now2), cancel: () => {
|
|
23267
|
+
work.waiters.delete(notify);
|
|
23268
|
+
} };
|
|
23269
|
+
}
|
|
23270
|
+
/** Read readiness for `workspace` changed: every read waiting on it may continue now. */
|
|
23271
|
+
readinessChanged(workspace) {
|
|
23272
|
+
const now2 = this.#now();
|
|
23273
|
+
for (const work of this.#waiting.values()) {
|
|
23274
|
+
if (work.dependency.workspace !== workspace) continue;
|
|
23275
|
+
work.idle = false;
|
|
23276
|
+
if (work.notBefore > now2) work.notBefore = now2;
|
|
23277
|
+
const waiters = [...work.waiters];
|
|
23278
|
+
work.waiters.clear();
|
|
23279
|
+
for (const notify of waiters) notify();
|
|
23280
|
+
}
|
|
23281
|
+
}
|
|
23282
|
+
/** The protocol wake is a bounded re-poll inside the read's retention deadline, never the raw backoff. */
|
|
23117
23283
|
#pending(workId2, work, now2) {
|
|
23284
|
+
const due2 = work.idle ? READ_RESUME_MAX_MILLIS : work.notBefore - now2;
|
|
23285
|
+
const resumeAfterMillis = Math.max(0, Math.min(due2, work.deadline - now2, READ_RESUME_MAX_MILLIS));
|
|
23118
23286
|
return { case: "pending", value: create(LocalReadPendingSchema, {
|
|
23119
23287
|
workId: workId2,
|
|
23120
|
-
resumeAfterMillis:
|
|
23288
|
+
resumeAfterMillis: Number.isFinite(resumeAfterMillis) ? Math.ceil(resumeAfterMillis) : READ_RESUME_MAX_MILLIS
|
|
23121
23289
|
}) };
|
|
23122
23290
|
}
|
|
23123
23291
|
clear() {
|
|
23292
|
+
const waiters = [...this.#waiting.values()].flatMap((work) => [...work.waiters]);
|
|
23124
23293
|
this.#waiting.clear();
|
|
23294
|
+
for (const notify of waiters) notify();
|
|
23125
23295
|
}
|
|
23126
23296
|
};
|
|
23127
23297
|
|
|
@@ -24027,13 +24197,15 @@ var ReplicaFollowEventKind = Object.freeze({
|
|
|
24027
24197
|
DependencyAdvanced: "dependency_advanced",
|
|
24028
24198
|
RebornRemotely: "reborn_remotely",
|
|
24029
24199
|
LocalCopyDropAuthorized: "local_copy_drop_authorized",
|
|
24030
|
-
LocalCopyUnreadable: "local_copy_unreadable"
|
|
24200
|
+
LocalCopyUnreadable: "local_copy_unreadable",
|
|
24201
|
+
RuntimeStarted: "runtime_started"
|
|
24031
24202
|
});
|
|
24032
24203
|
var ReplicaFollowEffectKind = Object.freeze({
|
|
24033
24204
|
ScheduleWake: "schedule_wake",
|
|
24034
24205
|
RunSync: "run_sync",
|
|
24035
24206
|
RefreshCredential: "refresh_credential"
|
|
24036
24207
|
});
|
|
24208
|
+
var KERNEL_REFUSAL_EVENT_FLOOR_MILLIS = 1e4;
|
|
24037
24209
|
var boundedRetry = (policy, attempt) => Math.min(policy.fallbackMillis, Math.max(250, policy.debounceMillis) * 2 ** Math.min(attempt - 1, 20));
|
|
24038
24210
|
var attempts = (state) => {
|
|
24039
24211
|
switch (state.case) {
|
|
@@ -24060,13 +24232,14 @@ var due = (atUnixMillis) => ({
|
|
|
24060
24232
|
state: { case: ReplicaFollowPhase.Due, dueAtUnixMillis: atUnixMillis },
|
|
24061
24233
|
effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis }]
|
|
24062
24234
|
});
|
|
24063
|
-
var run = (attempt, replaceLocalHead, warmingSinceUnixMillis) => ({
|
|
24235
|
+
var run = (attempt, replaceLocalHead, warmingSinceUnixMillis, refused) => ({
|
|
24064
24236
|
state: {
|
|
24065
24237
|
case: ReplicaFollowPhase.Running,
|
|
24066
24238
|
attempt,
|
|
24067
24239
|
observed: false,
|
|
24068
24240
|
...replaceLocalHead === void 0 ? {} : { replaceLocalHead },
|
|
24069
|
-
...warmingSinceUnixMillis === void 0 ? {} : { warmingSinceUnixMillis }
|
|
24241
|
+
...warmingSinceUnixMillis === void 0 ? {} : { warmingSinceUnixMillis },
|
|
24242
|
+
...refused === void 0 ? {} : { refused }
|
|
24070
24243
|
},
|
|
24071
24244
|
effects: [{ case: ReplicaFollowEffectKind.RunSync }]
|
|
24072
24245
|
});
|
|
@@ -24075,6 +24248,16 @@ var replacing = (expectedLocalHead, attempt, atUnixMillis) => ({
|
|
|
24075
24248
|
effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis }]
|
|
24076
24249
|
});
|
|
24077
24250
|
var quiet = (state) => ({ state, effects: [] });
|
|
24251
|
+
var blockedRefusal = (state) => state.case === ReplicaFollowPhase.Backoff && state.refusal?.blocked ? state.refusal : void 0;
|
|
24252
|
+
var refusedEvent = (state, refusal2, nowUnixMillis) => {
|
|
24253
|
+
const earliest = Math.max(nowUnixMillis, refusal2.atUnixMillis + KERNEL_REFUSAL_EVENT_FLOOR_MILLIS);
|
|
24254
|
+
const rerunAtUnixMillis = refusal2.rerunAtUnixMillis === void 0 ? earliest : Math.min(refusal2.rerunAtUnixMillis, earliest);
|
|
24255
|
+
return {
|
|
24256
|
+
state: { ...state, refusal: { ...refusal2, rerunAtUnixMillis } },
|
|
24257
|
+
effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis: rerunAtUnixMillis }]
|
|
24258
|
+
};
|
|
24259
|
+
};
|
|
24260
|
+
var refusedByKernel = (state) => state.case === ReplicaFollowPhase.Backoff && (state.refusal !== void 0 || deterministicKernelRefusal(state.failure) !== void 0);
|
|
24078
24261
|
function reduceReplicaFollow(state, event, policy) {
|
|
24079
24262
|
switch (event.case) {
|
|
24080
24263
|
case ReplicaFollowEventKind.Activate:
|
|
@@ -24087,6 +24270,9 @@ function reduceReplicaFollow(state, event, policy) {
|
|
|
24087
24270
|
if (state.case === ReplicaFollowPhase.Running) {
|
|
24088
24271
|
return { state: { ...state, observed: true }, effects: [] };
|
|
24089
24272
|
}
|
|
24273
|
+
if (state.case === ReplicaFollowPhase.Backoff && state.refusal?.blocked) {
|
|
24274
|
+
return refusedEvent(state, state.refusal, event.nowUnixMillis);
|
|
24275
|
+
}
|
|
24090
24276
|
if (state.case === ReplicaFollowPhase.RebornRemotely || state.case === ReplicaFollowPhase.LocalCopyUnreadable) return quiet(state);
|
|
24091
24277
|
if (state.case === ReplicaFollowPhase.InvalidCredential) {
|
|
24092
24278
|
return {
|
|
@@ -24096,6 +24282,9 @@ function reduceReplicaFollow(state, event, policy) {
|
|
|
24096
24282
|
}
|
|
24097
24283
|
return due(event.nowUnixMillis);
|
|
24098
24284
|
case ReplicaFollowEventKind.Reconnected:
|
|
24285
|
+
if (state.case === ReplicaFollowPhase.Backoff && state.refusal?.blocked) {
|
|
24286
|
+
return refusedEvent(state, state.refusal, event.nowUnixMillis);
|
|
24287
|
+
}
|
|
24099
24288
|
switch (state.case) {
|
|
24100
24289
|
case ReplicaFollowPhase.Running:
|
|
24101
24290
|
return { state: { ...state, observed: true }, effects: [] };
|
|
@@ -24141,15 +24330,25 @@ function reduceReplicaFollow(state, event, policy) {
|
|
|
24141
24330
|
}
|
|
24142
24331
|
return run(state.attempt + 1, state.expectedLocalHead);
|
|
24143
24332
|
case ReplicaFollowPhase.Offline:
|
|
24144
|
-
case ReplicaFollowPhase.Backoff:
|
|
24333
|
+
case ReplicaFollowPhase.Backoff: {
|
|
24334
|
+
const blocked = blockedRefusal(state);
|
|
24335
|
+
if (blocked) {
|
|
24336
|
+
if (blocked.rerunAtUnixMillis === void 0) return quiet(state);
|
|
24337
|
+
if (event.nowUnixMillis < blocked.rerunAtUnixMillis) {
|
|
24338
|
+
return { state, effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis: blocked.rerunAtUnixMillis }] };
|
|
24339
|
+
}
|
|
24340
|
+
return run(state.attempt + 1, void 0, void 0, state.failure);
|
|
24341
|
+
}
|
|
24145
24342
|
if (event.nowUnixMillis < state.retryAtUnixMillis) {
|
|
24146
24343
|
return { state, effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis: state.retryAtUnixMillis }] };
|
|
24147
24344
|
}
|
|
24148
24345
|
return run(
|
|
24149
24346
|
state.attempt + 1,
|
|
24150
24347
|
void 0,
|
|
24151
|
-
state.case === ReplicaFollowPhase.Backoff ? state.warming?.sinceUnixMillis : void 0
|
|
24348
|
+
state.case === ReplicaFollowPhase.Backoff ? state.warming?.sinceUnixMillis : void 0,
|
|
24349
|
+
state.case === ReplicaFollowPhase.Backoff && state.refusal ? state.failure : void 0
|
|
24152
24350
|
);
|
|
24351
|
+
}
|
|
24153
24352
|
case ReplicaFollowPhase.InvalidCredential:
|
|
24154
24353
|
if (!state.refreshDue && event.nowUnixMillis < state.retryAtUnixMillis) {
|
|
24155
24354
|
return { state, effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis: state.retryAtUnixMillis }] };
|
|
@@ -24226,6 +24425,31 @@ function reduceReplicaFollow(state, event, policy) {
|
|
|
24226
24425
|
};
|
|
24227
24426
|
}
|
|
24228
24427
|
case ReplicaFollowEventKind.Failed: {
|
|
24428
|
+
if (event.refusal && !(state.case === ReplicaFollowPhase.Running && state.replaceLocalHead !== void 0)) {
|
|
24429
|
+
const attempt2 = Math.max(1, attempts(state));
|
|
24430
|
+
const blocked = state.case === ReplicaFollowPhase.Running && state.refused === event.failure || state.case === ReplicaFollowPhase.Backoff && state.refusal !== void 0 && state.failure === event.failure;
|
|
24431
|
+
const refusal2 = {
|
|
24432
|
+
code: event.refusal.code,
|
|
24433
|
+
blocked,
|
|
24434
|
+
runtime: event.refusal.runtime,
|
|
24435
|
+
atUnixMillis: event.nowUnixMillis
|
|
24436
|
+
};
|
|
24437
|
+
if (!blocked) {
|
|
24438
|
+
const retryAtUnixMillis2 = event.nowUnixMillis + boundedRetry(policy, attempt2);
|
|
24439
|
+
return {
|
|
24440
|
+
state: { case: ReplicaFollowPhase.Backoff, attempt: attempt2, retryAtUnixMillis: retryAtUnixMillis2, failure: event.failure, refusal: refusal2 },
|
|
24441
|
+
effects: [{ case: ReplicaFollowEffectKind.ScheduleWake, atUnixMillis: retryAtUnixMillis2 }]
|
|
24442
|
+
};
|
|
24443
|
+
}
|
|
24444
|
+
const waiting = {
|
|
24445
|
+
case: ReplicaFollowPhase.Backoff,
|
|
24446
|
+
attempt: attempt2,
|
|
24447
|
+
retryAtUnixMillis: event.nowUnixMillis,
|
|
24448
|
+
failure: event.failure,
|
|
24449
|
+
refusal: refusal2
|
|
24450
|
+
};
|
|
24451
|
+
return state.case === ReplicaFollowPhase.Running && state.observed ? refusedEvent(waiting, refusal2, event.nowUnixMillis) : quiet(waiting);
|
|
24452
|
+
}
|
|
24229
24453
|
if (state.case === ReplicaFollowPhase.Running && state.observed) {
|
|
24230
24454
|
return due(event.nowUnixMillis);
|
|
24231
24455
|
}
|
|
@@ -24263,6 +24487,11 @@ function reduceReplicaFollow(state, event, policy) {
|
|
|
24263
24487
|
return quiet({ case: ReplicaFollowPhase.RebornRemotely, rebirth: event.rebirth });
|
|
24264
24488
|
case ReplicaFollowEventKind.LocalCopyUnreadable:
|
|
24265
24489
|
return quiet({ case: ReplicaFollowPhase.LocalCopyUnreadable, format: event.format });
|
|
24490
|
+
case ReplicaFollowEventKind.RuntimeStarted:
|
|
24491
|
+
if (state.case === ReplicaFollowPhase.Backoff && refusedByKernel(state) && state.refusal?.runtime !== event.runtime) {
|
|
24492
|
+
return due(event.nowUnixMillis);
|
|
24493
|
+
}
|
|
24494
|
+
return quiet(state);
|
|
24266
24495
|
case ReplicaFollowEventKind.LocalCopyDropAuthorized:
|
|
24267
24496
|
if (state.case === ReplicaFollowPhase.Running) return invalid(state, event);
|
|
24268
24497
|
return replacing(event.expectedLocalHead, 0, event.nowUnixMillis);
|
|
@@ -24309,11 +24538,15 @@ var ReplicaFollowController = class {
|
|
|
24309
24538
|
#replicaId;
|
|
24310
24539
|
/** Local children whose births custody refused (CAP-804): quarantined, never followed. */
|
|
24311
24540
|
#birthRefusals;
|
|
24312
|
-
|
|
24541
|
+
/** The kernel/runtime identity this process runs: a refusal is recorded under it, and a refusal
|
|
24542
|
+
* drawn under another identity is retried once when this process starts. */
|
|
24543
|
+
#runtime;
|
|
24544
|
+
constructor(desk, clock2, replicaId2, birthRefusals, runtime = "unidentified") {
|
|
24313
24545
|
this.#desk = desk;
|
|
24314
24546
|
this.#clock = clock2;
|
|
24315
24547
|
this.#replicaId = replicaId2;
|
|
24316
24548
|
this.#birthRefusals = birthRefusals ?? new BirthRefusalRegister(new MemoryPeerDeskStore());
|
|
24549
|
+
this.#runtime = runtime;
|
|
24317
24550
|
}
|
|
24318
24551
|
/** The durable quarantine of a local child whose birth custody refused; undefined otherwise. */
|
|
24319
24552
|
birthRefusal(workspace) {
|
|
@@ -24412,6 +24645,13 @@ var ReplicaFollowController = class {
|
|
|
24412
24645
|
recover() {
|
|
24413
24646
|
const now2 = this.#now();
|
|
24414
24647
|
if (!this.#recovered) {
|
|
24648
|
+
for (const follow of this.#desk.all()) {
|
|
24649
|
+
this.#desk.transition(follow.spec.workspace, {
|
|
24650
|
+
case: ReplicaFollowEventKind.RuntimeStarted,
|
|
24651
|
+
nowUnixMillis: now2,
|
|
24652
|
+
runtime: this.#runtime
|
|
24653
|
+
});
|
|
24654
|
+
}
|
|
24415
24655
|
for (const follow of this.#desk.all()) {
|
|
24416
24656
|
if (follow.state.case === ReplicaFollowPhase.Settled || follow.state.case === ReplicaFollowPhase.AwaitingDependency) {
|
|
24417
24657
|
this.#placementRecheck.add(follow.spec.workspace);
|
|
@@ -24500,11 +24740,12 @@ var ReplicaFollowController = class {
|
|
|
24500
24740
|
placementAnnounced(workspace) {
|
|
24501
24741
|
if (!this.followForWorkspace(workspace)) return void 0;
|
|
24502
24742
|
this.#placementRecheck.add(workspace);
|
|
24503
|
-
return this.reconnected(workspace);
|
|
24743
|
+
return this.reconnected(workspace, true);
|
|
24504
24744
|
}
|
|
24505
|
-
/** The upstream live link opened or reopened: reconcile once for missed pushes.
|
|
24506
|
-
|
|
24507
|
-
|
|
24745
|
+
/** The upstream live link opened or reopened: reconcile once for missed pushes. With
|
|
24746
|
+
* `remoteAdvanced`, the upstream announced a new frontier. Either re-runs a blocked refusal. */
|
|
24747
|
+
reconnected(workspace, remoteAdvanced = false) {
|
|
24748
|
+
return this.#event(workspace, ReplicaFollowEventKind.Reconnected, remoteAdvanced);
|
|
24508
24749
|
}
|
|
24509
24750
|
/** Another workspace advanced or settled; every held dependency gets one retry. */
|
|
24510
24751
|
dependencyAdvanced(except) {
|
|
@@ -24517,11 +24758,11 @@ var ReplicaFollowController = class {
|
|
|
24517
24758
|
}
|
|
24518
24759
|
return woken;
|
|
24519
24760
|
}
|
|
24520
|
-
#event(workspace, kind) {
|
|
24761
|
+
#event(workspace, kind, remoteAdvanced = false) {
|
|
24521
24762
|
const follow = this.#recovered ? this.#desk.get(workspace) : void 0;
|
|
24522
24763
|
if (!follow) return void 0;
|
|
24523
24764
|
const nowUnixMillis = this.#now();
|
|
24524
|
-
this.#desk.transition(workspace, { case: kind, nowUnixMillis });
|
|
24765
|
+
this.#desk.transition(workspace, kind === ReplicaFollowEventKind.Reconnected && remoteAdvanced ? { case: kind, nowUnixMillis, remoteAdvanced } : { case: kind, nowUnixMillis });
|
|
24525
24766
|
const nextDueAt = dueAt(this.#desk.get(workspace));
|
|
24526
24767
|
return nextDueAt === void 0 ? void 0 : Math.max(0, nextDueAt - nowUnixMillis);
|
|
24527
24768
|
}
|
|
@@ -24668,7 +24909,9 @@ var ReplicaFollowController = class {
|
|
|
24668
24909
|
* still opening the workspace and named when to ask again. */
|
|
24669
24910
|
failed(workspace, error, custodianRetryAfterMillis) {
|
|
24670
24911
|
if (!this.#recovered) this.recover();
|
|
24671
|
-
|
|
24912
|
+
const refusal2 = deterministicKernelRefusal(error);
|
|
24913
|
+
if (refusal2 !== void 0) custodianRetryAfterMillis = void 0;
|
|
24914
|
+
else custodianRetryAfterMillis ??= warmingFromFailureMessage(error instanceof Error ? error.message : String(error));
|
|
24672
24915
|
if (!this.#desk.get(workspace)) return;
|
|
24673
24916
|
const format = localCopyFormatRefusal(error);
|
|
24674
24917
|
if (format !== void 0) {
|
|
@@ -24683,9 +24926,15 @@ var ReplicaFollowController = class {
|
|
|
24683
24926
|
case: ReplicaFollowEventKind.Failed,
|
|
24684
24927
|
nowUnixMillis: this.#now(),
|
|
24685
24928
|
failure: error instanceof Error ? error.message : String(error),
|
|
24686
|
-
...custodianRetryAfterMillis === void 0 ? {} : { custodianRetryAfterMillis }
|
|
24929
|
+
...custodianRetryAfterMillis === void 0 ? {} : { custodianRetryAfterMillis },
|
|
24930
|
+
...refusal2 === void 0 ? {} : { refusal: { code: refusal2.code, runtime: this.#runtime } }
|
|
24687
24931
|
});
|
|
24688
24932
|
}
|
|
24933
|
+
/** The kernel refusal behind this follow's backoff (`blocked` once the kernel repeated it). */
|
|
24934
|
+
refusal(workspace) {
|
|
24935
|
+
const state = this.followForWorkspace(workspace)?.state;
|
|
24936
|
+
return state?.case === ReplicaFollowPhase.Backoff ? state.refusal : void 0;
|
|
24937
|
+
}
|
|
24689
24938
|
/** Closing an interaction realm must not erase a peer-owned follow. */
|
|
24690
24939
|
closeInteractionRealm() {
|
|
24691
24940
|
}
|
|
@@ -24718,9 +24967,12 @@ function dueAt(follow) {
|
|
|
24718
24967
|
case ReplicaFollowPhase.RebornRemotely:
|
|
24719
24968
|
case ReplicaFollowPhase.LocalCopyUnreadable:
|
|
24720
24969
|
return void 0;
|
|
24970
|
+
case ReplicaFollowPhase.Backoff: {
|
|
24971
|
+
const blocked = blockedRefusal(follow.state);
|
|
24972
|
+
return blocked ? blocked.rerunAtUnixMillis : follow.state.retryAtUnixMillis;
|
|
24973
|
+
}
|
|
24721
24974
|
case ReplicaFollowPhase.Offline:
|
|
24722
24975
|
case ReplicaFollowPhase.InvalidCredential:
|
|
24723
|
-
case ReplicaFollowPhase.Backoff:
|
|
24724
24976
|
return follow.state.retryAtUnixMillis;
|
|
24725
24977
|
default: {
|
|
24726
24978
|
const exhaustive = follow.state;
|
|
@@ -24736,6 +24988,10 @@ function delayUntilDue(follow, nowUnixMillis) {
|
|
|
24736
24988
|
return nextDueAt === void 0 ? void 0 : Math.max(0, nextDueAt - nowUnixMillis);
|
|
24737
24989
|
}
|
|
24738
24990
|
|
|
24991
|
+
// ../src/runtime/runtime_identity.ts
|
|
24992
|
+
init_define_NOMOS_KERNEL_RELEASE();
|
|
24993
|
+
var PEER_RUNTIME_IDENTITY = "0.116.4" ? `@githolon/runtime@${"0.116.4"}` : "@githolon/runtime@unbundled";
|
|
24994
|
+
|
|
24739
24995
|
// ../src/runtime/replica_follow_desk.ts
|
|
24740
24996
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
24741
24997
|
var REPLICA_FOLLOW_NAMESPACE = "nomos.peer-desk.replica-follow/v2";
|
|
@@ -24764,6 +25020,16 @@ var decodeWarming = (value) => {
|
|
|
24764
25020
|
...Number.isFinite(warming.hintMillis) ? { hintMillis: warming.hintMillis } : {}
|
|
24765
25021
|
} } : {};
|
|
24766
25022
|
};
|
|
25023
|
+
var decodeRefusal = (value) => {
|
|
25024
|
+
const refusal2 = value;
|
|
25025
|
+
return refusal2 && typeof refusal2.code === "string" && typeof refusal2.runtime === "string" && Number.isFinite(refusal2.atUnixMillis) ? { refusal: {
|
|
25026
|
+
code: refusal2.code,
|
|
25027
|
+
blocked: refusal2.blocked === true,
|
|
25028
|
+
runtime: refusal2.runtime,
|
|
25029
|
+
atUnixMillis: refusal2.atUnixMillis,
|
|
25030
|
+
...Number.isFinite(refusal2.rerunAtUnixMillis) ? { rerunAtUnixMillis: refusal2.rerunAtUnixMillis } : {}
|
|
25031
|
+
} } : {};
|
|
25032
|
+
};
|
|
24767
25033
|
var decodeState = (record, value) => {
|
|
24768
25034
|
if (!value || typeof value !== "object") throw corrupt(record, "state is not an object");
|
|
24769
25035
|
const state = value;
|
|
@@ -24778,7 +25044,8 @@ var decodeState = (record, value) => {
|
|
|
24778
25044
|
attempt: state.attempt,
|
|
24779
25045
|
observed: state.observed === true,
|
|
24780
25046
|
...state.replaceLocalHead === void 0 ? {} : { replaceLocalHead: state.replaceLocalHead },
|
|
24781
|
-
...Number.isFinite(state.warmingSinceUnixMillis) ? { warmingSinceUnixMillis: state.warmingSinceUnixMillis } : {}
|
|
25047
|
+
...Number.isFinite(state.warmingSinceUnixMillis) ? { warmingSinceUnixMillis: state.warmingSinceUnixMillis } : {},
|
|
25048
|
+
...typeof state.refused === "string" ? { refused: state.refused } : {}
|
|
24782
25049
|
};
|
|
24783
25050
|
case ReplicaFollowPhase.Settled:
|
|
24784
25051
|
return {
|
|
@@ -24810,7 +25077,8 @@ var decodeState = (record, value) => {
|
|
|
24810
25077
|
attempt: state.attempt,
|
|
24811
25078
|
retryAtUnixMillis: state.retryAtUnixMillis,
|
|
24812
25079
|
failure: state.failure,
|
|
24813
|
-
...decodeWarming(state.warming)
|
|
25080
|
+
...decodeWarming(state.warming),
|
|
25081
|
+
...decodeRefusal(state.refusal)
|
|
24814
25082
|
};
|
|
24815
25083
|
case ReplicaFollowPhase.RebornRemotely: {
|
|
24816
25084
|
const rebirth = state.rebirth ?? {};
|
|
@@ -25000,9 +25268,15 @@ var ReplicaFollowDesk = class {
|
|
|
25000
25268
|
break;
|
|
25001
25269
|
}
|
|
25002
25270
|
case ReplicaFollowPhase.Offline:
|
|
25003
|
-
case ReplicaFollowPhase.Backoff:
|
|
25004
|
-
|
|
25271
|
+
case ReplicaFollowPhase.Backoff: {
|
|
25272
|
+
const blocked = blockedRefusal(follow.state);
|
|
25273
|
+
if (blocked && blocked.rerunAtUnixMillis === void 0) break;
|
|
25274
|
+
wakes.push({
|
|
25275
|
+
workspace: follow.spec.workspace,
|
|
25276
|
+
atUnixMillis: blocked?.rerunAtUnixMillis ?? follow.state.retryAtUnixMillis
|
|
25277
|
+
});
|
|
25005
25278
|
break;
|
|
25279
|
+
}
|
|
25006
25280
|
case ReplicaFollowPhase.InvalidCredential:
|
|
25007
25281
|
wakes.push({
|
|
25008
25282
|
workspace: follow.spec.workspace,
|
|
@@ -25640,6 +25914,8 @@ var refusal = (requestId, code, message, retryAfterMillis) => frame({ case: "ref
|
|
|
25640
25914
|
message,
|
|
25641
25915
|
...retryAfterMillis === void 0 ? {} : { retryAfterMillis }
|
|
25642
25916
|
}) });
|
|
25917
|
+
var OFF_MAIN_AFTER_MILLIS = 6e4;
|
|
25918
|
+
var MAX_UNFOUND_HEADS = 1024;
|
|
25643
25919
|
var LiveStreamServer = class {
|
|
25644
25920
|
#kernel;
|
|
25645
25921
|
#now;
|
|
@@ -25653,6 +25929,10 @@ var LiveStreamServer = class {
|
|
|
25653
25929
|
/** By workspace: the history slices copies are fetching (a few, newest last). */
|
|
25654
25930
|
#slices = /* @__PURE__ */ new Map();
|
|
25655
25931
|
#cachedBytes = 0;
|
|
25932
|
+
/** By workspace and asked head: when this peer first answered "acquiring its own history" to a level
|
|
25933
|
+
* ask from a head it could not find. A shallow custody can never prove the head is off its main (its
|
|
25934
|
+
* walk is never `complete`), so after OFF_MAIN_AFTER_MILLIS of the same answer it says so anyway. */
|
|
25935
|
+
#unfound = /* @__PURE__ */ new Map();
|
|
25656
25936
|
constructor(kernel, now2 = Date.now, historyWanted = () => {
|
|
25657
25937
|
}, sealed = () => void 0) {
|
|
25658
25938
|
this.#kernel = kernel;
|
|
@@ -25700,9 +25980,12 @@ var LiveStreamServer = class {
|
|
|
25700
25980
|
if (!head) return [...answer, ...this.#checkpoint(workspace, requestId, create(LiveCheckpointRequestSchema))];
|
|
25701
25981
|
if (head === main) return answer;
|
|
25702
25982
|
let behind;
|
|
25983
|
+
let offMain = false;
|
|
25703
25984
|
try {
|
|
25704
25985
|
const summary = this.#kernel.historySummary(mount, requestId, head, main);
|
|
25705
25986
|
behind = summary.reachedBase ? summary.commitCount : void 0;
|
|
25987
|
+
offMain = !summary.reachedBase && (summary.complete || !this.#shallow(mount, requestId));
|
|
25988
|
+
if (summary.reachedBase) this.#unfound.delete(`${workspace}\0${head}`);
|
|
25706
25989
|
} catch {
|
|
25707
25990
|
behind = void 0;
|
|
25708
25991
|
}
|
|
@@ -25711,6 +25994,22 @@ var LiveStreamServer = class {
|
|
|
25711
25994
|
if (remountAfter > 0) {
|
|
25712
25995
|
return [...answer, ...this.#checkpoint(workspace, requestId, create(LiveCheckpointRequestSchema), behind ?? 0n)];
|
|
25713
25996
|
}
|
|
25997
|
+
const key = `${workspace}\0${head}`;
|
|
25998
|
+
const firstUnfound = this.#unfound.get(key);
|
|
25999
|
+
if (!offMain && firstUnfound === void 0) {
|
|
26000
|
+
if (this.#unfound.size >= MAX_UNFOUND_HEADS) this.#unfound.delete(this.#unfound.keys().next().value);
|
|
26001
|
+
this.#unfound.set(key, this.#now());
|
|
26002
|
+
} else if (!offMain && this.#now() - firstUnfound >= OFF_MAIN_AFTER_MILLIS) {
|
|
26003
|
+
offMain = true;
|
|
26004
|
+
}
|
|
26005
|
+
if (offMain) {
|
|
26006
|
+
this.#unfound.delete(key);
|
|
26007
|
+
return [...answer, refusal(
|
|
26008
|
+
"",
|
|
26009
|
+
"nomos.stream.off_main",
|
|
26010
|
+
"the copy's head is not on this peer's main; it must converge its own work, not level from it"
|
|
26011
|
+
)];
|
|
26012
|
+
}
|
|
25714
26013
|
this.#historyWanted(workspace);
|
|
25715
26014
|
return [...answer, refusal(
|
|
25716
26015
|
"",
|
|
@@ -25722,6 +26021,17 @@ var LiveStreamServer = class {
|
|
|
25722
26021
|
const maxOffers = Math.min(Math.max(1, frontier.maxOffers || MAX_STREAM_BATCH_OFFERS), MAX_STREAM_BATCH_OFFERS);
|
|
25723
26022
|
return [...answer, this.#batch(workspace, requestId, head, main, maxOffers)];
|
|
25724
26023
|
}
|
|
26024
|
+
/** Whether this peer's custody of the workspace is shallow. Unknown counts as shallow: it keeps the
|
|
26025
|
+
* existing wait for history rather than calling a head off main. */
|
|
26026
|
+
#shallow(mount, requestId) {
|
|
26027
|
+
const boundaries = this.#kernel.shallowBoundaries;
|
|
26028
|
+
if (!boundaries) return true;
|
|
26029
|
+
try {
|
|
26030
|
+
return boundaries.call(this.#kernel, mount, requestId).length > 0;
|
|
26031
|
+
} catch {
|
|
26032
|
+
return true;
|
|
26033
|
+
}
|
|
26034
|
+
}
|
|
25725
26035
|
/**
|
|
25726
26036
|
* The `maxOffers` offers directly above `base`, as the commits this peer admitted them in: one
|
|
25727
26037
|
* bounded walk and one bounded pack (O(batch)). The copy's kernel adopts exactly these commits on
|
|
@@ -26089,7 +26399,6 @@ var OwnOffers = class {
|
|
|
26089
26399
|
};
|
|
26090
26400
|
var LiveCorrespondenceDesk = class {
|
|
26091
26401
|
#links = /* @__PURE__ */ new Map();
|
|
26092
|
-
#pending = /* @__PURE__ */ new Map();
|
|
26093
26402
|
/** What procedures asked of each workspace's stream, and what it delivered (peer_catchup.md). */
|
|
26094
26403
|
inbound;
|
|
26095
26404
|
#own = new OwnOffers();
|
|
@@ -26125,13 +26434,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26125
26434
|
* never held. Absent (a custodian, a relay, a test), every write goes out on the next drive.
|
|
26126
26435
|
*/
|
|
26127
26436
|
writeBurst;
|
|
26128
|
-
/**
|
|
26129
|
-
* An application peer's receive step (CAP-757; sized by time, CAP-762). A received batch of other
|
|
26130
|
-
* senders' offers larger than one step is not admitted on the socket, in one kernel-thread hold:
|
|
26131
|
-
* the follow receives it instead, in yielding steps of the policy's current size. Absent (a
|
|
26132
|
-
* custodian, a relay, a test), every received batch is admitted as it arrives.
|
|
26133
|
-
*/
|
|
26134
|
-
receiveStep;
|
|
26135
26437
|
/** The last progress counted per workspace, served while a burst holds recounting back. */
|
|
26136
26438
|
#progress = /* @__PURE__ */ new Map();
|
|
26137
26439
|
/** When a held-back progress recount became owed; it reports at the burst's next due point. */
|
|
@@ -26221,10 +26523,12 @@ var LiveCorrespondenceDesk = class {
|
|
|
26221
26523
|
sent: link.stream?.sent,
|
|
26222
26524
|
unansweredAgesMs: (link.stream?.unanswered ?? []).map((entry) => Number((now2 - (entry.due - LIVE_ANSWER_WINDOW)) / 1000000n)),
|
|
26223
26525
|
custodianLocal: link.custodian?.local,
|
|
26224
|
-
convergeDue: link.convergeDue !== void 0
|
|
26526
|
+
convergeDue: link.convergeDue !== void 0,
|
|
26527
|
+
resident: false
|
|
26225
26528
|
};
|
|
26226
26529
|
try {
|
|
26227
26530
|
if (this.kernel.isResident(link.binding.workspace) !== false) {
|
|
26531
|
+
state.resident = true;
|
|
26228
26532
|
const mount = workspaceMount(link.binding.workspace);
|
|
26229
26533
|
state.head = this.kernel.head(mount, ++this.#requestId);
|
|
26230
26534
|
state.tracked = this.kernel.resolveRef(mount, ++this.#requestId, TRACKED_MAIN2);
|
|
@@ -26293,7 +26597,7 @@ var LiveCorrespondenceDesk = class {
|
|
|
26293
26597
|
}
|
|
26294
26598
|
#count(link, mount) {
|
|
26295
26599
|
const head = this.kernel.head(mount, ++this.#requestId);
|
|
26296
|
-
const acknowledged = this.#cursor(link, mount)?.local ?? this.kernel.resolveRef(mount, ++this.#requestId, TRACKED_MAIN2);
|
|
26600
|
+
const acknowledged = this.#cursor(link, mount)?.local ?? this.#heldPeerMain(link, mount, head) ?? this.kernel.resolveRef(mount, ++this.#requestId, TRACKED_MAIN2);
|
|
26297
26601
|
if (!head || !acknowledged) return void 0;
|
|
26298
26602
|
const count = (base, tip) => {
|
|
26299
26603
|
if (base === tip) return 0;
|
|
@@ -26327,6 +26631,14 @@ var LiveCorrespondenceDesk = class {
|
|
|
26327
26631
|
#holds(mount, base, tip) {
|
|
26328
26632
|
return this.#chain(mount, base, tip)?.reachedBase === true;
|
|
26329
26633
|
}
|
|
26634
|
+
/** The custodian's main as last heard, when this copy holds it on its own head's chain above the
|
|
26635
|
+
* sealed bookmark: the commits up to it came from the stream and are the custodian's (CAP-838). */
|
|
26636
|
+
#heldPeerMain(link, mount, head, tracked = this.kernel.resolveRef(mount, ++this.#requestId, TRACKED_MAIN2)) {
|
|
26637
|
+
const main = link.peerMain;
|
|
26638
|
+
if (!main || !head || main === tracked) return void 0;
|
|
26639
|
+
if (tracked && !this.#holds(mount, tracked, main)) return void 0;
|
|
26640
|
+
return this.#holds(mount, main, head) ? main : void 0;
|
|
26641
|
+
}
|
|
26330
26642
|
/** This connection's admission cursor, while it still leads the saved bookmark. A cursor on local
|
|
26331
26643
|
* history (admitted, not yet sealed) remembers the bookmark it was set above; once anything else
|
|
26332
26644
|
* moves the bookmark (a follow round adopting the custodian's main) the bookmark rules again, so
|
|
@@ -26403,7 +26715,7 @@ var LiveCorrespondenceDesk = class {
|
|
|
26403
26715
|
evidencePack: options.carryCommits || intents.some((intent) => intent.transportRequirement === IntentTransportRequirement.COMMIT_TREE) ? this.kernel.packDelta(mount, requestId, batchHead, base) : new Uint8Array()
|
|
26404
26716
|
});
|
|
26405
26717
|
}
|
|
26406
|
-
receive(
|
|
26718
|
+
receive(receipt) {
|
|
26407
26719
|
const link = [...this.#links.values()].find((item) => item.id === receipt.connectionId);
|
|
26408
26720
|
if (!link) return;
|
|
26409
26721
|
if (receipt.observation.case === "closed" || receipt.observation.case === "closure") {
|
|
@@ -26417,6 +26729,7 @@ var LiveCorrespondenceDesk = class {
|
|
|
26417
26729
|
link.ownMains = [];
|
|
26418
26730
|
delete link.lastAnnounced;
|
|
26419
26731
|
delete link.lastPlacementAnnounced;
|
|
26732
|
+
link.peerMain = void 0;
|
|
26420
26733
|
link.dirty = true;
|
|
26421
26734
|
link.due = this.clock.monotonicNanoseconds() + BigInt(Math.min(3e4, 250 * 2 ** Math.min(link.failures++, 7))) * 1000000n;
|
|
26422
26735
|
return;
|
|
@@ -26426,7 +26739,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26426
26739
|
link.openedAt = this.clock.monotonicNanoseconds();
|
|
26427
26740
|
link.failures = 0;
|
|
26428
26741
|
link.unheard = void 0;
|
|
26429
|
-
if (!link.publisher) this.#hello(link);
|
|
26430
26742
|
if (!link.publisher) this.inbound.opened(link.binding.workspace);
|
|
26431
26743
|
this.linkOpened?.(link.binding.workspace);
|
|
26432
26744
|
return;
|
|
@@ -26460,18 +26772,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26460
26772
|
if (this.inbound.frame(link.binding.workspace, frame2.message)) return;
|
|
26461
26773
|
if (frame2.message.case === "frontier") {
|
|
26462
26774
|
this.#announced(link, frame2.message.value.head);
|
|
26463
|
-
if (this.kernel.isResident(link.binding.workspace) === false) return;
|
|
26464
|
-
if (!frame2.message.value.response) {
|
|
26465
|
-
this.#send(link, { case: "frontier", value: {
|
|
26466
|
-
head: this.kernel.head(workspaceMount(link.binding.workspace), ++this.#requestId) ?? "",
|
|
26467
|
-
response: true
|
|
26468
|
-
} });
|
|
26469
|
-
}
|
|
26470
|
-
try {
|
|
26471
|
-
const batch2 = this.export(link.binding, frame2.message.value.head, ++this.#requestId);
|
|
26472
|
-
if (batch2.source.case === "inline" && batch2.source.value.offers.length) this.#send(link, { case: "offers", value: batch2 });
|
|
26473
|
-
} catch {
|
|
26474
|
-
}
|
|
26475
26775
|
return;
|
|
26476
26776
|
}
|
|
26477
26777
|
if (frame2.message.case === "admission") {
|
|
@@ -26479,48 +26779,18 @@ var LiveCorrespondenceDesk = class {
|
|
|
26479
26779
|
this.progressChanged?.(link.binding.workspace);
|
|
26480
26780
|
return;
|
|
26481
26781
|
}
|
|
26482
|
-
|
|
26483
|
-
|
|
26484
|
-
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
26488
|
-
|
|
26489
|
-
return;
|
|
26490
|
-
}
|
|
26491
|
-
if (this.receiveStep !== void 0 && offers.length > this.receiveStep.offers) {
|
|
26492
|
-
this.remoteAdvanced?.(link.binding.workspace);
|
|
26493
|
-
return;
|
|
26494
|
-
}
|
|
26495
|
-
if ([...this.#pending.values()].some((pending) => pending.link === link && pending.bytes.length === bytes.length && pending.bytes.every((byte, index) => byte === bytes[index]))) return;
|
|
26496
|
-
if (this.#pending.size >= 32) return;
|
|
26497
|
-
this.#pending.set(request.requestId, { link, bytes });
|
|
26498
|
-
return create(RuntimeRequestSchema, {
|
|
26499
|
-
protocolVersion: 2,
|
|
26500
|
-
requestId: request.requestId,
|
|
26501
|
-
target: { workspace: link.binding.workspace, session: link.binding.replicaId },
|
|
26502
|
-
// HTTP sender reconciliation controls are not part of a live proposal.
|
|
26503
|
-
// Keeping fastForwardOnly while removing baseHead silently skips every
|
|
26504
|
-
// offer against a nonempty local head. The Kernel judges sealed causality;
|
|
26505
|
-
// live receivers neither ask for a return archive nor confirm the sender.
|
|
26506
|
-
command: { case: "admitCustodyOffers", value: create(CustodyOfferBatchRequestSchema, {
|
|
26507
|
-
...batch,
|
|
26508
|
-
baseHead: void 0,
|
|
26509
|
-
observedHead: void 0,
|
|
26510
|
-
fastForwardOnly: false,
|
|
26511
|
-
...offers.length === batch.source.value.offers.length ? {} : { source: { case: "inline", value: { ...batch.source.value, offers } } }
|
|
26512
|
-
}) }
|
|
26513
|
-
});
|
|
26782
|
+
}
|
|
26783
|
+
/** The newest local commit the custodian admitted unchanged over this workspace's open link: its
|
|
26784
|
+
* main holds that commit, sealed or not, so it is no work to offer (CAP-838). */
|
|
26785
|
+
admittedLocal(workspace) {
|
|
26786
|
+
const link = this.#links.get(workspace);
|
|
26787
|
+
const cursor = link?.state === "open" && !link.publisher ? link.custodian : void 0;
|
|
26788
|
+
return cursor && cursor.main === cursor.local ? cursor.local : void 0;
|
|
26514
26789
|
}
|
|
26515
26790
|
/** Offers this replica sent another way (the HTTP lane): their echo on the live link is its own. */
|
|
26516
26791
|
offered(workspace, commitOids) {
|
|
26517
26792
|
this.#own.commits(workspace, commitOids);
|
|
26518
26793
|
}
|
|
26519
|
-
/** Whether this request is the admission of a batch relayed to this follower over its live link.
|
|
26520
|
-
* Such an admission is never answered to a sender, so it keeps no custody receipts (CAP-759). */
|
|
26521
|
-
relayed(requestId) {
|
|
26522
|
-
return this.#pending.has(requestId);
|
|
26523
|
-
}
|
|
26524
26794
|
/**
|
|
26525
26795
|
* The custodian judged a live batch. Admission acknowledges the batch at once; its admitted main
|
|
26526
26796
|
* becomes this connection's cursor when it is already in local verified history. The saved
|
|
@@ -26540,9 +26810,10 @@ var LiveCorrespondenceDesk = class {
|
|
|
26540
26810
|
...admission.sealedMain ? { sealed: admission.sealedMain } : {}
|
|
26541
26811
|
};
|
|
26542
26812
|
}
|
|
26543
|
-
if (!admission?.main
|
|
26813
|
+
if (!admission?.main) return;
|
|
26544
26814
|
const ownBatchHead = this.#own.batchHead(workspace, live.requestId);
|
|
26545
26815
|
const ours = ownBatchHead !== void 0;
|
|
26816
|
+
if (this.kernel.isResident(workspace) === false) return;
|
|
26546
26817
|
let stale = false;
|
|
26547
26818
|
if (ours) link.unheard = void 0;
|
|
26548
26819
|
if (ours && link.stream) {
|
|
@@ -26630,6 +26901,7 @@ var LiveCorrespondenceDesk = class {
|
|
|
26630
26901
|
* upstream head it already recorded. Re-announcing a known frontier - which is all an
|
|
26631
26902
|
* upstream can do while this replica is ahead or held on a dependency - is not an event. */
|
|
26632
26903
|
#announced(link, head) {
|
|
26904
|
+
if (head) link.peerMain = head;
|
|
26633
26905
|
if (!head || link.lastAnnounced === head) return;
|
|
26634
26906
|
link.lastAnnounced = head;
|
|
26635
26907
|
if (this.kernel.isResident(link.binding.workspace) === false) {
|
|
@@ -26654,15 +26926,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26654
26926
|
if (frontier === this.kernel.resolveRef(mount, ++this.#requestId, PLACEMENT_REF3)) return;
|
|
26655
26927
|
this.placementAdvanced?.(link.binding.workspace);
|
|
26656
26928
|
}
|
|
26657
|
-
settled(request, response) {
|
|
26658
|
-
if (response.outcome.case === "correspondencePending" || response.outcome.case === "localReadPending") return;
|
|
26659
|
-
this.#pending.delete(request.requestId);
|
|
26660
|
-
}
|
|
26661
|
-
failed(requestId) {
|
|
26662
|
-
const pending = this.#pending.get(requestId);
|
|
26663
|
-
this.#pending.delete(requestId);
|
|
26664
|
-
if (pending) this.remoteAdvanced?.(pending.link.binding.workspace);
|
|
26665
|
-
}
|
|
26666
26929
|
disconnect(workspace) {
|
|
26667
26930
|
const link = this.#links.get(workspace);
|
|
26668
26931
|
if (!link) return;
|
|
@@ -26670,7 +26933,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26670
26933
|
if (link.state === "open") this.linkClosed?.(workspace, closure);
|
|
26671
26934
|
this.#links.delete(workspace);
|
|
26672
26935
|
this.#progress.delete(workspace);
|
|
26673
|
-
for (const [id, pending] of this.#pending) if (pending.link === link) this.#pending.delete(id);
|
|
26674
26936
|
}
|
|
26675
26937
|
/** The digest of the settled-letter outcomes this copy holds; empty when it cannot tell. */
|
|
26676
26938
|
#outcomesDigest(workspace) {
|
|
@@ -26708,14 +26970,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26708
26970
|
transfer.retain(create(PeerGitRefsFetchedSchema, { refs: [...wanted.refs], pack }));
|
|
26709
26971
|
this.progressChanged?.(link.binding.workspace);
|
|
26710
26972
|
}
|
|
26711
|
-
#hello(link) {
|
|
26712
|
-
if (this.kernel.isResident(link.binding.workspace) === false) return;
|
|
26713
|
-
const head = this.kernel.head(workspaceMount(link.binding.workspace), ++this.#requestId);
|
|
26714
|
-
this.#send(link, { case: "frontier", value: {
|
|
26715
|
-
head: head ?? "",
|
|
26716
|
-
outcomesDigest: this.#outcomesDigest(link.binding.workspace)
|
|
26717
|
-
} });
|
|
26718
|
-
}
|
|
26719
26973
|
drive() {
|
|
26720
26974
|
const now2 = this.clock.monotonicNanoseconds();
|
|
26721
26975
|
let wake;
|
|
@@ -26764,7 +27018,8 @@ var LiveCorrespondenceDesk = class {
|
|
|
26764
27018
|
link.custodian = void 0;
|
|
26765
27019
|
}
|
|
26766
27020
|
this.#cursor(link, mount);
|
|
26767
|
-
|
|
27021
|
+
let answered2 = link.publisher ? link.published ??= head ?? "" : link.custodian?.local ?? this.kernel.resolveRef(mount, ++this.#requestId, TRACKED_MAIN2) ?? "";
|
|
27022
|
+
if (!link.publisher && !link.custodian) answered2 = this.#heldPeerMain(link, mount, head, answered2) ?? answered2;
|
|
26768
27023
|
const fence = link.custodian?.main ?? answered2;
|
|
26769
27024
|
if (link.stream && !(head && this.#holds(mount, link.stream.sent, head))) {
|
|
26770
27025
|
link.stream = void 0;
|
|
@@ -26865,7 +27120,6 @@ var LiveCorrespondenceDesk = class {
|
|
|
26865
27120
|
}
|
|
26866
27121
|
close() {
|
|
26867
27122
|
for (const workspace of [...this.#links.keys()]) this.disconnect(workspace);
|
|
26868
|
-
this.#pending.clear();
|
|
26869
27123
|
}
|
|
26870
27124
|
};
|
|
26871
27125
|
|
|
@@ -27825,6 +28079,7 @@ var PLACEMENT_REF4 = "refs/nomos/placement";
|
|
|
27825
28079
|
var RECEIVED_PLACEMENT_REF = "refs/remotes/nomos/placement";
|
|
27826
28080
|
var STREAM_BATCH_OFFERS = 64;
|
|
27827
28081
|
var REMOUNT_AFTER_OFFERS = 128;
|
|
28082
|
+
var MAX_REMOUNTS_LOST = 2;
|
|
27828
28083
|
var HISTORY_SLICES = [8, 64, 256, 256];
|
|
27829
28084
|
var WorkspaceSyncPhase = Object.freeze({
|
|
27830
28085
|
Idle: "idle",
|
|
@@ -27845,7 +28100,11 @@ var deferredLocal = "refs/nomos/sync-deferred-local";
|
|
|
27845
28100
|
var judgedThisLifetime = /* @__PURE__ */ new Map();
|
|
27846
28101
|
function releaseSyncJudgment(workspace) {
|
|
27847
28102
|
judgedThisLifetime.delete(workspace);
|
|
28103
|
+
notContainedThisLifetime.delete(workspace);
|
|
27848
28104
|
}
|
|
28105
|
+
var notContainedThisLifetime = /* @__PURE__ */ new Map();
|
|
28106
|
+
var MAX_NOT_CONTAINED = 1024;
|
|
28107
|
+
var NOT_CONTAINED_REFUSALS = /* @__PURE__ */ new Set(["nomos.stream.off_main", "nomos.stream.history_required"]);
|
|
27849
28108
|
var MAX_REBASES_PER_ROUND = 2;
|
|
27850
28109
|
var elapsedMillis = (clock2, started) => {
|
|
27851
28110
|
const finished = clock2.monotonicNanoseconds();
|
|
@@ -27901,6 +28160,7 @@ var WorkspaceSyncWork = class {
|
|
|
27901
28160
|
#lastStep;
|
|
27902
28161
|
#probeOnly;
|
|
27903
28162
|
#liveConnected;
|
|
28163
|
+
#liveAdmitted;
|
|
27904
28164
|
/** Set when this round adopted the upstream's own head as its base (see #establishBase). */
|
|
27905
28165
|
#establishedBase;
|
|
27906
28166
|
#receiveState;
|
|
@@ -27917,6 +28177,11 @@ var WorkspaceSyncWork = class {
|
|
|
27917
28177
|
/** The custodian head this round last offered or pulled from. */
|
|
27918
28178
|
#rebaseFrom;
|
|
27919
28179
|
#rebases = 0;
|
|
28180
|
+
#remountsLost = 0;
|
|
28181
|
+
/** This round's level ask is a containment question (CAP-870): on a refusal, take this path. */
|
|
28182
|
+
#containment;
|
|
28183
|
+
/** The stale bookmark a contained round replaces: its custodian's main holds the local head. */
|
|
28184
|
+
#containedBookmark;
|
|
27920
28185
|
/** Local intents this round's rebase refused and parked on the refused-offer desk. */
|
|
27921
28186
|
#parkedLocal = 0;
|
|
27922
28187
|
/** An application-authorized drop: replace local custody at exactly this head. */
|
|
@@ -27938,6 +28203,7 @@ var WorkspaceSyncWork = class {
|
|
|
27938
28203
|
this.#placementFetched = args.placementFetched;
|
|
27939
28204
|
this.#probeOnly = args.probeOnly ?? false;
|
|
27940
28205
|
this.#liveConnected = args.liveConnected === true;
|
|
28206
|
+
this.#liveAdmitted = args.liveAdmitted;
|
|
27941
28207
|
this.#receiveState = args.receiveState;
|
|
27942
28208
|
this.#completeHistory = args.completeHistory === true;
|
|
27943
28209
|
this.#repairMissingCommit = args.repairMissingCommit === true;
|
|
@@ -28012,9 +28278,15 @@ var WorkspaceSyncWork = class {
|
|
|
28012
28278
|
if (this.#receiveState) return this.#acquire("full");
|
|
28013
28279
|
if (this.#replaceLocalHead !== void 0) return this.#acquire(this.#repairMissingCommit ? "full" : "mount");
|
|
28014
28280
|
if (!this.#localHead) return this.#acquire(this.#completeHistory ? "full" : "mount");
|
|
28015
|
-
|
|
28016
|
-
|
|
28281
|
+
const unadmitted = this.#holdsLocalWork() && this.#localHead !== this.#liveAdmitted;
|
|
28282
|
+
if (this.#liveConnected && unadmitted && this.#previousHead) {
|
|
28283
|
+
if (this.#mayBeContained()) return this.#askContainment("offer");
|
|
28284
|
+
return this.#notContained();
|
|
28017
28285
|
}
|
|
28286
|
+
if (this.#liveConnected && this.#probeOnly && this.#previousHead && this.#holdsUnsyncedCommits() && this.#command.admit && this.#localHead === this.#liveAdmitted) {
|
|
28287
|
+
return this.#acquire("level");
|
|
28288
|
+
}
|
|
28289
|
+
if (this.#liveConnected && !this.#probeOnly && !unadmitted) return this.#acquire("level");
|
|
28018
28290
|
const requestId = `${this.#binding.replicaId}:${this.#requestId}:${this.workId}:frontier`;
|
|
28019
28291
|
return this.#beginOffer(encodeInlineCustodyOffer({
|
|
28020
28292
|
session: this.#binding.replicaId,
|
|
@@ -28023,6 +28295,60 @@ var WorkspaceSyncWork = class {
|
|
|
28023
28295
|
intents: []
|
|
28024
28296
|
}), { case: "awaiting", requestId }, true);
|
|
28025
28297
|
}
|
|
28298
|
+
/** Whether the custodian's main may hold this copy's head: not already refused in this lifetime. */
|
|
28299
|
+
#mayBeContained() {
|
|
28300
|
+
return !!this.#localHead && this.#offerHead === void 0 && notContainedThisLifetime.get(this.#mount.workspace) !== this.#localHead;
|
|
28301
|
+
}
|
|
28302
|
+
/** The probe named a custodian main that is not on this copy's own chain, so the copy's head may be
|
|
28303
|
+
* on the custodian's. A main below the local head (the custodian is behind) cannot contain it. A
|
|
28304
|
+
* complete copy answers a main it does not hold after one existence check, and one it holds after
|
|
28305
|
+
* a walk to where the two chains join. A kernel that cannot say keeps the existing path. */
|
|
28306
|
+
#custodianMainOffLocalChain() {
|
|
28307
|
+
if (!this.#kernel.historySummary || !this.#desiredHead || !this.#localHead) return false;
|
|
28308
|
+
try {
|
|
28309
|
+
return !this.#kernel.historySummary(this.#mount, this.#requestId, this.#desiredHead, this.#localHead).reachedBase;
|
|
28310
|
+
} catch (error) {
|
|
28311
|
+
return error instanceof KernelHistoryRequired;
|
|
28312
|
+
}
|
|
28313
|
+
}
|
|
28314
|
+
/** Level from the local head (CAP-870). The stream answers with the custodian's commits above it
|
|
28315
|
+
* (or "level") only when its main holds that head; then this round converges onto them, moves the
|
|
28316
|
+
* bookmark, and offers nothing. One custodian lookup, O(commits behind); no local enumeration. */
|
|
28317
|
+
#askContainment(otherwise) {
|
|
28318
|
+
this.#containment = otherwise;
|
|
28319
|
+
return this.#acquire("level");
|
|
28320
|
+
}
|
|
28321
|
+
/** The stream answered from the local head: it is on the custodian's main. The stale bookmark
|
|
28322
|
+
* stays this round's to replace; the local head is the round's base from here. */
|
|
28323
|
+
#contained() {
|
|
28324
|
+
this.#containment = void 0;
|
|
28325
|
+
this.#containedBookmark = this.#previousHead;
|
|
28326
|
+
this.#previousHead = this.#localHead;
|
|
28327
|
+
}
|
|
28328
|
+
/** The custodian's main does not hold the local head (or the stream could not say): the copy holds
|
|
28329
|
+
* work of its own. Go straight on to the pull/rebase/offer path. A dependency planner's probe hands
|
|
28330
|
+
* the round back to it to plan the outbound suffix. */
|
|
28331
|
+
#notContained(code) {
|
|
28332
|
+
const otherwise = this.#containment ?? "offer";
|
|
28333
|
+
this.#containment = void 0;
|
|
28334
|
+
if (code !== void 0 && NOT_CONTAINED_REFUSALS.has(code) && this.#localHead) {
|
|
28335
|
+
if (notContainedThisLifetime.size >= MAX_NOT_CONTAINED && !notContainedThisLifetime.has(this.#mount.workspace)) {
|
|
28336
|
+
notContainedThisLifetime.delete(notContainedThisLifetime.keys().next().value);
|
|
28337
|
+
}
|
|
28338
|
+
notContainedThisLifetime.set(this.#mount.workspace, this.#localHead);
|
|
28339
|
+
}
|
|
28340
|
+
if (this.#probeOnly) {
|
|
28341
|
+
this.#needsReconciliation = true;
|
|
28342
|
+
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
28343
|
+
workspace: this.#mount.workspace,
|
|
28344
|
+
currentHead: this.#localHead,
|
|
28345
|
+
previousHead: this.#previousHead,
|
|
28346
|
+
stillAhead: true,
|
|
28347
|
+
offerReceipt: this.#offerReceipt
|
|
28348
|
+
}));
|
|
28349
|
+
}
|
|
28350
|
+
return otherwise === "pull" ? this.#pullBeforeOffer() : this.#offerOrReconcile();
|
|
28351
|
+
}
|
|
28026
28352
|
#offerOrReconcile() {
|
|
28027
28353
|
if (this.#command.admit && this.#localHead && this.#localHead !== this.#previousHead) {
|
|
28028
28354
|
const suffix = this.#kernel.transportIntentsAbove(
|
|
@@ -28101,7 +28427,14 @@ var WorkspaceSyncWork = class {
|
|
|
28101
28427
|
return this.#previousHead === void 0 && this.#localHead !== void 0 && this.#replaceLocalHead === void 0;
|
|
28102
28428
|
}
|
|
28103
28429
|
#holdsLocalWork() {
|
|
28104
|
-
return this.#command.admit &&
|
|
28430
|
+
return this.#command.admit && this.#holdsUnsyncedCommits();
|
|
28431
|
+
}
|
|
28432
|
+
/** Commits on local main the provider has not been seen to admit, whether or not this round may
|
|
28433
|
+
* offer them. Remount replaces local main, so it asks this, never the admit-gated question: a copy
|
|
28434
|
+
* holding unsynced work is offered and rebased, never remounted. A copy without a bookmark cannot
|
|
28435
|
+
* prove it holds nothing of its own. */
|
|
28436
|
+
#holdsUnsyncedCommits() {
|
|
28437
|
+
return !!this.#localHead && this.#localHead !== this.#previousHead;
|
|
28105
28438
|
}
|
|
28106
28439
|
/** Ask only for the custodian's delta; the offer follows once this peer has rebased. */
|
|
28107
28440
|
#pullBeforeOffer() {
|
|
@@ -28139,13 +28472,13 @@ var WorkspaceSyncWork = class {
|
|
|
28139
28472
|
}
|
|
28140
28473
|
if (purpose === "level") {
|
|
28141
28474
|
this.#state = { phase: WorkspaceSyncPhase.Stream, purpose, startedAt };
|
|
28142
|
-
return this.#streamEffect("level", { case: "level", value: create(LiveFrontierSchema, {
|
|
28475
|
+
return this.#streamEffect(this.#containment ? "contain" : "level", { case: "level", value: create(LiveFrontierSchema, {
|
|
28143
28476
|
head: this.#localHead ?? "",
|
|
28144
28477
|
response: false,
|
|
28145
28478
|
maxOffers: this.#receiveStep?.offers ?? STREAM_BATCH_OFFERS,
|
|
28146
28479
|
// Only a copy holding no work above its bookmark may be remounted instead of replaying, and
|
|
28147
28480
|
// never one whose durable copy must hold the complete history (a head-only mount it cannot keep).
|
|
28148
|
-
remountAfterOffers: this.#
|
|
28481
|
+
remountAfterOffers: this.#holdsUnsyncedCommits() || this.#completeHistory ? 0 : REMOUNT_AFTER_OFFERS
|
|
28149
28482
|
}) });
|
|
28150
28483
|
}
|
|
28151
28484
|
const outcomes = new EffectOutcomeTransfer(this.#kernel, this.#mount, this.#requestId);
|
|
@@ -28222,6 +28555,7 @@ var WorkspaceSyncWork = class {
|
|
|
28222
28555
|
}
|
|
28223
28556
|
if (answer.outcome.case === "refusal") {
|
|
28224
28557
|
const refusal2 = answer.outcome.value;
|
|
28558
|
+
if (state.purpose === "level" && this.#containment) return this.#notContained(refusal2.code);
|
|
28225
28559
|
if (state.afterPlacement) return this.#finish(state.afterPlacement);
|
|
28226
28560
|
return this.#transportFailure(`${refusal2.code}: ${refusal2.message}`, false);
|
|
28227
28561
|
}
|
|
@@ -28258,6 +28592,11 @@ var WorkspaceSyncWork = class {
|
|
|
28258
28592
|
state.startedAt
|
|
28259
28593
|
);
|
|
28260
28594
|
}
|
|
28595
|
+
if (state.purpose === "level" && this.#containment) {
|
|
28596
|
+
const answeredFromHead = answer.outcome.case === "level" || answer.outcome.case === "pack" && answer.outcome.value.base === this.#localHead;
|
|
28597
|
+
if (!answeredFromHead) return this.#notContained();
|
|
28598
|
+
this.#contained();
|
|
28599
|
+
}
|
|
28261
28600
|
if (state.purpose === "level" && answer.outcome.case === "pack" && answer.outcome.value.base) {
|
|
28262
28601
|
const batch = answer.outcome.value;
|
|
28263
28602
|
if (batch.base !== this.#previousHead && batch.base !== this.#kernel.head(this.#mount, this.#requestId)) {
|
|
@@ -28396,13 +28735,19 @@ var WorkspaceSyncWork = class {
|
|
|
28396
28735
|
/**
|
|
28397
28736
|
* A copy holding no work above its bookmark, too far behind to replay the backlog: mount the
|
|
28398
28737
|
* peer's checkpoint in place of its exact local main (O(state), the same trust as a cold mount).
|
|
28399
|
-
* The kernel keeps the replaced head at refs/heads/custody-preserved/<old
|
|
28400
|
-
*
|
|
28738
|
+
* The kernel keeps the replaced head at refs/heads/custody-preserved/<old>. That ref is custody the
|
|
28739
|
+
* kernel guards (custody may not delete an effectful branch), so it is left where it is: a remount
|
|
28740
|
+
* that deleted it threw after main had already moved, leaving the bookmark behind main and the
|
|
28741
|
+
* round failed for ever (2026-10-08, estate-5b493d6b).
|
|
28742
|
+
*
|
|
28743
|
+
* The copy may have moved while the checkpoint streamed (a live batch, a local write). Then it is
|
|
28744
|
+
* no longer the copy the remount was asked for: take the round up again from its new head. Local
|
|
28745
|
+
* work is offered first; a copy that is still only behind asks to level again.
|
|
28401
28746
|
*/
|
|
28402
28747
|
#remount(candidateHead, receivedBytes, prefetchMillis) {
|
|
28403
28748
|
const expected = this.#kernel.head(this.#mount, this.#requestId);
|
|
28404
|
-
if (expected === void 0 || expected !== this.#localHead || this.#
|
|
28405
|
-
return this.#
|
|
28749
|
+
if (expected === void 0 || expected !== this.#localHead || this.#holdsUnsyncedCommits()) {
|
|
28750
|
+
return this.#remountLost(expected);
|
|
28406
28751
|
}
|
|
28407
28752
|
const started = this.#clock.monotonicNanoseconds();
|
|
28408
28753
|
if (candidateHead !== expected) {
|
|
@@ -28410,7 +28755,6 @@ var WorkspaceSyncWork = class {
|
|
|
28410
28755
|
if (!restored.accepted || restored.head !== candidateHead) {
|
|
28411
28756
|
throw new Error(`kernel refused to remount '${this.#mount.workspace}' at ${candidateHead}`);
|
|
28412
28757
|
}
|
|
28413
|
-
this.#kernel.deleteRef?.(this.#mount, this.#requestId, `refs/heads/custody-preserved/${expected}`);
|
|
28414
28758
|
this.#providerCustodyMounted();
|
|
28415
28759
|
}
|
|
28416
28760
|
return this.#converged(create(SyncResponseSchema, {
|
|
@@ -28420,6 +28764,25 @@ var WorkspaceSyncWork = class {
|
|
|
28420
28764
|
admittedRemoteHead: candidateHead
|
|
28421
28765
|
}), candidateHead, receivedBytes, prefetchMillis, started);
|
|
28422
28766
|
}
|
|
28767
|
+
/** The remount's copy changed under it. Bounded: a copy that keeps moving under live traffic is
|
|
28768
|
+
* levelled by its live link anyway, and the next round starts from wherever it got to. */
|
|
28769
|
+
#remountLost(head) {
|
|
28770
|
+
if (head === void 0 || ++this.#remountsLost > MAX_REMOUNTS_LOST) {
|
|
28771
|
+
return this.#transportFailure("the copy changed while its remount was streaming", false);
|
|
28772
|
+
}
|
|
28773
|
+
this.#localHead = head;
|
|
28774
|
+
this.#previousHead = this.#kernel.resolveRef(this.#mount, this.#requestId, trackedMain);
|
|
28775
|
+
if (this.#desiredHead === this.#localHead) {
|
|
28776
|
+
return this.#converged(create(SyncResponseSchema, {
|
|
28777
|
+
admitted: true,
|
|
28778
|
+
head,
|
|
28779
|
+
candidateHead: head,
|
|
28780
|
+
admittedRemoteHead: head
|
|
28781
|
+
}), head, 0, 0);
|
|
28782
|
+
}
|
|
28783
|
+
if (this.#holdsLocalWork() && this.#desiredHead !== this.#previousHead) return this.#pullBeforeOffer();
|
|
28784
|
+
return this.#offerOrReconcile();
|
|
28785
|
+
}
|
|
28423
28786
|
resume(receipt) {
|
|
28424
28787
|
if (receipt.effectId === this.#lastReceiptEffectId && this.#lastStep) {
|
|
28425
28788
|
return this.#lastStep;
|
|
@@ -28584,6 +28947,10 @@ var WorkspaceSyncWork = class {
|
|
|
28584
28947
|
offerReceipt: this.#offerReceipt
|
|
28585
28948
|
}));
|
|
28586
28949
|
}
|
|
28950
|
+
const custodianMoved = this.#holdsLocalWork() && this.#desiredHead !== this.#previousHead;
|
|
28951
|
+
if (custodianMoved && this.#mayBeContained() && this.#custodianMainOffLocalChain()) {
|
|
28952
|
+
return this.#askContainment("pull");
|
|
28953
|
+
}
|
|
28587
28954
|
if (this.#probeOnly) {
|
|
28588
28955
|
this.#needsReconciliation = true;
|
|
28589
28956
|
return this.#finish(create(SyncWorkspaceResponseSchema, {
|
|
@@ -28594,9 +28961,7 @@ var WorkspaceSyncWork = class {
|
|
|
28594
28961
|
offerReceipt: this.#offerReceipt
|
|
28595
28962
|
}));
|
|
28596
28963
|
}
|
|
28597
|
-
if (
|
|
28598
|
-
return this.#pullBeforeOffer();
|
|
28599
|
-
}
|
|
28964
|
+
if (custodianMoved) return this.#pullBeforeOffer();
|
|
28600
28965
|
return this.#offerOrReconcile();
|
|
28601
28966
|
}
|
|
28602
28967
|
this.#admissionSummary = summarizeCustodyAdmission(admission);
|
|
@@ -28930,7 +29295,8 @@ var WorkspaceSyncWork = class {
|
|
|
28930
29295
|
}));
|
|
28931
29296
|
}
|
|
28932
29297
|
#converged(verdict, candidateHead, receivedBytes, prefetchMillis, started = this.#clock.monotonicNanoseconds(), rebirth) {
|
|
28933
|
-
|
|
29298
|
+
const tracked = verdict.admitted && this.#candidateDurable ? this.#kernel.resolveRef(this.#mount, this.#requestId, trackedMain) : void 0;
|
|
29299
|
+
if (verdict.admitted && this.#candidateDurable && tracked !== candidateHead && (tracked === this.#previousHead || this.#containedBookmark !== void 0 && tracked === this.#containedBookmark)) {
|
|
28934
29300
|
this.#kernel.writeRef(
|
|
28935
29301
|
this.#mount,
|
|
28936
29302
|
this.#requestId,
|
|
@@ -29219,10 +29585,16 @@ var WorkspaceDependencySyncWork = class {
|
|
|
29219
29585
|
this.#plan = [this.#args.root];
|
|
29220
29586
|
return this.#advance();
|
|
29221
29587
|
}
|
|
29222
|
-
|
|
29588
|
+
const { kernel, requestId, root } = this.#args;
|
|
29589
|
+
const tracked = this.#args.liveConnected ? kernel.resolveRef(root.mount, requestId, trackedMain2) : void 0;
|
|
29590
|
+
if (tracked && kernel.head(root.mount, requestId) === tracked) {
|
|
29223
29591
|
this.#plan = this.#planDependencies();
|
|
29224
29592
|
return this.#advance();
|
|
29225
29593
|
}
|
|
29594
|
+
if (tracked) {
|
|
29595
|
+
this.#current = this.#work(root, `${this.workId}:contain`, true);
|
|
29596
|
+
return this.#accept(this.#current.start());
|
|
29597
|
+
}
|
|
29226
29598
|
this.#current = this.#work(this.#args.root, `${this.workId}:frontier`, true);
|
|
29227
29599
|
return this.#accept(this.#current.start());
|
|
29228
29600
|
}
|
|
@@ -30545,6 +30917,11 @@ var ReplicaFollowLane = class {
|
|
|
30545
30917
|
const delay = this.#controller.reconnected(workspace);
|
|
30546
30918
|
if (delay !== void 0) this.#scheduleAt(workspace, this.#now() + delay);
|
|
30547
30919
|
}
|
|
30920
|
+
/** The upstream announced a new frontier: news even to a follow the kernel refused. */
|
|
30921
|
+
remoteAdvanced(workspace) {
|
|
30922
|
+
const delay = this.#controller.reconnected(workspace, true);
|
|
30923
|
+
if (delay !== void 0) this.#scheduleAt(workspace, this.#now() + delay);
|
|
30924
|
+
}
|
|
30548
30925
|
/** The upstream rang for a new placement frontier (see ReplicaFollowController). */
|
|
30549
30926
|
placementAnnounced(workspace) {
|
|
30550
30927
|
const delay = this.#controller.placementAnnounced(workspace);
|
|
@@ -30859,6 +31236,7 @@ var CorrespondenceDesk = class {
|
|
|
30859
31236
|
#onPlacementFetched;
|
|
30860
31237
|
#onShallowMount;
|
|
30861
31238
|
#onOffered;
|
|
31239
|
+
#liveAdmitted;
|
|
30862
31240
|
#onOutboxHistoryRequired;
|
|
30863
31241
|
#backgroundHistory;
|
|
30864
31242
|
#receiveStep;
|
|
@@ -30915,6 +31293,7 @@ var CorrespondenceDesk = class {
|
|
|
30915
31293
|
this.#onPlacementFetched = args.onPlacementFetched;
|
|
30916
31294
|
this.#onShallowMount = args.onShallowMount;
|
|
30917
31295
|
this.#onOffered = args.onOffered;
|
|
31296
|
+
this.#liveAdmitted = args.liveAdmitted;
|
|
30918
31297
|
this.#onOutboxHistoryRequired = args.onOutboxHistoryRequired;
|
|
30919
31298
|
this.#backgroundHistory = args.backgroundHistory !== false;
|
|
30920
31299
|
this.#receiveStep = args.receiveStep;
|
|
@@ -31241,6 +31620,12 @@ var CorrespondenceDesk = class {
|
|
|
31241
31620
|
this.#followLane.reconnected(workspace);
|
|
31242
31621
|
this.reportFollows();
|
|
31243
31622
|
}
|
|
31623
|
+
/** The upstream announced a new frontier for this workspace over its live link: an event which
|
|
31624
|
+
* re-runs a round the kernel refused (its inputs changed). */
|
|
31625
|
+
replicaRemoteAdvanced(workspace) {
|
|
31626
|
+
this.#followLane.remoteAdvanced(workspace);
|
|
31627
|
+
this.reportFollows();
|
|
31628
|
+
}
|
|
31244
31629
|
/** The upstream rang for a new placement frontier: the next round re-checks placement even
|
|
31245
31630
|
* though main has not moved. */
|
|
31246
31631
|
replicaPlacementAnnounced(workspace) {
|
|
@@ -31277,10 +31662,11 @@ var CorrespondenceDesk = class {
|
|
|
31277
31662
|
const state = this.#follows.followForWorkspace(workspace)?.state;
|
|
31278
31663
|
if (!state) return [];
|
|
31279
31664
|
const isSettled = state.case === ReplicaFollowPhase.Settled;
|
|
31665
|
+
const refused = state.case === ReplicaFollowPhase.Backoff && state.refusal?.blocked === true;
|
|
31280
31666
|
const isLive = this.#openLinks.has(workspace);
|
|
31281
31667
|
if (isSettled) settled += 1;
|
|
31282
31668
|
if (isLive) live += 1;
|
|
31283
|
-
const phase = {
|
|
31669
|
+
const phase = refused ? FollowObservationPhase.KERNEL_REFUSED : {
|
|
31284
31670
|
[ReplicaFollowPhase.Desired]: FollowObservationPhase.DESIRED,
|
|
31285
31671
|
[ReplicaFollowPhase.Due]: FollowObservationPhase.DUE,
|
|
31286
31672
|
[ReplicaFollowPhase.Running]: FollowObservationPhase.RUNNING,
|
|
@@ -31294,7 +31680,7 @@ var CorrespondenceDesk = class {
|
|
|
31294
31680
|
[ReplicaFollowPhase.LocalCopyUnreadable]: FollowObservationPhase.LOCAL_COPY_UNREADABLE
|
|
31295
31681
|
}[state.case];
|
|
31296
31682
|
const detail = state.case === ReplicaFollowPhase.AwaitingDependency ? state.reason : state.case === ReplicaFollowPhase.InvalidCredential || state.case === ReplicaFollowPhase.Backoff ? state.failure : state.case === ReplicaFollowPhase.RebornRemotely ? state.rebirth.detail : state.case === ReplicaFollowPhase.LocalCopyUnreadable ? state.format : void 0;
|
|
31297
|
-
const retryAt = state.case === ReplicaFollowPhase.Offline || state.case === ReplicaFollowPhase.InvalidCredential || state.case === ReplicaFollowPhase.Backoff ? state.retryAtUnixMillis : void 0;
|
|
31683
|
+
const retryAt = refused ? state.case === ReplicaFollowPhase.Backoff ? state.refusal?.rerunAtUnixMillis : void 0 : state.case === ReplicaFollowPhase.Offline || state.case === ReplicaFollowPhase.InvalidCredential || state.case === ReplicaFollowPhase.Backoff ? state.retryAtUnixMillis : void 0;
|
|
31298
31684
|
const dueAt2 = state.case === ReplicaFollowPhase.Due || state.case === ReplicaFollowPhase.ReplacingLocal ? state.dueAtUnixMillis : void 0;
|
|
31299
31685
|
const attempt = state.case === ReplicaFollowPhase.Running || state.case === ReplicaFollowPhase.Offline || state.case === ReplicaFollowPhase.InvalidCredential || state.case === ReplicaFollowPhase.Backoff || state.case === ReplicaFollowPhase.ReplacingLocal ? state.attempt : void 0;
|
|
31300
31686
|
const activeEffect = state.case === ReplicaFollowPhase.Running ? this.#activeSyncEffect(workspace) : void 0;
|
|
@@ -31792,6 +32178,7 @@ var CorrespondenceDesk = class {
|
|
|
31792
32178
|
workId: id,
|
|
31793
32179
|
probeOnly,
|
|
31794
32180
|
liveConnected: this.#openLinks.has(target3.mount.workspace),
|
|
32181
|
+
...this.#openLinks.has(target3.mount.workspace) && this.#liveAdmitted?.(target3.mount.workspace) ? { liveAdmitted: this.#liveAdmitted(target3.mount.workspace) } : {},
|
|
31795
32182
|
...beforeConverge === void 0 ? {} : { beforeConverge },
|
|
31796
32183
|
...target3.receiveState === void 0 ? {} : { receiveState: target3.receiveState },
|
|
31797
32184
|
...target3.offerHead === void 0 ? {} : { offerHead: target3.offerHead },
|
|
@@ -33418,10 +33805,10 @@ var OfferReadinessDesk = class {
|
|
|
33418
33805
|
while (true) {
|
|
33419
33806
|
this.#ports.owe(owed.workspace);
|
|
33420
33807
|
while (this.#ports.owes(owed.workspace)) {
|
|
33421
|
-
const
|
|
33808
|
+
const { ticked, wake: readiness } = this.#ports.tick();
|
|
33422
33809
|
const delayMillis = Math.min(LONGEST_WAKE_MILLIS, Math.max(
|
|
33423
33810
|
FIRST_WAKE_MILLIS,
|
|
33424
|
-
|
|
33811
|
+
ticked.progress !== void 0 ? FIRST_WAKE_MILLIS : readiness.case === "after" ? readiness.millis : LONGEST_WAKE_MILLIS
|
|
33425
33812
|
));
|
|
33426
33813
|
const receipt = yield { case: "wake", value: create(PeerWakeEffectSchema, {
|
|
33427
33814
|
effectId: `${workId2}:repay-${++wake}`,
|
|
@@ -33553,7 +33940,6 @@ function isAnsweredStateRead(command, response) {
|
|
|
33553
33940
|
}
|
|
33554
33941
|
var INTERACTIVE_PEER_CLASSES = /* @__PURE__ */ new Set(["personal-device", "browser"]);
|
|
33555
33942
|
var INTERACTION_FIRST_MILLIS = 15e3;
|
|
33556
|
-
var READ_READINESS_ONLY = 1n << 62n;
|
|
33557
33943
|
var PLACEMENT_READ_RETRY_MILLIS = 60000n;
|
|
33558
33944
|
var STREAM_WAKE_FIRST_MILLIS = 20;
|
|
33559
33945
|
var HISTORY_STREAM_SLICES = [8, 64, 256];
|
|
@@ -33572,7 +33958,6 @@ var APPLICATION_WAITS = /* @__PURE__ */ new Set([
|
|
|
33572
33958
|
"continueLocalRead"
|
|
33573
33959
|
]);
|
|
33574
33960
|
var APPLICATION_WAIT_NANOS = 5000000000n;
|
|
33575
|
-
var READ_INDEX_BUILDING = /the read index of .+ is being built|read maintenance continues it/;
|
|
33576
33961
|
var PeerOffice = class {
|
|
33577
33962
|
#kernel;
|
|
33578
33963
|
#sessions = new SessionDirectory();
|
|
@@ -33767,7 +34152,6 @@ var PeerOffice = class {
|
|
|
33767
34152
|
}
|
|
33768
34153
|
}, (workspace) => this.#residency.lastRetainedHead(workspace));
|
|
33769
34154
|
if (this.#writeBurst) this.#live.writeBurst = this.#writeBurst;
|
|
33770
|
-
if (this.#interactivePeer) this.#live.receiveStep = this.#receiveStepBudget;
|
|
33771
34155
|
this.#credentials = new CredentialSessions(this.#sessions, credentials, observations);
|
|
33772
34156
|
this.#readability = new LocalCopyReadability((workspace, format) => {
|
|
33773
34157
|
this.#follows.markLocalCopyUnreadable(workspace, format);
|
|
@@ -33785,7 +34169,8 @@ var PeerOffice = class {
|
|
|
33785
34169
|
new ReplicaFollowDesk(seatedStore),
|
|
33786
34170
|
observations,
|
|
33787
34171
|
this.#replicaId,
|
|
33788
|
-
new BirthRefusalRegister(seatedStore)
|
|
34172
|
+
new BirthRefusalRegister(seatedStore),
|
|
34173
|
+
PEER_RUNTIME_IDENTITY
|
|
33789
34174
|
);
|
|
33790
34175
|
this.#compatibility = new CompatibilityDesk(
|
|
33791
34176
|
this.#queries,
|
|
@@ -33804,13 +34189,20 @@ var PeerOffice = class {
|
|
|
33804
34189
|
this.#trace,
|
|
33805
34190
|
observations,
|
|
33806
34191
|
{
|
|
33807
|
-
|
|
33808
|
-
|
|
33809
|
-
|
|
33810
|
-
workspace
|
|
33811
|
-
|
|
33812
|
-
|
|
33813
|
-
|
|
34192
|
+
// A read waiting on this workspace is released by the readiness event itself, not a timer.
|
|
34193
|
+
readReady: (workspace) => {
|
|
34194
|
+
this.#subscriptions.readIndexReady(workspace);
|
|
34195
|
+
this.#readResponses?.readinessChanged(workspace);
|
|
34196
|
+
},
|
|
34197
|
+
frontierAdvanced: (workspace) => {
|
|
34198
|
+
this.#subscriptions.readIndexAdvanced(workspace);
|
|
34199
|
+
this.#readResponses?.readinessChanged(workspace);
|
|
34200
|
+
},
|
|
34201
|
+
readinessMayChange: (workspace) => this.#readResponses?.readinessChanged(workspace),
|
|
34202
|
+
localCopyUnreadable: (workspace, format) => {
|
|
34203
|
+
this.#readability.mark(workspace, this.#kernel.head(workspaceMount(workspace), 0n), format);
|
|
34204
|
+
this.#readResponses?.readinessChanged(workspace);
|
|
34205
|
+
},
|
|
33814
34206
|
isLocalCopyUnreadable: (workspace) => this.#readability.decide(workspace) !== void 0
|
|
33815
34207
|
},
|
|
33816
34208
|
// The kernel owes this peer's writes to an ordered background flush the desk schedules.
|
|
@@ -33872,7 +34264,7 @@ var PeerOffice = class {
|
|
|
33872
34264
|
aggregateIds: new Set([...admitted.affected, ...admitted.created].map((ref) => ref.id)),
|
|
33873
34265
|
// What the offer changed, typed as reads resolve it: watches it cannot change are not read.
|
|
33874
34266
|
stateChange: admitted.stateChange
|
|
33875
|
-
}, () => this.#readMaintenance.
|
|
34267
|
+
}, () => this.#readMaintenance.tickReadReadiness().ticked.progress !== void 0));
|
|
33876
34268
|
noteTimeline("ownOffer", `${delivery.delivered}/${delivery.watches}`);
|
|
33877
34269
|
noteTimeline("ownOfferSkipped", delivery.skipped);
|
|
33878
34270
|
});
|
|
@@ -33887,7 +34279,12 @@ var PeerOffice = class {
|
|
|
33887
34279
|
observations,
|
|
33888
34280
|
// A waiting read advances read readiness only: it must never run a background command or
|
|
33889
34281
|
// closure warm on the foreground's turn.
|
|
33890
|
-
() => this.#readMaintenance.
|
|
34282
|
+
() => this.#readMaintenance.tickReadReadiness(),
|
|
34283
|
+
// A read the kernel still finds behind, with nothing owing its index, owes it again (as a watch does).
|
|
34284
|
+
(request) => {
|
|
34285
|
+
if (request.command.case !== "kernelCall") throw new Error("local read continuation lost its query");
|
|
34286
|
+
this.#readMaintenance.schedule(request.command.value.session, ReadMaintenanceCause.READ_DEBT);
|
|
34287
|
+
},
|
|
33891
34288
|
(request) => {
|
|
33892
34289
|
if (request.command.case !== "kernelCall") throw new Error("local read continuation lost its query");
|
|
33893
34290
|
return this.#application.call(request, request.command.value);
|
|
@@ -33988,6 +34385,7 @@ var PeerOffice = class {
|
|
|
33988
34385
|
onShallowMount: (workspace, credentialCapability) => this.#historyAcquisition.materialise(workspace, credentialCapability),
|
|
33989
34386
|
// The edge echoes an HTTP offer batch back over this replica's own live link (CAP-764).
|
|
33990
34387
|
onOffered: (workspace, commitOids) => this.#live.offered(workspace, commitOids),
|
|
34388
|
+
liveAdmitted: (workspace) => this.#live.admittedLocal(workspace),
|
|
33991
34389
|
onOutboxHistoryRequired: (workspace, credentialCapability) => this.#historyAcquisition.requestFromBackground(workspace, credentialCapability),
|
|
33992
34390
|
backgroundHistory: profile.backgroundHistory !== false,
|
|
33993
34391
|
onSyncCompleted: (session, event) => {
|
|
@@ -34013,7 +34411,7 @@ var PeerOffice = class {
|
|
|
34013
34411
|
this.#origins.ignored(workspace, origin2, "follow");
|
|
34014
34412
|
return false;
|
|
34015
34413
|
};
|
|
34016
|
-
this.#live.remoteAdvanced = (workspace) => this.#correspondence.
|
|
34414
|
+
this.#live.remoteAdvanced = (workspace) => this.#correspondence.replicaRemoteAdvanced(workspace);
|
|
34017
34415
|
this.#live.declined = (workspace) => this.#correspondence.liveAdmissionDeclined(workspace);
|
|
34018
34416
|
this.#live.acknowledged = (workspace, ack) => this.#correspondence.liveAdmissionAcknowledged(workspace, ack);
|
|
34019
34417
|
this.#live.intentsAdmitted = (workspace, intentIds, local) => this.#correspondence.liveIntentsAdmitted(workspace, intentIds, local);
|
|
@@ -34225,7 +34623,7 @@ var PeerOffice = class {
|
|
|
34225
34623
|
},
|
|
34226
34624
|
owes: (workspace) => this.#readMaintenance.owesReadReadiness(workspace),
|
|
34227
34625
|
// Read readiness only, as a waiting read takes: never a background command or pack import (CAP-806).
|
|
34228
|
-
tick: () => this.#readMaintenance.
|
|
34626
|
+
tick: () => this.#readMaintenance.tickReadReadiness(),
|
|
34229
34627
|
emit: (session, event) => this.#trace.emit(session, event)
|
|
34230
34628
|
});
|
|
34231
34629
|
this.#modelAdoption = new ModelAdoptionDesk({
|
|
@@ -34623,9 +35021,8 @@ var PeerOffice = class {
|
|
|
34623
35021
|
let pendingEffectId;
|
|
34624
35022
|
try {
|
|
34625
35023
|
if (request.command.case === "socketReceipt") {
|
|
34626
|
-
|
|
34627
|
-
|
|
34628
|
-
request = admission;
|
|
35024
|
+
this.#live.receive(request.command.value);
|
|
35025
|
+
return this.#response(request, { case: "socketObserved", value: create(PeerSocketObservedSchema) });
|
|
34629
35026
|
}
|
|
34630
35027
|
const result = timelinePhase("access", () => this.#access.handle(request));
|
|
34631
35028
|
if (result.outcome.case === "correspondencePending") {
|
|
@@ -34634,11 +35031,9 @@ var PeerOffice = class {
|
|
|
34634
35031
|
}
|
|
34635
35032
|
if (result.outcome.case === "watchStarted") this.#subscriptions.retainDependencies(result.outcome.value.subscriptionId, scope);
|
|
34636
35033
|
this.#subscriptions.recoverySettled(result);
|
|
34637
|
-
this.#live.settled(request, result);
|
|
34638
35034
|
this.#correspondence.observeResponse(result);
|
|
34639
35035
|
return result;
|
|
34640
35036
|
} catch (error) {
|
|
34641
|
-
this.#live.failed(request.requestId);
|
|
34642
35037
|
const failure = this.#failure(request, "nomos.runtime.workspace_access", error instanceof Error ? error.message : String(error));
|
|
34643
35038
|
this.#subscriptions.recoverySettled(failure);
|
|
34644
35039
|
this.#correspondence.observeResponse(failure);
|
|
@@ -34905,11 +35300,7 @@ var PeerOffice = class {
|
|
|
34905
35300
|
try {
|
|
34906
35301
|
const mount = this.#sessions.mountForWorkspace(request.target.workspace);
|
|
34907
35302
|
const previousHead = this.#kernel.head(mount, request.requestId);
|
|
34908
|
-
const admission = this.#custodyAdmission.admit(
|
|
34909
|
-
request,
|
|
34910
|
-
request.command.value,
|
|
34911
|
-
{ journal: !this.#live.relayed(request.requestId) }
|
|
34912
|
-
);
|
|
35303
|
+
const admission = this.#custodyAdmission.admit(request, request.command.value);
|
|
34913
35304
|
admission.sealedMain = this.#residency.retainedHead(request.target.workspace);
|
|
34914
35305
|
if (custodyAdmissionNeedsReadMaintenance(admission)) {
|
|
34915
35306
|
admission.localMaintenance = this.#readMaintenance.scheduleWorkspace(
|
|
@@ -35026,16 +35417,17 @@ var PeerOffice = class {
|
|
|
35026
35417
|
});
|
|
35027
35418
|
case "historicQuery": {
|
|
35028
35419
|
const answered2 = this.#historicQueries.execute(request, request.command.value);
|
|
35029
|
-
if (answered2.outcome.case === "
|
|
35420
|
+
if (answered2.outcome.case === "query" && answered2.outcome.value.result.case === "readIndexPending") {
|
|
35030
35421
|
this.#readMaintenance.schedule(request.command.value.session, ReadMaintenanceCause.READ_DEBT);
|
|
35422
|
+
const workspace = answered2.outcome.value.result.value.workspace || (this.#sessions.bindingIfPresent(request.command.value.session)?.workspace ?? "");
|
|
35031
35423
|
return this.#response(request, {
|
|
35032
35424
|
case: "unavailable",
|
|
35033
35425
|
value: create(RuntimeUnavailableSchema, {
|
|
35034
35426
|
reason: RuntimeUnavailableReason.LOCAL_REPLICA_NOT_READY,
|
|
35035
|
-
workspace
|
|
35427
|
+
workspace,
|
|
35036
35428
|
retryable: true,
|
|
35037
35429
|
retryAfterMillis: 250,
|
|
35038
|
-
detail:
|
|
35430
|
+
detail: `the read index of ${workspace} is being built on this device; read maintenance continues it`
|
|
35039
35431
|
})
|
|
35040
35432
|
});
|
|
35041
35433
|
}
|
|
@@ -35455,6 +35847,10 @@ var PeerOffice = class {
|
|
|
35455
35847
|
nextEvent() {
|
|
35456
35848
|
return this.#trace.nextEvent();
|
|
35457
35849
|
}
|
|
35850
|
+
/** In-process shells wait on a held local read here instead of polling `resumeAfterMillis`. */
|
|
35851
|
+
localReadResumable(workId2, notify) {
|
|
35852
|
+
return this.#readResponses.onResumable(workId2, notify);
|
|
35853
|
+
}
|
|
35458
35854
|
close() {
|
|
35459
35855
|
if (this.retentionOutstanding() > 0) {
|
|
35460
35856
|
throw new Error("custody retention pending; retain the acknowledged frontiers before disposing the peer");
|
|
@@ -35804,6 +36200,23 @@ function createPeerOfficeFrameRunner(kernel, credentials, profile, observations,
|
|
|
35804
36200
|
lanes?.wake(delayMillis);
|
|
35805
36201
|
};
|
|
35806
36202
|
const runner = {
|
|
36203
|
+
localReadResumable(workId2) {
|
|
36204
|
+
let resolve4;
|
|
36205
|
+
const resumable = new Promise((done) => {
|
|
36206
|
+
resolve4 = done;
|
|
36207
|
+
});
|
|
36208
|
+
let timer;
|
|
36209
|
+
const wait = peer2.localReadResumable(workId2, () => {
|
|
36210
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
36211
|
+
resolve4();
|
|
36212
|
+
});
|
|
36213
|
+
if (wait === void 0) return void 0;
|
|
36214
|
+
timer = setTimeout(() => {
|
|
36215
|
+
wait.cancel();
|
|
36216
|
+
resolve4();
|
|
36217
|
+
}, wait.afterMillis);
|
|
36218
|
+
return resumable;
|
|
36219
|
+
},
|
|
35807
36220
|
handleFrame(frame2) {
|
|
35808
36221
|
const response = peer2.invoke(frame2).responseProtobuf;
|
|
35809
36222
|
drainEvents();
|
|
@@ -36032,7 +36445,7 @@ async function drivePeerFrame(runner, initialFrame, fetchImpl = globalThis.fetch
|
|
|
36032
36445
|
if (response.outcome.case === "localReadPending") {
|
|
36033
36446
|
if (yieldLocalReadToCaller) return responseFrame;
|
|
36034
36447
|
const pending2 = response.outcome.value;
|
|
36035
|
-
await new Promise((resolve4) => setTimeout(resolve4, pending2.resumeAfterMillis));
|
|
36448
|
+
await (runner.localReadResumable?.(pending2.workId) ?? new Promise((resolve4) => setTimeout(resolve4, pending2.resumeAfterMillis)));
|
|
36036
36449
|
responseFrame = await turn(() => runner.handleFrame(toBinary(RuntimeRequestSchema, create(RuntimeRequestSchema, {
|
|
36037
36450
|
protocolVersion: response.protocolVersion,
|
|
36038
36451
|
requestId: response.requestId,
|
|
@@ -37239,11 +37652,30 @@ var sha256Hex2 = async (bytes) => {
|
|
|
37239
37652
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
37240
37653
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
37241
37654
|
};
|
|
37242
|
-
var okBytes = async (fetchImpl, url) => {
|
|
37655
|
+
var okBytes = async (fetchImpl, url, progress, expectedBytes) => {
|
|
37243
37656
|
const response = await fetchImpl(url);
|
|
37244
37657
|
if (!response.ok) throw new Error(`runtime artifact ${url}: HTTP ${response.status}`);
|
|
37245
|
-
|
|
37658
|
+
const reader = progress ? response.body?.getReader() : void 0;
|
|
37659
|
+
if (!reader) return new Uint8Array(await response.arrayBuffer());
|
|
37660
|
+
const total = expectedBytes ?? Number(response.headers.get("content-length") ?? 0);
|
|
37661
|
+
const chunks = [];
|
|
37662
|
+
let received = 0;
|
|
37663
|
+
for (; ; ) {
|
|
37664
|
+
const { done, value } = await reader.read();
|
|
37665
|
+
if (done) break;
|
|
37666
|
+
chunks.push(value);
|
|
37667
|
+
received += value.byteLength;
|
|
37668
|
+
progress(received, total);
|
|
37669
|
+
}
|
|
37670
|
+
const bytes = new Uint8Array(received);
|
|
37671
|
+
let offset = 0;
|
|
37672
|
+
for (const chunk of chunks) {
|
|
37673
|
+
bytes.set(chunk, offset);
|
|
37674
|
+
offset += chunk.byteLength;
|
|
37675
|
+
}
|
|
37676
|
+
return bytes;
|
|
37246
37677
|
};
|
|
37678
|
+
var elapsedSince = (started) => globalThis.performance.now() - started;
|
|
37247
37679
|
async function selectReleaseKernel(args) {
|
|
37248
37680
|
const fetchImpl = args.fetchImpl ?? ((input, init) => globalThis.fetch(input, init));
|
|
37249
37681
|
const base = args.base.replace(/\/+$/, "");
|
|
@@ -37271,11 +37703,27 @@ async function selectReleaseKernel(args) {
|
|
|
37271
37703
|
}
|
|
37272
37704
|
}
|
|
37273
37705
|
const url = new URL(relativeUrl, repositoryUrl).toString();
|
|
37274
|
-
const
|
|
37275
|
-
|
|
37276
|
-
|
|
37706
|
+
const report = (progress) => {
|
|
37707
|
+
try {
|
|
37708
|
+
args.onProgress?.(progress);
|
|
37709
|
+
} catch {
|
|
37710
|
+
}
|
|
37711
|
+
};
|
|
37712
|
+
let started = globalThis.performance.now();
|
|
37713
|
+
const bytes = await okBytes(fetchImpl, url, args.onProgress ? (receivedBytes, totalBytes) => report({ stage: "download", artifact: name, receivedBytes, totalBytes }) : void 0, expected?.size);
|
|
37714
|
+
report({ stage: "fetched", artifact: name, millis: elapsedSince(started) });
|
|
37715
|
+
if (expected) {
|
|
37716
|
+
started = globalThis.performance.now();
|
|
37717
|
+
if (bytes.byteLength !== expected.size || await sha256Hex2(bytes) !== expected.sha256) {
|
|
37718
|
+
throw new Error(`runtime artifact ${url} does not match the OSGi resource it was selected by`);
|
|
37719
|
+
}
|
|
37720
|
+
report({ stage: "verified", artifact: name, millis: elapsedSince(started) });
|
|
37721
|
+
}
|
|
37722
|
+
if (expected && args.cache) {
|
|
37723
|
+
started = globalThis.performance.now();
|
|
37724
|
+
await args.cache.write(resource.contentSha256, name, bytes);
|
|
37725
|
+
report({ stage: "stored", artifact: name, millis: elapsedSince(started) });
|
|
37277
37726
|
}
|
|
37278
|
-
if (expected && args.cache) await args.cache.write(resource.contentSha256, name, bytes);
|
|
37279
37727
|
return bytes;
|
|
37280
37728
|
};
|
|
37281
37729
|
await verifySelectedKernel({
|
|
@@ -37339,6 +37787,34 @@ async function verifySelectedKernel(args) {
|
|
|
37339
37787
|
}
|
|
37340
37788
|
}
|
|
37341
37789
|
|
|
37790
|
+
// ../../platform-shells/shared/retained_boot_fetch.ts
|
|
37791
|
+
init_define_NOMOS_KERNEL_RELEASE();
|
|
37792
|
+
function retainedBootFetch(base, fetchImpl, store, repositoryUrl) {
|
|
37793
|
+
const root = base.replace(/\/+$/, "");
|
|
37794
|
+
return async (input, init) => {
|
|
37795
|
+
const url = String(input);
|
|
37796
|
+
const bootArtifact = url === (repositoryUrl ?? `${root}/v1/runtime/repository.xml`) || url === `${root}/v1/runtime/nomos.law.usdz`;
|
|
37797
|
+
if (!bootArtifact) return fetchImpl(input, init);
|
|
37798
|
+
const networkInit = { ...init, cache: "no-store", signal: init?.signal ?? AbortSignal.timeout(15e3) };
|
|
37799
|
+
if (!store) return fetchImpl(input, networkInit);
|
|
37800
|
+
let response;
|
|
37801
|
+
let failure;
|
|
37802
|
+
try {
|
|
37803
|
+
response = await fetchImpl(input, networkInit);
|
|
37804
|
+
} catch (error) {
|
|
37805
|
+
failure = error;
|
|
37806
|
+
}
|
|
37807
|
+
if (response !== void 0 && response.status < 500) {
|
|
37808
|
+
if (response.ok) await store.put(url, response.clone());
|
|
37809
|
+
return response;
|
|
37810
|
+
}
|
|
37811
|
+
const retained = await store.match(url);
|
|
37812
|
+
if (retained) return retained;
|
|
37813
|
+
if (response !== void 0) return response;
|
|
37814
|
+
throw failure;
|
|
37815
|
+
};
|
|
37816
|
+
}
|
|
37817
|
+
|
|
37342
37818
|
// ../../runtime/kernel_local_dev_trust.ts
|
|
37343
37819
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
37344
37820
|
var LOCAL_DEV_KERNEL_TRUST_ROOT_ENV = "NOMOS_LOCAL_DEV_KERNEL_TRUST_ROOT";
|
|
@@ -37408,10 +37884,28 @@ var physicalFetch = async (input, init) => {
|
|
|
37408
37884
|
}, { signal: gitHttpBackendLifetime.signal });
|
|
37409
37885
|
return new Response(result.body, { status: result.status, headers: result.headers });
|
|
37410
37886
|
};
|
|
37887
|
+
var diskBootStore = (directory2) => {
|
|
37888
|
+
const file = (url) => join5(directory2, createHash2("sha256").update(url).digest("hex"));
|
|
37889
|
+
return {
|
|
37890
|
+
async match(url) {
|
|
37891
|
+
if (!existsSync5(`${file(url)}.body`) || !existsSync5(`${file(url)}.headers.json`)) return void 0;
|
|
37892
|
+
return new Response(new Uint8Array(readFileSync3(`${file(url)}.body`)), {
|
|
37893
|
+
headers: JSON.parse(readFileSync3(`${file(url)}.headers.json`, "utf8"))
|
|
37894
|
+
});
|
|
37895
|
+
},
|
|
37896
|
+
async put(url, response) {
|
|
37897
|
+
mkdirSync5(directory2, { recursive: true });
|
|
37898
|
+
writeFileSync5(`${file(url)}.body`, new Uint8Array(await response.arrayBuffer()));
|
|
37899
|
+
writeFileSync5(`${file(url)}.headers.json`, JSON.stringify(Object.fromEntries(response.headers)));
|
|
37900
|
+
}
|
|
37901
|
+
};
|
|
37902
|
+
};
|
|
37411
37903
|
var fetchReleaseBundle = async (cloud) => {
|
|
37412
37904
|
const cacheRoot = join5(data, "runtime");
|
|
37413
37905
|
const selected = await selectReleaseKernel({
|
|
37414
37906
|
base: cloud,
|
|
37907
|
+
// Offline-first: an unreachable channel boots the last selection this peer verified, verified again now.
|
|
37908
|
+
fetchImpl: retainedBootFetch(cloud, (input, init) => fetch(input, init), diskBootStore(join5(cacheRoot, "boot"))),
|
|
37415
37909
|
release: define_NOMOS_KERNEL_RELEASE_default,
|
|
37416
37910
|
// The pinned production roots; plus a developer's local-dev root only when named explicitly AND the custodian is
|
|
37417
37911
|
// on this machine (CAP-813) — announced on stderr either way.
|