@hraness/oh 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +598 -0
  3. package/dist/canonical.d.ts +32 -0
  4. package/dist/canonical.d.ts.map +1 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +3419 -0
  8. package/dist/contract.d.ts +33 -0
  9. package/dist/contract.d.ts.map +1 -0
  10. package/dist/graph.d.ts +67 -0
  11. package/dist/graph.d.ts.map +1 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +1988 -0
  15. package/dist/libsql.d.ts +57 -0
  16. package/dist/libsql.d.ts.map +1 -0
  17. package/dist/libsql.js +2662 -0
  18. package/dist/memory.d.ts +366 -0
  19. package/dist/memory.d.ts.map +1 -0
  20. package/dist/memory.js +3650 -0
  21. package/dist/ontology.d.ts +242 -0
  22. package/dist/ontology.d.ts.map +1 -0
  23. package/dist/operation.d.ts +24 -0
  24. package/dist/operation.d.ts.map +1 -0
  25. package/dist/projection-public.d.ts +59 -0
  26. package/dist/projection-public.d.ts.map +1 -0
  27. package/dist/projection-public.js +1682 -0
  28. package/dist/projection-suss.d.ts +17 -0
  29. package/dist/projection-suss.d.ts.map +1 -0
  30. package/dist/projection-suss.js +1721 -0
  31. package/dist/projection.d.ts +315 -0
  32. package/dist/projection.d.ts.map +1 -0
  33. package/dist/schema.d.ts +45 -0
  34. package/dist/schema.d.ts.map +1 -0
  35. package/dist/sdk.d.ts +51 -0
  36. package/dist/sdk.d.ts.map +1 -0
  37. package/dist/sdk.js +3072 -0
  38. package/dist/search.d.ts +34 -0
  39. package/dist/search.d.ts.map +1 -0
  40. package/dist/semantic.d.ts +83 -0
  41. package/dist/semantic.d.ts.map +1 -0
  42. package/dist/semantic.js +706 -0
  43. package/dist/sqlite/driver.d.ts +6 -0
  44. package/dist/sqlite/driver.d.ts.map +1 -0
  45. package/dist/sqlite/index.d.ts +5 -0
  46. package/dist/sqlite/index.d.ts.map +1 -0
  47. package/dist/sqlite/index.js +2840 -0
  48. package/dist/sqlite/migrations.d.ts +10 -0
  49. package/dist/sqlite/migrations.d.ts.map +1 -0
  50. package/dist/sqlite/port.d.ts +40 -0
  51. package/dist/sqlite/port.d.ts.map +1 -0
  52. package/dist/sqlite/runtime.d.ts +23 -0
  53. package/dist/sqlite/runtime.d.ts.map +1 -0
  54. package/dist/sqlite/store.d.ts +85 -0
  55. package/dist/sqlite/store.d.ts.map +1 -0
  56. package/dist/store.d.ts +236 -0
  57. package/dist/store.d.ts.map +1 -0
  58. package/dist/store.js +996 -0
  59. package/dist/sync.d.ts +57 -0
  60. package/dist/sync.d.ts.map +1 -0
  61. package/dist/sync.js +1328 -0
  62. package/package.json +121 -0
  63. package/skills/oh/SKILL.md +206 -0
  64. package/skills/oh/agents/openai.yaml +4 -0
  65. package/spec/README.md +74 -0
  66. package/spec/manifest.json +41 -0
  67. package/spec/v1/canonical-json.md +59 -0
  68. package/spec/v1/contract.json +28 -0
  69. package/spec/v1/contract.schema.json +58 -0
  70. package/spec/v1/embedding-profile.json +11 -0
  71. package/spec/v1/embedding.md +56 -0
  72. package/spec/v1/graph.md +87 -0
  73. package/spec/v1/memory.md +193 -0
  74. package/spec/v1/migration.md +92 -0
  75. package/spec/v1/ontology.json +55 -0
  76. package/spec/v1/ontology.md +80 -0
  77. package/spec/v1/operation.schema.json +138 -0
  78. package/spec/v1/projection-identity.schema.json +58 -0
  79. package/spec/v1/projection-query.schema.json +60 -0
  80. package/spec/v1/projection-result.schema.json +452 -0
  81. package/spec/v1/projection-rule-pack.schema.json +182 -0
  82. package/spec/v1/projection.md +165 -0
  83. package/spec/v1/record.schema.json +95 -0
  84. package/spec/v1/schema-evolution.md +51 -0
  85. package/spec/v1/schema-revision.schema.json +178 -0
  86. package/spec/v1/storage.md +88 -0
  87. package/spec/v1/store.md +131 -0
  88. package/spec/v1/sync-bundle.schema.json +51 -0
  89. package/spec/v1/sync.md +67 -0
  90. package/src/canonical.test.ts +46 -0
  91. package/src/canonical.ts +203 -0
  92. package/src/cli.test.ts +103 -0
  93. package/src/cli.ts +308 -0
  94. package/src/contract.ts +87 -0
  95. package/src/contracts.test.ts +147 -0
  96. package/src/graph.ts +248 -0
  97. package/src/index.ts +8 -0
  98. package/src/libsql.test.ts +657 -0
  99. package/src/libsql.ts +1687 -0
  100. package/src/memory.test.ts +783 -0
  101. package/src/memory.ts +1684 -0
  102. package/src/ontology.ts +573 -0
  103. package/src/operation.ts +80 -0
  104. package/src/projection-public.ts +53 -0
  105. package/src/projection-suss.ts +129 -0
  106. package/src/projection.test.ts +418 -0
  107. package/src/projection.ts +1457 -0
  108. package/src/schema.ts +156 -0
  109. package/src/sdk.ts +96 -0
  110. package/src/search.ts +66 -0
  111. package/src/semantic.test.ts +480 -0
  112. package/src/semantic.ts +333 -0
  113. package/src/sqlite/driver.ts +47 -0
  114. package/src/sqlite/index.ts +4 -0
  115. package/src/sqlite/migrations.test.ts +44 -0
  116. package/src/sqlite/migrations.ts +178 -0
  117. package/src/sqlite/port.test.ts +127 -0
  118. package/src/sqlite/port.ts +120 -0
  119. package/src/sqlite/runtime.test.ts +68 -0
  120. package/src/sqlite/runtime.ts +53 -0
  121. package/src/sqlite/store.test.ts +295 -0
  122. package/src/sqlite/store.ts +988 -0
  123. package/src/store.test.ts +121 -0
  124. package/src/store.ts +701 -0
  125. package/src/sync.test.ts +117 -0
  126. package/src/sync.ts +227 -0
package/dist/memory.js ADDED
@@ -0,0 +1,3650 @@
1
+ // src/canonical.ts
2
+ import { createHash, randomBytes } from "node:crypto";
3
+
4
+ class OhValidationError extends Error {
5
+ code;
6
+ path;
7
+ constructor(code, path, message) {
8
+ super(`${path}: ${message}`);
9
+ this.name = "OhValidationError";
10
+ this.code = code;
11
+ this.path = path;
12
+ }
13
+ }
14
+ function isPlainRecord(value) {
15
+ if (typeof value !== "object" || value === null || Array.isArray(value))
16
+ return false;
17
+ const prototype = Object.getPrototypeOf(value);
18
+ return prototype === Object.prototype || prototype === null;
19
+ }
20
+ function hasExactKeys(value, keys) {
21
+ const actual = Object.keys(value);
22
+ return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key));
23
+ }
24
+ function assertUnicodeScalarString(value, path) {
25
+ for (let index = 0;index < value.length; index += 1) {
26
+ const code = value.charCodeAt(index);
27
+ if (code >= 55296 && code <= 56319) {
28
+ const next = value.charCodeAt(index + 1);
29
+ if (!(next >= 56320 && next <= 57343)) {
30
+ throw new OhValidationError("invalid-unicode", path, "contains an unpaired surrogate");
31
+ }
32
+ index += 1;
33
+ } else if (code >= 56320 && code <= 57343) {
34
+ throw new OhValidationError("invalid-unicode", path, "contains an unpaired surrogate");
35
+ }
36
+ }
37
+ }
38
+ function encodeCanonical(value, path, ancestors) {
39
+ if (value === null || typeof value === "boolean")
40
+ return JSON.stringify(value);
41
+ if (typeof value === "string") {
42
+ assertUnicodeScalarString(value, path);
43
+ return JSON.stringify(value);
44
+ }
45
+ if (typeof value === "number") {
46
+ if (!Number.isFinite(value)) {
47
+ throw new OhValidationError("non-json-number", path, "must be finite");
48
+ }
49
+ if (Object.is(value, -0)) {
50
+ throw new OhValidationError("noncanonical-number", path, "negative zero is not canonical");
51
+ }
52
+ return JSON.stringify(value);
53
+ }
54
+ if (typeof value !== "object" || value === null) {
55
+ throw new OhValidationError("non-json-value", path, `cannot encode ${typeof value}`);
56
+ }
57
+ if (ancestors.has(value)) {
58
+ throw new OhValidationError("cycle", path, "contains a cycle");
59
+ }
60
+ ancestors.add(value);
61
+ try {
62
+ if (Array.isArray(value)) {
63
+ const encoded = [];
64
+ for (let index = 0;index < value.length; index += 1) {
65
+ if (!Object.hasOwn(value, index)) {
66
+ throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
67
+ }
68
+ encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
69
+ }
70
+ const extraKeys = Reflect.ownKeys(value).filter((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= value.length));
71
+ if (extraKeys.length > 0) {
72
+ throw new OhValidationError("non-json-property", path, "array has non-index properties");
73
+ }
74
+ return `[${encoded.join(",")}]`;
75
+ }
76
+ if (!isPlainRecord(value)) {
77
+ throw new OhValidationError("non-plain-object", path, "must be a plain object");
78
+ }
79
+ const ownKeys = Reflect.ownKeys(value);
80
+ if (ownKeys.some((key) => typeof key !== "string")) {
81
+ throw new OhValidationError("non-json-property", path, "object has a symbol property");
82
+ }
83
+ const keys = ownKeys;
84
+ for (const key of keys) {
85
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
86
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
87
+ throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
88
+ }
89
+ }
90
+ keys.sort();
91
+ const entries = keys.map((key) => {
92
+ assertUnicodeScalarString(key, `${path}.<key>`);
93
+ return `${JSON.stringify(key)}:${encodeCanonical(value[key], `${path}.${key}`, ancestors)}`;
94
+ });
95
+ return `{${entries.join(",")}}`;
96
+ } finally {
97
+ ancestors.delete(value);
98
+ }
99
+ }
100
+ function canonicalJson(value) {
101
+ return encodeCanonical(value, "$", new Set);
102
+ }
103
+ function parseCanonicalJson(text, maximumBytes = 16 * 1024 * 1024) {
104
+ if (utf8ByteLength(text) > maximumBytes) {
105
+ throw new OhValidationError("limit-exceeded", "$", "canonical JSON exceeds its byte limit");
106
+ }
107
+ let value;
108
+ try {
109
+ value = JSON.parse(text);
110
+ } catch {
111
+ throw new OhValidationError("invalid-json", "$", "is not valid JSON");
112
+ }
113
+ if (canonicalJson(value) !== text) {
114
+ throw new OhValidationError("noncanonical-json", "$", "keys or values are not canonical");
115
+ }
116
+ return value;
117
+ }
118
+ function utf8ByteLength(value) {
119
+ return Buffer.byteLength(value, "utf8");
120
+ }
121
+ function sha256Hex(value) {
122
+ return createHash("sha256").update(value).digest("hex");
123
+ }
124
+ function canonicalSha256(value) {
125
+ return sha256Hex(canonicalJson(value));
126
+ }
127
+ function parseSha256Hex(value) {
128
+ return typeof value === "string" && /^[a-f0-9]{64}$/u.test(value) ? value : null;
129
+ }
130
+ function parseCanonicalInstantV1(value) {
131
+ if (typeof value !== "string" || !/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}Z$/u.test(value)) {
132
+ return null;
133
+ }
134
+ const timestamp = Date.parse(value);
135
+ return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value ? value : null;
136
+ }
137
+ function canonicalNow() {
138
+ return new Date().toISOString();
139
+ }
140
+ function safeCode(value, maximumLength = 128) {
141
+ return typeof value === "string" && value.length <= maximumLength && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
142
+ }
143
+ function orderedUnique(values, key) {
144
+ return values.every((value, index) => index === 0 || key(values[index - 1]) < key(value));
145
+ }
146
+ function sortUnique(values, key) {
147
+ const sorted = [...values].sort((left, right) => {
148
+ const leftKey = key(left);
149
+ const rightKey = key(right);
150
+ return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
151
+ });
152
+ if (!orderedUnique(sorted, key)) {
153
+ throw new OhValidationError("duplicate", "$", "contains duplicate canonical values");
154
+ }
155
+ return sorted;
156
+ }
157
+
158
+ // src/graph.ts
159
+ var OH_GRAPH_FORMAT_VERSION_V1 = 1;
160
+ var OH_GRAPH_LIMITS_V1 = Object.freeze({
161
+ changesPerOperation: 8192,
162
+ dependenciesPerRecord: 4096,
163
+ recordBytes: 1024 * 1024,
164
+ recordsPerSnapshot: 65536
165
+ });
166
+ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
167
+ "activity",
168
+ "assertion",
169
+ "context",
170
+ "dependency-manifest",
171
+ "edition",
172
+ "entity",
173
+ "evidence",
174
+ "identity-operation",
175
+ "inquiry",
176
+ "inquiry-event",
177
+ "review-decision",
178
+ "rights-decision",
179
+ "schema",
180
+ "shape",
181
+ "statement",
182
+ "type-membership",
183
+ "view",
184
+ "vocabulary"
185
+ ];
186
+ function recordKey(value) {
187
+ return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
188
+ }
189
+ function createKnowledgeGraphRecordV1(input) {
190
+ if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1 || !Array.isArray(input.dependencies))
191
+ throw new TypeError("Invalid graph record input.");
192
+ const key = recordKey(input.key);
193
+ const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
194
+ if (key === null || kind === undefined || input.dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
195
+ throw new TypeError("Invalid graph record identity.");
196
+ const dependencies = input.dependencies.map(recordKey);
197
+ if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
198
+ throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
199
+ }
200
+ const valueJson = canonicalJson(input.value);
201
+ if (Buffer.byteLength(valueJson, "utf8") > OH_GRAPH_LIMITS_V1.recordBytes) {
202
+ throw new RangeError("Graph record value exceeds its canonical byte limit.");
203
+ }
204
+ const payload = { dependencies, key, kind, v: 1, value: input.value };
205
+ return { ...payload, recordSha256: canonicalSha256(payload) };
206
+ }
207
+ function parseKnowledgeGraphRecordV1(value) {
208
+ if (!isPlainRecord(value) || !Object.hasOwn(value, "recordSha256"))
209
+ return null;
210
+ const recordSha256 = parseSha256Hex(value.recordSha256);
211
+ const { recordSha256: _digest, ...input } = value;
212
+ try {
213
+ const created = createKnowledgeGraphRecordV1(input);
214
+ return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
215
+ } catch {
216
+ return null;
217
+ }
218
+ }
219
+ function knowledgeGraphRecordRefV1(record) {
220
+ return {
221
+ dependencies: record.dependencies,
222
+ key: record.key,
223
+ kind: record.kind,
224
+ sha256: record.recordSha256,
225
+ v: 1
226
+ };
227
+ }
228
+ function changeKey(change) {
229
+ return change.kind === "put" ? change.record.key : change.key;
230
+ }
231
+ function canonicalKnowledgeGraphChangesV1(changes) {
232
+ const normalized = [];
233
+ for (const change of changes) {
234
+ if (!isPlainRecord(change) || change.v !== 1)
235
+ throw new TypeError("Invalid graph change.");
236
+ if (change.kind === "put") {
237
+ const record = parseKnowledgeGraphRecordV1(change.record);
238
+ if (record === null)
239
+ throw new TypeError("Invalid graph record in change.");
240
+ normalized.push({ kind: "put", record, v: 1 });
241
+ } else if (change.kind === "tombstone") {
242
+ const key = recordKey(change.key);
243
+ const priorSha256 = parseSha256Hex(change.priorSha256);
244
+ if (key === null || priorSha256 === null)
245
+ throw new TypeError("Invalid graph tombstone.");
246
+ normalized.push({ key, kind: "tombstone", priorSha256, v: 1 });
247
+ } else
248
+ throw new TypeError("Unknown graph change kind.");
249
+ }
250
+ return sortUnique(normalized, changeKey);
251
+ }
252
+ function graphRevisionSha256V1(input) {
253
+ const changes = canonicalKnowledgeGraphChangesV1(input.changes);
254
+ const operationId = safeCode(input.operationId);
255
+ const parentGraphRevisionSha256 = input.parentGraphRevisionSha256 === null ? null : parseSha256Hex(input.parentGraphRevisionSha256);
256
+ const recordsSha256 = parseSha256Hex(input.recordsSha256);
257
+ const revision = Number.isSafeInteger(input.revision) && input.revision > 0 ? input.revision : null;
258
+ if (changes.length === 0 || changes.length > OH_GRAPH_LIMITS_V1.changesPerOperation || operationId === null || recordsSha256 === null || revision === null || input.parentGraphRevisionSha256 !== null && parentGraphRevisionSha256 === null || revision === 1 !== (parentGraphRevisionSha256 === null)) {
259
+ throw new TypeError("Invalid graph revision digest input.");
260
+ }
261
+ return canonicalSha256({ changes, operationId, parentGraphRevisionSha256, recordsSha256, revision, v: 1 });
262
+ }
263
+
264
+ // src/ontology.ts
265
+ var OH_ONTOLOGY_VERSION_V1 = "1.0.0";
266
+ var OH_CONTRACT_ID_V1 = "oh.ontology.v1";
267
+ var OH_KNOWLEDGE_LIMITS_V1 = Object.freeze({
268
+ dimensions: 64,
269
+ listValues: 256,
270
+ qualifiers: 128,
271
+ statementBytes: 256 * 1024,
272
+ textBytes: 64 * 1024
273
+ });
274
+
275
+ // src/schema.ts
276
+ var OH_SCHEMA_FORMAT_VERSION_V1 = 1;
277
+
278
+ // src/contract.ts
279
+ var manifestPayload = Object.freeze({
280
+ contractId: OH_CONTRACT_ID_V1,
281
+ graphFormatVersion: OH_GRAPH_FORMAT_VERSION_V1,
282
+ ontologyVersion: OH_ONTOLOGY_VERSION_V1,
283
+ recordKinds: OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1,
284
+ schemaFormatVersion: OH_SCHEMA_FORMAT_VERSION_V1,
285
+ v: 1
286
+ });
287
+ var OH_CONTRACT_MANIFEST_V1 = Object.freeze({
288
+ ...manifestPayload,
289
+ contractSha256: canonicalSha256(manifestPayload)
290
+ });
291
+ class OhRecordCodecRegistry {
292
+ #codecs = new Map;
293
+ #sealed = false;
294
+ register(codec) {
295
+ if (this.#sealed)
296
+ throw new TypeError("The codec registry is sealed.");
297
+ if (this.#codecs.has(codec.kind))
298
+ throw new TypeError(`A codec is already registered for ${codec.kind}.`);
299
+ this.#codecs.set(codec.kind, Object.freeze({ kind: codec.kind, parse: codec.parse }));
300
+ return this;
301
+ }
302
+ parse(kind, value) {
303
+ const codec = this.#codecs.get(kind);
304
+ if (codec !== undefined)
305
+ return codec.parse(value);
306
+ try {
307
+ canonicalJson(value);
308
+ return value;
309
+ } catch {
310
+ return null;
311
+ }
312
+ }
313
+ has(kind) {
314
+ return this.#codecs.has(kind);
315
+ }
316
+ parseRequired(kind, value) {
317
+ const codec = this.#codecs.get(kind);
318
+ if (codec === undefined)
319
+ return null;
320
+ try {
321
+ const parsed = codec.parse(value);
322
+ if (parsed === null)
323
+ return null;
324
+ canonicalJson(parsed);
325
+ return parsed;
326
+ } catch {
327
+ return null;
328
+ }
329
+ }
330
+ seal() {
331
+ this.#sealed = true;
332
+ return this;
333
+ }
334
+ get sealed() {
335
+ return this.#sealed;
336
+ }
337
+ }
338
+ // src/operation.ts
339
+ var OH_OPERATION_MAX_BYTES_V1 = 64 * 1024 * 1024;
340
+ function parsePayload(value) {
341
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
342
+ "actorId",
343
+ "changes",
344
+ "contractId",
345
+ "graphRevisionSha256",
346
+ "instant",
347
+ "operationId",
348
+ "parentOperationSha256",
349
+ "recordsSha256",
350
+ "sequence",
351
+ "spaceId",
352
+ "v"
353
+ ]) || value.v !== 1 || value.contractId !== OH_CONTRACT_ID_V1 || !Array.isArray(value.changes))
354
+ return null;
355
+ const actorId = safeCode(value.actorId);
356
+ const operationId = safeCode(value.operationId);
357
+ const spaceId = safeCode(value.spaceId);
358
+ const graphRevisionSha256 = parseSha256Hex(value.graphRevisionSha256);
359
+ const parentOperationSha256 = value.parentOperationSha256 === null ? null : parseSha256Hex(value.parentOperationSha256);
360
+ const recordsSha256 = parseSha256Hex(value.recordsSha256);
361
+ const instant = parseCanonicalInstantV1(value.instant);
362
+ const sequence = Number.isSafeInteger(value.sequence) && value.sequence > 0 ? value.sequence : null;
363
+ let changes;
364
+ try {
365
+ changes = canonicalKnowledgeGraphChangesV1(value.changes);
366
+ } catch {
367
+ return null;
368
+ }
369
+ if (changes.length === 0 || changes.length > OH_GRAPH_LIMITS_V1.changesPerOperation)
370
+ return null;
371
+ return actorId !== null && operationId !== null && spaceId !== null && graphRevisionSha256 !== null && recordsSha256 !== null && instant !== null && sequence !== null && (value.parentOperationSha256 === null || parentOperationSha256 !== null) && sequence === 1 === (parentOperationSha256 === null) ? {
372
+ actorId,
373
+ changes,
374
+ contractId: OH_CONTRACT_ID_V1,
375
+ graphRevisionSha256,
376
+ instant,
377
+ operationId,
378
+ parentOperationSha256,
379
+ recordsSha256,
380
+ sequence,
381
+ spaceId,
382
+ v: 1
383
+ } : null;
384
+ }
385
+ function createOhOperationV1(input) {
386
+ const payload = parsePayload(input);
387
+ if (payload === null)
388
+ throw new TypeError("Invalid Oh operation payload.");
389
+ const operation = { ...payload, operationSha256: canonicalSha256(payload) };
390
+ if (Buffer.byteLength(canonicalJson(operation), "utf8") > OH_OPERATION_MAX_BYTES_V1) {
391
+ throw new RangeError("Oh operation exceeds its canonical byte limit.");
392
+ }
393
+ return operation;
394
+ }
395
+ function parseOhOperationV1(value) {
396
+ if (!isPlainRecord(value) || !Object.hasOwn(value, "operationSha256"))
397
+ return null;
398
+ const operationSha256 = parseSha256Hex(value.operationSha256);
399
+ const { operationSha256: _digest, ...input } = value;
400
+ const payload = parsePayload(input);
401
+ return operationSha256 !== null && payload !== null && Buffer.byteLength(canonicalJson({ ...payload, operationSha256 }), "utf8") <= OH_OPERATION_MAX_BYTES_V1 && canonicalSha256(payload) === operationSha256 ? { ...payload, operationSha256 } : null;
402
+ }
403
+
404
+ // src/store.ts
405
+ class OhConflictError extends Error {
406
+ constructor(message) {
407
+ super(message);
408
+ this.name = "OhConflictError";
409
+ }
410
+ }
411
+
412
+ class OhIntegrityError extends Error {
413
+ constructor(message) {
414
+ super(message);
415
+ this.name = "OhIntegrityError";
416
+ }
417
+ }
418
+
419
+ class OhDependencyError extends Error {
420
+ constructor(message) {
421
+ super(message);
422
+ this.name = "OhDependencyError";
423
+ }
424
+ }
425
+
426
+ class OhProfileError extends Error {
427
+ constructor(message) {
428
+ super(message);
429
+ this.name = "OhProfileError";
430
+ }
431
+ }
432
+ var OH_CANONICAL_STORE_PROFILE_V1 = createOhStoreProfileV1({
433
+ applicationProfileSha256: null,
434
+ capabilities: {
435
+ changesSince: true,
436
+ dependencyClosureExport: true,
437
+ exactSnapshots: true,
438
+ operationReplication: true,
439
+ semanticBundleCommit: true,
440
+ v: 1,
441
+ wholeSpacePurge: false
442
+ },
443
+ profileId: "oh.store.canonical.v1",
444
+ profileKind: "canonical",
445
+ v: 1
446
+ });
447
+ var OH_WORKING_STORE_PROFILE_V1 = createOhStoreProfileV1({
448
+ applicationProfileSha256: null,
449
+ capabilities: {
450
+ changesSince: true,
451
+ dependencyClosureExport: true,
452
+ exactSnapshots: true,
453
+ operationReplication: false,
454
+ semanticBundleCommit: true,
455
+ v: 1,
456
+ wholeSpacePurge: true
457
+ },
458
+ profileId: "oh.store.working.v1",
459
+ profileKind: "working",
460
+ v: 1
461
+ });
462
+ var OH_DEPENDENCY_CLOSURE_LIMITS_V1 = Object.freeze({
463
+ bytes: 64 * 1024 * 1024,
464
+ records: 8192,
465
+ roots: 1024
466
+ });
467
+
468
+ class OhPurgedSpaceError extends Error {
469
+ receipt;
470
+ constructor(receipt) {
471
+ super(`Oh space ${receipt.spaceId} was purged at ${receipt.purgedAt}.`);
472
+ this.name = "OhPurgedSpaceError";
473
+ this.receipt = receipt;
474
+ }
475
+ }
476
+ var EMPTY_RECORDS_SHA256 = canonicalSha256([]);
477
+ function emptyOhHeadV1() {
478
+ return {
479
+ generation: 0,
480
+ graphRevisionSha256: null,
481
+ operationSha256: null,
482
+ recordsSha256: EMPTY_RECORDS_SHA256,
483
+ sequence: 0,
484
+ v: 1
485
+ };
486
+ }
487
+ function parseOhHeadV1(value) {
488
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
489
+ "generation",
490
+ "graphRevisionSha256",
491
+ "operationSha256",
492
+ "recordsSha256",
493
+ "sequence",
494
+ "v"
495
+ ]) || value.v !== 1)
496
+ return null;
497
+ const graphRevisionSha256 = value.graphRevisionSha256 === null ? null : parseSha256Hex(value.graphRevisionSha256);
498
+ const operationSha256 = value.operationSha256 === null ? null : parseSha256Hex(value.operationSha256);
499
+ const recordsSha256 = parseSha256Hex(value.recordsSha256);
500
+ const generation = Number.isSafeInteger(value.generation) && value.generation >= 0 ? value.generation : null;
501
+ const sequence = Number.isSafeInteger(value.sequence) && value.sequence >= 0 ? value.sequence : null;
502
+ return generation !== null && sequence !== null && generation === sequence && recordsSha256 !== null && (value.graphRevisionSha256 === null || graphRevisionSha256 !== null) && (value.operationSha256 === null || operationSha256 !== null) && sequence === 0 === (operationSha256 === null) && sequence === 0 === (graphRevisionSha256 === null) ? { generation, graphRevisionSha256, operationSha256, recordsSha256, sequence, v: 1 } : null;
503
+ }
504
+ function parseOhHeadRefV1(value) {
505
+ const complete = parseOhHeadV1(value);
506
+ if (complete !== null) {
507
+ return { operationSha256: complete.operationSha256, sequence: complete.sequence };
508
+ }
509
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["operationSha256", "sequence"]))
510
+ return null;
511
+ const operationSha256 = value.operationSha256 === null ? null : parseSha256Hex(value.operationSha256);
512
+ const sequence = Number.isSafeInteger(value.sequence) && value.sequence >= 0 ? value.sequence : null;
513
+ return sequence !== null && (value.operationSha256 === null || operationSha256 !== null) && sequence === 0 === (operationSha256 === null) ? { operationSha256, sequence } : null;
514
+ }
515
+ function parseCapabilities(value) {
516
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
517
+ "changesSince",
518
+ "dependencyClosureExport",
519
+ "exactSnapshots",
520
+ "operationReplication",
521
+ "semanticBundleCommit",
522
+ "v",
523
+ "wholeSpacePurge"
524
+ ]) || value.changesSince !== true || value.dependencyClosureExport !== true || value.exactSnapshots !== true || typeof value.operationReplication !== "boolean" || value.semanticBundleCommit !== true || value.v !== 1 || typeof value.wholeSpacePurge !== "boolean")
525
+ return null;
526
+ return {
527
+ changesSince: true,
528
+ dependencyClosureExport: true,
529
+ exactSnapshots: true,
530
+ operationReplication: value.operationReplication,
531
+ semanticBundleCommit: true,
532
+ v: 1,
533
+ wholeSpacePurge: value.wholeSpacePurge
534
+ };
535
+ }
536
+ function createOhStoreProfileV1(input) {
537
+ if (!isPlainRecord(input) || !hasExactKeys(input, [
538
+ "applicationProfileSha256",
539
+ "capabilities",
540
+ "profileId",
541
+ "profileKind",
542
+ "v"
543
+ ]) || input.v !== 1)
544
+ throw new TypeError("Invalid Oh store profile input.");
545
+ const profileId = safeCode(input.profileId);
546
+ const applicationProfileSha256 = input.applicationProfileSha256 === null ? null : parseSha256Hex(input.applicationProfileSha256);
547
+ const capabilities = parseCapabilities(input.capabilities);
548
+ if (profileId === null || capabilities === null || input.applicationProfileSha256 !== null && applicationProfileSha256 === null || input.profileKind !== "canonical" && input.profileKind !== "working") {
549
+ throw new TypeError("Invalid Oh store profile input.");
550
+ }
551
+ if (input.profileKind === "working" && (capabilities.operationReplication || !capabilities.wholeSpacePurge)) {
552
+ throw new OhProfileError("A working profile must disable operation replication and permit whole-space purge.");
553
+ }
554
+ if (input.profileKind === "canonical" && capabilities.wholeSpacePurge) {
555
+ throw new OhProfileError("A canonical profile cannot permit whole-space purge.");
556
+ }
557
+ const payload = {
558
+ applicationProfileSha256,
559
+ capabilities: Object.freeze(capabilities),
560
+ profileId,
561
+ profileKind: input.profileKind,
562
+ v: 1
563
+ };
564
+ return Object.freeze({ ...payload, profileSha256: canonicalSha256(payload) });
565
+ }
566
+ function parseOhStoreProfileV1(value) {
567
+ if (!isPlainRecord(value) || !Object.hasOwn(value, "profileSha256"))
568
+ return null;
569
+ const digest = parseSha256Hex(value.profileSha256);
570
+ const { profileSha256: _profileSha256, ...input } = value;
571
+ try {
572
+ const created = createOhStoreProfileV1(input);
573
+ return digest !== null && created.profileSha256 === digest ? created : null;
574
+ } catch {
575
+ return null;
576
+ }
577
+ }
578
+ function createOhStoreBindingV1(input) {
579
+ const profile = parseOhStoreProfileV1(input.profile);
580
+ const realmId = safeCode(input.realmId);
581
+ const spaceId = safeCode(input.spaceId);
582
+ if (input.v !== 1 || profile === null || realmId === null || spaceId === null) {
583
+ throw new TypeError("Invalid Oh store binding input.");
584
+ }
585
+ const payload = {
586
+ contractSha256: OH_CONTRACT_MANIFEST_V1.contractSha256,
587
+ profile,
588
+ realmId,
589
+ spaceId,
590
+ v: 1
591
+ };
592
+ return Object.freeze({ ...payload, bindingSha256: canonicalSha256(payload) });
593
+ }
594
+ function parseOhStoreBindingV1(value) {
595
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
596
+ "bindingSha256",
597
+ "contractSha256",
598
+ "profile",
599
+ "realmId",
600
+ "spaceId",
601
+ "v"
602
+ ]) || value.v !== 1 || value.contractSha256 !== OH_CONTRACT_MANIFEST_V1.contractSha256)
603
+ return null;
604
+ const bindingSha256 = parseSha256Hex(value.bindingSha256);
605
+ const profile = parseOhStoreProfileV1(value.profile);
606
+ try {
607
+ if (bindingSha256 === null || profile === null)
608
+ return null;
609
+ const created = createOhStoreBindingV1({
610
+ profile,
611
+ realmId: value.realmId,
612
+ spaceId: value.spaceId,
613
+ v: 1
614
+ });
615
+ return created.bindingSha256 === bindingSha256 ? created : null;
616
+ } catch {
617
+ return null;
618
+ }
619
+ }
620
+ function sortedRecords(records) {
621
+ return [...records].sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0);
622
+ }
623
+ function verifyDependencies(records) {
624
+ for (const record of records.values()) {
625
+ for (const dependency of record.dependencies) {
626
+ if (!records.has(dependency))
627
+ throw new OhDependencyError(`Missing dependency ${dependency} for ${record.key}.`);
628
+ }
629
+ }
630
+ }
631
+ function replayOhOperationsV1(spaceId, values, maximumRecords = OH_GRAPH_LIMITS_V1.recordsPerSnapshot) {
632
+ const parsedSpaceId = safeCode(spaceId);
633
+ if (parsedSpaceId === null || !Number.isSafeInteger(maximumRecords) || maximumRecords < 1 || maximumRecords > OH_GRAPH_LIMITS_V1.recordsPerSnapshot) {
634
+ throw new TypeError("Invalid operation replay input.");
635
+ }
636
+ const records = new Map;
637
+ const operationIds = new Set;
638
+ let head = emptyOhHeadV1();
639
+ for (const value of values) {
640
+ const operation = parseOhOperationV1(value);
641
+ if (operation === null || operation.spaceId !== parsedSpaceId || operation.sequence !== head.sequence + 1 || operation.parentOperationSha256 !== head.operationSha256 || operationIds.has(operation.operationId)) {
642
+ throw new OhIntegrityError("Operation replay chain is broken.");
643
+ }
644
+ operationIds.add(operation.operationId);
645
+ for (const change of operation.changes) {
646
+ if (change.kind === "put")
647
+ records.set(change.record.key, change.record);
648
+ else {
649
+ const prior = records.get(change.key);
650
+ if (prior?.recordSha256 !== change.priorSha256) {
651
+ throw new OhIntegrityError("Replay tombstone does not match its prior record.");
652
+ }
653
+ records.delete(change.key);
654
+ }
655
+ }
656
+ if (records.size > maximumRecords)
657
+ throw new RangeError("Operation replay exceeds its record bound.");
658
+ verifyDependencies(records);
659
+ const refs = sortedRecords(records.values()).map(knowledgeGraphRecordRefV1);
660
+ const recordsSha256 = canonicalSha256(refs);
661
+ const graphRevisionSha256 = graphRevisionSha256V1({
662
+ changes: operation.changes,
663
+ operationId: operation.operationId,
664
+ parentGraphRevisionSha256: head.graphRevisionSha256,
665
+ recordsSha256,
666
+ revision: operation.sequence
667
+ });
668
+ if (recordsSha256 !== operation.recordsSha256 || graphRevisionSha256 !== operation.graphRevisionSha256) {
669
+ throw new OhIntegrityError("Replay does not reproduce an operation head.");
670
+ }
671
+ head = {
672
+ generation: operation.sequence,
673
+ graphRevisionSha256,
674
+ operationSha256: operation.operationSha256,
675
+ recordsSha256,
676
+ sequence: operation.sequence,
677
+ v: 1
678
+ };
679
+ }
680
+ return { head, records: sortedRecords(records.values()), v: 1 };
681
+ }
682
+ function transitionOhSnapshotV1(input) {
683
+ const actorId = safeCode(input.actorId);
684
+ const operationId = safeCode(input.operationId);
685
+ const spaceId = safeCode(input.spaceId);
686
+ const instant = parseCanonicalInstantV1(input.instant);
687
+ const changes = canonicalKnowledgeGraphChangesV1(input.changes);
688
+ if (actorId === null || operationId === null || spaceId === null || instant === null || changes.length === 0 || changes.length > OH_GRAPH_LIMITS_V1.changesPerOperation) {
689
+ throw new TypeError("Invalid graph transition input.");
690
+ }
691
+ const head = parseOhHeadV1(input.snapshot.head);
692
+ if (input.snapshot.v !== 1 || head === null || !Array.isArray(input.snapshot.records)) {
693
+ throw new OhIntegrityError("The transition snapshot is invalid.");
694
+ }
695
+ const records = new Map;
696
+ for (const value of input.snapshot.records) {
697
+ const record = parseKnowledgeGraphRecordV1(value);
698
+ if (record === null || records.has(record.key)) {
699
+ throw new OhIntegrityError("The transition snapshot contains an invalid record.");
700
+ }
701
+ records.set(record.key, record);
702
+ }
703
+ verifyDependencies(records);
704
+ const priorRecordsSha256 = canonicalSha256(sortedRecords(records.values()).map(knowledgeGraphRecordRefV1));
705
+ if (priorRecordsSha256 !== head.recordsSha256) {
706
+ throw new OhIntegrityError("The transition snapshot does not reproduce its head.");
707
+ }
708
+ for (const change of changes) {
709
+ if (change.kind === "put")
710
+ records.set(change.record.key, change.record);
711
+ else {
712
+ const prior = records.get(change.key);
713
+ if (prior === undefined || prior.recordSha256 !== change.priorSha256) {
714
+ throw new OhConflictError(`The prior digest for ${change.key} does not match the snapshot.`);
715
+ }
716
+ records.delete(change.key);
717
+ }
718
+ }
719
+ if (records.size > OH_GRAPH_LIMITS_V1.recordsPerSnapshot) {
720
+ throw new RangeError("Graph transition exceeds its record snapshot limit.");
721
+ }
722
+ verifyDependencies(records);
723
+ const nextRecords = sortedRecords(records.values());
724
+ const recordsSha256 = canonicalSha256(nextRecords.map(knowledgeGraphRecordRefV1));
725
+ const graphRevisionSha256 = graphRevisionSha256V1({
726
+ changes,
727
+ operationId,
728
+ parentGraphRevisionSha256: head.graphRevisionSha256,
729
+ recordsSha256,
730
+ revision: head.sequence + 1
731
+ });
732
+ const operation = createOhOperationV1({
733
+ actorId,
734
+ changes,
735
+ contractId: OH_CONTRACT_MANIFEST_V1.contractId,
736
+ graphRevisionSha256,
737
+ instant,
738
+ operationId,
739
+ parentOperationSha256: head.operationSha256,
740
+ recordsSha256,
741
+ sequence: head.sequence + 1,
742
+ spaceId,
743
+ v: 1
744
+ });
745
+ const nextHead = {
746
+ generation: operation.sequence,
747
+ graphRevisionSha256,
748
+ operationSha256: operation.operationSha256,
749
+ recordsSha256,
750
+ sequence: operation.sequence,
751
+ v: 1
752
+ };
753
+ return { operation, snapshot: { head: nextHead, records: nextRecords, v: 1 } };
754
+ }
755
+ function normalizeRoots(values) {
756
+ if (!Array.isArray(values) || values.length < 1 || values.length > OH_DEPENDENCY_CLOSURE_LIMITS_V1.roots) {
757
+ throw new RangeError(`A dependency closure needs 1 through ${OH_DEPENDENCY_CLOSURE_LIMITS_V1.roots} roots.`);
758
+ }
759
+ const roots = values.map((value) => safeCode(value, 512));
760
+ if (roots.some((value) => value === null))
761
+ throw new TypeError("Invalid dependency closure root.");
762
+ const sorted = [...roots].sort();
763
+ if (sorted.some((value, index) => index > 0 && sorted[index - 1] === value)) {
764
+ throw new TypeError("Dependency closure roots must be unique.");
765
+ }
766
+ return sorted;
767
+ }
768
+ function closureRecords(available, roots, maximumRecords, maximumBytes = OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes - 64 * 1024) {
769
+ const selected = new Map;
770
+ const pending = [...roots];
771
+ let selectedBytes = 0;
772
+ while (pending.length > 0) {
773
+ const key = pending.pop();
774
+ if (selected.has(key))
775
+ continue;
776
+ const record = available.get(key);
777
+ if (record === undefined)
778
+ throw new OhDependencyError(`Dependency closure record ${key} is missing.`);
779
+ selectedBytes += Buffer.byteLength(canonicalJson(record), "utf8") + 1;
780
+ if (selectedBytes > maximumBytes)
781
+ throw new RangeError("Dependency closure exceeds its canonical byte bound.");
782
+ selected.set(key, record);
783
+ if (selected.size > maximumRecords)
784
+ throw new RangeError("Dependency closure exceeds its record bound.");
785
+ pending.push(...record.dependencies);
786
+ }
787
+ return sortedRecords(selected.values());
788
+ }
789
+ function createOhDependencyClosureV1(input) {
790
+ const binding = parseOhStoreBindingV1(input.binding);
791
+ const head = parseOhHeadV1(input.snapshot.head);
792
+ const maximumRecords = input.maximumRecords ?? OH_DEPENDENCY_CLOSURE_LIMITS_V1.records;
793
+ if (binding === null || head === null || !Number.isSafeInteger(maximumRecords) || maximumRecords < 1 || maximumRecords > OH_DEPENDENCY_CLOSURE_LIMITS_V1.records) {
794
+ throw new TypeError("Invalid dependency closure input.");
795
+ }
796
+ const roots = normalizeRoots(input.roots);
797
+ const available = new Map;
798
+ for (const value of input.snapshot.records) {
799
+ const record = parseKnowledgeGraphRecordV1(value);
800
+ if (record === null || available.has(record.key))
801
+ throw new OhIntegrityError("Snapshot contains an invalid record.");
802
+ available.set(record.key, record);
803
+ }
804
+ const recordsSha256 = canonicalSha256(sortedRecords(available.values()).map(knowledgeGraphRecordRefV1));
805
+ if (recordsSha256 !== head.recordsSha256)
806
+ throw new OhIntegrityError("Snapshot records do not reproduce its head.");
807
+ const records = closureRecords(available, roots, maximumRecords);
808
+ const payload = { binding, head, records, roots, v: 1 };
809
+ const closure = { ...payload, closureSha256: canonicalSha256(payload) };
810
+ if (Buffer.byteLength(canonicalJson(closure), "utf8") > OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes) {
811
+ throw new RangeError("Dependency closure exceeds its canonical byte bound.");
812
+ }
813
+ return Object.freeze(closure);
814
+ }
815
+ function parseOhDependencyClosureV1(value) {
816
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
817
+ "binding",
818
+ "closureSha256",
819
+ "head",
820
+ "records",
821
+ "roots",
822
+ "v"
823
+ ]) || value.v !== 1 || !Array.isArray(value.records) || !Array.isArray(value.roots) || value.records.length > OH_DEPENDENCY_CLOSURE_LIMITS_V1.records)
824
+ return null;
825
+ const binding = parseOhStoreBindingV1(value.binding);
826
+ const head = parseOhHeadV1(value.head);
827
+ const closureSha256 = parseSha256Hex(value.closureSha256);
828
+ if (binding === null || head === null || closureSha256 === null)
829
+ return null;
830
+ const records = new Map;
831
+ for (const item of value.records) {
832
+ const record = parseKnowledgeGraphRecordV1(item);
833
+ if (record === null || records.has(record.key))
834
+ return null;
835
+ records.set(record.key, record);
836
+ }
837
+ try {
838
+ const roots = normalizeRoots(value.roots);
839
+ if (canonicalJson(roots) !== canonicalJson(value.roots))
840
+ return null;
841
+ const exact = closureRecords(records, roots, OH_DEPENDENCY_CLOSURE_LIMITS_V1.records);
842
+ if (canonicalJson(exact) !== canonicalJson(value.records))
843
+ return null;
844
+ const payload = { binding, head, records: exact, roots, v: 1 };
845
+ const parsed = { ...payload, closureSha256 };
846
+ return canonicalSha256(payload) === closureSha256 && Buffer.byteLength(canonicalJson(parsed), "utf8") <= OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes ? Object.freeze(parsed) : null;
847
+ } catch {
848
+ return null;
849
+ }
850
+ }
851
+ function verifyOhDependencyClosureV1(value) {
852
+ const closure = parseOhDependencyClosureV1(value);
853
+ return closure === null ? { ok: false, reason: "invalid-closure" } : { closure, ok: true };
854
+ }
855
+ function verifyOhDependencyClosureAgainstV1(value, expected) {
856
+ const binding = parseOhStoreBindingV1(expected.binding);
857
+ const head = parseOhHeadV1(expected.head);
858
+ if (binding === null || head === null)
859
+ return { ok: false, reason: "invalid-expectation" };
860
+ const closure = parseOhDependencyClosureV1(value);
861
+ if (closure === null)
862
+ return { ok: false, reason: "invalid-closure" };
863
+ if (closure.binding.bindingSha256 !== binding.bindingSha256)
864
+ return { ok: false, reason: "binding-mismatch" };
865
+ if (canonicalJson(closure.head) !== canonicalJson(head))
866
+ return { ok: false, reason: "head-mismatch" };
867
+ return { closure, ok: true, verification: "expected-authority-and-head" };
868
+ }
869
+ function createOhSpacePurgeReceiptV1(input) {
870
+ const binding = parseOhStoreBindingV1(input.binding);
871
+ const priorHead = parseOhHeadV1(input.priorHead);
872
+ const purgedAt = parseCanonicalInstantV1(input.purgedAt);
873
+ if (binding === null || priorHead === null || purgedAt === null || binding.profile.profileKind !== "working" || !binding.profile.capabilities.wholeSpacePurge) {
874
+ throw new OhProfileError("Only a bound working realm can produce a purge receipt.");
875
+ }
876
+ const payload = {
877
+ bindingSha256: binding.bindingSha256,
878
+ priorHead,
879
+ purgedAt,
880
+ spaceId: binding.spaceId,
881
+ v: 1
882
+ };
883
+ return Object.freeze({ ...payload, receiptSha256: canonicalSha256(payload) });
884
+ }
885
+ function parseOhSpacePurgeReceiptV1(value) {
886
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
887
+ "bindingSha256",
888
+ "priorHead",
889
+ "purgedAt",
890
+ "receiptSha256",
891
+ "spaceId",
892
+ "v"
893
+ ]) || value.v !== 1)
894
+ return null;
895
+ const bindingSha256 = parseSha256Hex(value.bindingSha256);
896
+ const priorHead = parseOhHeadV1(value.priorHead);
897
+ const purgedAt = parseCanonicalInstantV1(value.purgedAt);
898
+ const receiptSha256 = parseSha256Hex(value.receiptSha256);
899
+ const spaceId = safeCode(value.spaceId);
900
+ if (bindingSha256 === null || priorHead === null || purgedAt === null || receiptSha256 === null || spaceId === null)
901
+ return null;
902
+ const payload = { bindingSha256, priorHead, purgedAt, spaceId, v: 1 };
903
+ return canonicalSha256(payload) === receiptSha256 ? Object.freeze({ ...payload, receiptSha256 }) : null;
904
+ }
905
+
906
+ class OhSemanticBundleIngressV1 {
907
+ #codecs;
908
+ #store;
909
+ constructor(store, codecs) {
910
+ this.#store = store;
911
+ this.#codecs = codecs.seal();
912
+ }
913
+ async commit(value) {
914
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
915
+ "actorId",
916
+ "expectedHead",
917
+ "instant",
918
+ "operationId",
919
+ "puts",
920
+ "tombstones",
921
+ "v"
922
+ ]) || value.v !== 1 || !Array.isArray(value.puts) || !Array.isArray(value.tombstones) || value.puts.length + value.tombstones.length < 1 || value.puts.length + value.tombstones.length > OH_GRAPH_LIMITS_V1.changesPerOperation) {
923
+ throw new TypeError("Invalid semantic bundle.");
924
+ }
925
+ const actorId = safeCode(value.actorId);
926
+ const operationId = safeCode(value.operationId);
927
+ const expected = value.expectedHead;
928
+ const instant = value.instant === null ? undefined : parseCanonicalInstantV1(value.instant);
929
+ if (actorId === null || operationId === null || !isPlainRecord(expected) || !hasExactKeys(expected, ["generation", "operationSha256"]) || !Number.isSafeInteger(expected.generation) || expected.generation < 0 || expected.operationSha256 !== null && parseSha256Hex(expected.operationSha256) === null || expected.generation === 0 !== (expected.operationSha256 === null) || value.instant !== null && instant === null)
930
+ throw new TypeError("Invalid semantic bundle identity.");
931
+ const changes = [];
932
+ for (const item of value.puts) {
933
+ if (!isPlainRecord(item) || !hasExactKeys(item, ["dependencies", "key", "kind", "v", "value"]) || item.v !== 1 || !Array.isArray(item.dependencies) || !OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.some((kind) => kind === item.kind)) {
934
+ throw new TypeError("Invalid semantic bundle put.");
935
+ }
936
+ const parsed = this.#codecs.parseRequired(item.kind, item.value);
937
+ if (parsed === null)
938
+ throw new TypeError(`The ${String(item.kind)} codec rejected a semantic value.`);
939
+ const record = createKnowledgeGraphRecordV1({
940
+ dependencies: item.dependencies,
941
+ key: item.key,
942
+ kind: item.kind,
943
+ v: 1,
944
+ value: parsed
945
+ });
946
+ changes.push({ kind: "put", record, v: 1 });
947
+ }
948
+ for (const item of value.tombstones) {
949
+ if (!isPlainRecord(item) || !hasExactKeys(item, ["key", "priorSha256", "v"]) || item.v !== 1) {
950
+ throw new TypeError("Invalid semantic bundle tombstone.");
951
+ }
952
+ const priorSha256 = parseSha256Hex(item.priorSha256);
953
+ if (typeof item.key !== "string" || priorSha256 === null)
954
+ throw new TypeError("Invalid semantic bundle tombstone.");
955
+ changes.push({ key: item.key, kind: "tombstone", priorSha256, v: 1 });
956
+ }
957
+ const canonical = canonicalKnowledgeGraphChangesV1(changes);
958
+ return await this.#store.commit({
959
+ actorId,
960
+ changes: canonical,
961
+ expectedHead: {
962
+ generation: expected.generation,
963
+ operationSha256: expected.operationSha256
964
+ },
965
+ ...typeof instant === "string" ? { instant } : {},
966
+ operationId
967
+ });
968
+ }
969
+ }
970
+
971
+ // src/memory.ts
972
+ import { createHmac, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
973
+
974
+ // src/projection.ts
975
+ var OH_PROJECTION_FORMAT_VERSION_V1 = 1;
976
+ var OH_PROJECTION_SEMANTICS_V1 = "oh.projection.positive-datalog.v1";
977
+ var OH_PROJECTION_INTERNAL_ENGINE_V1 = "oh.naive.positive.v1";
978
+ var OH_PROJECTION_LIMITS_V1 = Object.freeze({
979
+ arity: 32,
980
+ atomBytes: 16 * 1024,
981
+ derivedTuples: 262144,
982
+ facts: 262144,
983
+ literalsPerRule: 64,
984
+ proofDepth: 128,
985
+ proofNodes: 4096,
986
+ queryLiterals: 64,
987
+ queryMatches: 262144,
988
+ queryResults: 65536,
989
+ relations: 4096,
990
+ resultBytes: 16 * 1024 * 1024,
991
+ rounds: 1024,
992
+ rules: 1024,
993
+ sourcesPerFact: 64,
994
+ totalProofNodes: 65536,
995
+ variables: 256,
996
+ workUnits: 16777216
997
+ });
998
+ var recordFactExtractorPayloadV1 = {
999
+ factPackId: "oh.record-facts",
1000
+ factPackRevision: 1,
1001
+ relations: ["oh.dependency", "oh.record"],
1002
+ semantics: OH_PROJECTION_SEMANTICS_V1,
1003
+ v: 1
1004
+ };
1005
+ var OH_PROJECTION_RECORD_FACT_EXTRACTOR_V1 = Object.freeze({
1006
+ ...recordFactExtractorPayloadV1,
1007
+ extractorSha256: canonicalSha256(recordFactExtractorPayloadV1)
1008
+ });
1009
+ function nonnegativeInteger(value) {
1010
+ return Number.isSafeInteger(value) && value >= 0 ? value : null;
1011
+ }
1012
+ function positiveInteger(value, maximum = Number.MAX_SAFE_INTEGER) {
1013
+ return Number.isSafeInteger(value) && value >= 1 && value <= maximum ? value : null;
1014
+ }
1015
+ function projectionName(value, maximumLength = 128) {
1016
+ return safeCode(value, maximumLength);
1017
+ }
1018
+ function compareCanonical(left, right) {
1019
+ const leftKey = canonicalJson(left);
1020
+ const rightKey = canonicalJson(right);
1021
+ return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
1022
+ }
1023
+ function compareProjectionFacts(left, right) {
1024
+ return compareCanonical([left.relation, left.tuple], [right.relation, right.tuple]);
1025
+ }
1026
+ var INVALID_PROJECTION_ATOM = Symbol("invalid-projection-atom");
1027
+ function atom(value) {
1028
+ if (value !== null && typeof value !== "boolean" && typeof value !== "number" && typeof value !== "string") {
1029
+ return INVALID_PROJECTION_ATOM;
1030
+ }
1031
+ try {
1032
+ const encoded = canonicalJson(value);
1033
+ return utf8ByteLength(encoded) <= OH_PROJECTION_LIMITS_V1.atomBytes ? value : INVALID_PROJECTION_ATOM;
1034
+ } catch {
1035
+ return INVALID_PROJECTION_ATOM;
1036
+ }
1037
+ }
1038
+ function tuple(value) {
1039
+ if (!Array.isArray(value) || value.length < 1 || value.length > OH_PROJECTION_LIMITS_V1.arity)
1040
+ return null;
1041
+ const parsed = value.map(atom);
1042
+ return parsed.some((item) => item === INVALID_PROJECTION_ATOM) ? null : parsed;
1043
+ }
1044
+ function parseRecordRef(value) {
1045
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["dependencies", "key", "kind", "sha256", "v"]) || value.v !== 1 || !Array.isArray(value.dependencies))
1046
+ return null;
1047
+ const key = safeCode(value.key, 512);
1048
+ const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === value.kind);
1049
+ const sha256 = parseSha256Hex(value.sha256);
1050
+ const dependencies = value.dependencies.map((dependency) => safeCode(dependency, 512));
1051
+ if (key === null || kind === undefined || sha256 === null || dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord || dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key))
1052
+ return null;
1053
+ return { dependencies, key, kind, sha256, v: 1 };
1054
+ }
1055
+ function createOhProjectionSnapshotV1(input) {
1056
+ const spaceId = projectionName(input.spaceId);
1057
+ const generation = nonnegativeInteger(input.head.generation);
1058
+ const sequence = nonnegativeInteger(input.head.sequence);
1059
+ const operationSha256 = input.head.operationSha256 === null ? null : parseSha256Hex(input.head.operationSha256);
1060
+ const graphRevisionSha256 = input.head.graphRevisionSha256 === null ? null : parseSha256Hex(input.head.graphRevisionSha256);
1061
+ const declaredRecordsSha256 = parseSha256Hex(input.head.recordsSha256);
1062
+ if (spaceId === null || generation === null || sequence === null || generation !== sequence || input.head.operationSha256 !== null && operationSha256 === null || input.head.graphRevisionSha256 !== null && graphRevisionSha256 === null || sequence === 0 !== (operationSha256 === null) || sequence === 0 !== (graphRevisionSha256 === null) || declaredRecordsSha256 === null || input.records.length > OH_GRAPH_LIMITS_V1.recordsPerSnapshot) {
1063
+ throw new TypeError("Invalid projection snapshot head.");
1064
+ }
1065
+ const records = input.records.map(parseKnowledgeGraphRecordV1);
1066
+ if (records.some((record) => record === null))
1067
+ throw new TypeError("Invalid record in projection snapshot.");
1068
+ const recordRefs = sortUnique(records.map(knowledgeGraphRecordRefV1), (reference) => reference.key);
1069
+ const recordsSha256 = canonicalSha256(recordRefs);
1070
+ if (recordsSha256 !== declaredRecordsSha256) {
1071
+ throw new TypeError("Projection snapshot records do not reproduce the declared head.");
1072
+ }
1073
+ const keys = new Set(recordRefs.map((reference) => reference.key));
1074
+ if (recordRefs.some((reference) => reference.dependencies.some((dependency) => !keys.has(dependency)))) {
1075
+ throw new TypeError("Projection snapshot has a missing record dependency.");
1076
+ }
1077
+ const payload = {
1078
+ contractSha256: OH_CONTRACT_MANIFEST_V1.contractSha256,
1079
+ generation,
1080
+ graphRevisionSha256,
1081
+ operationSha256,
1082
+ recordRefs,
1083
+ recordsSha256,
1084
+ sequence,
1085
+ spaceId,
1086
+ v: 1
1087
+ };
1088
+ return { ...payload, snapshotSha256: canonicalSha256(payload) };
1089
+ }
1090
+ function parseOhProjectionSnapshotV1(value) {
1091
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
1092
+ "contractSha256",
1093
+ "generation",
1094
+ "graphRevisionSha256",
1095
+ "operationSha256",
1096
+ "recordRefs",
1097
+ "recordsSha256",
1098
+ "sequence",
1099
+ "snapshotSha256",
1100
+ "spaceId",
1101
+ "v"
1102
+ ]) || value.v !== 1 || !Array.isArray(value.recordRefs) || value.recordRefs.length > OH_GRAPH_LIMITS_V1.recordsPerSnapshot)
1103
+ return null;
1104
+ const contractSha256 = parseSha256Hex(value.contractSha256);
1105
+ const generation = nonnegativeInteger(value.generation);
1106
+ const sequence = nonnegativeInteger(value.sequence);
1107
+ const graphRevisionSha256 = value.graphRevisionSha256 === null ? null : parseSha256Hex(value.graphRevisionSha256);
1108
+ const operationSha256 = value.operationSha256 === null ? null : parseSha256Hex(value.operationSha256);
1109
+ const recordsSha256 = parseSha256Hex(value.recordsSha256);
1110
+ const snapshotSha256 = parseSha256Hex(value.snapshotSha256);
1111
+ const spaceId = projectionName(value.spaceId);
1112
+ const recordRefs = value.recordRefs.map(parseRecordRef);
1113
+ if (contractSha256 !== OH_CONTRACT_MANIFEST_V1.contractSha256 || generation === null || sequence === null || generation !== sequence || spaceId === null || recordsSha256 === null || snapshotSha256 === null || value.graphRevisionSha256 !== null && graphRevisionSha256 === null || value.operationSha256 !== null && operationSha256 === null || sequence === 0 !== (operationSha256 === null) || sequence === 0 !== (graphRevisionSha256 === null) || recordRefs.some((reference) => reference === null))
1114
+ return null;
1115
+ const refs = recordRefs;
1116
+ if (!orderedUnique(refs, (reference) => reference.key) || canonicalSha256(refs) !== recordsSha256)
1117
+ return null;
1118
+ const keys = new Set(refs.map((reference) => reference.key));
1119
+ if (refs.some((reference) => reference.dependencies.some((dependency) => !keys.has(dependency))))
1120
+ return null;
1121
+ const payload = {
1122
+ contractSha256,
1123
+ generation,
1124
+ graphRevisionSha256,
1125
+ operationSha256,
1126
+ recordRefs: refs,
1127
+ recordsSha256,
1128
+ sequence,
1129
+ spaceId,
1130
+ v: 1
1131
+ };
1132
+ return canonicalSha256(payload) === snapshotSha256 ? { ...payload, snapshotSha256 } : null;
1133
+ }
1134
+ function createOhProjectionFactV1(input) {
1135
+ const relation = projectionName(input.relation);
1136
+ const parsedTuple = tuple(input.tuple);
1137
+ if (relation === null || parsedTuple === null || input.sources.length < 1 || input.sources.length > OH_PROJECTION_LIMITS_V1.sourcesPerFact) {
1138
+ throw new TypeError("Invalid projection fact.");
1139
+ }
1140
+ const sources = input.sources.map((source) => {
1141
+ if (!isPlainRecord(source) || !hasExactKeys(source, ["key", "recordSha256", "v"]) || source.v !== 1) {
1142
+ throw new TypeError("Invalid projection fact source.");
1143
+ }
1144
+ const key = safeCode(source.key, 512);
1145
+ const recordSha256 = parseSha256Hex(source.recordSha256);
1146
+ if (key === null || recordSha256 === null)
1147
+ throw new TypeError("Invalid projection fact source.");
1148
+ return { key, recordSha256, v: 1 };
1149
+ }).sort(compareCanonical);
1150
+ if (!orderedUnique(sources, (source) => source.key)) {
1151
+ throw new TypeError("Projection fact sources must have unique record keys.");
1152
+ }
1153
+ const payload = { relation, sources, tuple: parsedTuple, v: 1 };
1154
+ return { ...payload, factSha256: canonicalSha256(payload) };
1155
+ }
1156
+ function parseOhProjectionFactV1(value) {
1157
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["factSha256", "relation", "sources", "tuple", "v"]) || value.v !== 1 || !Array.isArray(value.sources) || !Array.isArray(value.tuple))
1158
+ return null;
1159
+ const factSha256 = parseSha256Hex(value.factSha256);
1160
+ try {
1161
+ const fact = createOhProjectionFactV1({
1162
+ relation: value.relation,
1163
+ sources: value.sources,
1164
+ tuple: value.tuple
1165
+ });
1166
+ return factSha256 !== null && fact.factSha256 === factSha256 ? fact : null;
1167
+ } catch {
1168
+ return null;
1169
+ }
1170
+ }
1171
+ function mergeProjectionFacts(facts) {
1172
+ const grouped = new Map;
1173
+ for (const fact of facts) {
1174
+ const identity = canonicalJson([fact.relation, fact.tuple]);
1175
+ let group = grouped.get(identity);
1176
+ if (group === undefined) {
1177
+ group = { relation: fact.relation, sources: new Map, tuple: fact.tuple };
1178
+ grouped.set(identity, group);
1179
+ }
1180
+ for (const source of fact.sources) {
1181
+ const existing = group.sources.get(source.key);
1182
+ if (existing !== undefined && existing.recordSha256 !== source.recordSha256) {
1183
+ throw new TypeError("One fact source key is bound to multiple record digests.");
1184
+ }
1185
+ group.sources.set(source.key, source);
1186
+ }
1187
+ }
1188
+ return [...grouped.values()].map((group) => createOhProjectionFactV1({
1189
+ relation: group.relation,
1190
+ sources: [...group.sources.values()],
1191
+ tuple: group.tuple
1192
+ })).sort(compareProjectionFacts);
1193
+ }
1194
+ function createOhProjectionDatasetV1(input) {
1195
+ const snapshot = parseOhProjectionSnapshotV1(input.snapshot);
1196
+ const extractorSha256 = parseSha256Hex(input.extractorSha256);
1197
+ const factPackId = projectionName(input.factPackId);
1198
+ const factPackRevision = positiveInteger(input.factPackRevision);
1199
+ if (snapshot === null || extractorSha256 === null || factPackId === null || factPackRevision === null || input.facts.length > OH_PROJECTION_LIMITS_V1.facts)
1200
+ throw new TypeError("Invalid projection dataset.");
1201
+ const parsedFacts = input.facts.map(parseOhProjectionFactV1);
1202
+ if (parsedFacts.some((fact) => fact === null))
1203
+ throw new TypeError("Invalid fact in projection dataset.");
1204
+ const facts = mergeProjectionFacts(parsedFacts);
1205
+ if (facts.length > OH_PROJECTION_LIMITS_V1.facts)
1206
+ throw new RangeError("Projection dataset has too many facts.");
1207
+ const refs = new Map(snapshot.recordRefs.map((reference) => [reference.key, reference.sha256]));
1208
+ for (const fact of facts) {
1209
+ for (const source of fact.sources) {
1210
+ if (refs.get(source.key) !== source.recordSha256) {
1211
+ throw new TypeError("Projection fact source is not present at the exact input snapshot.");
1212
+ }
1213
+ }
1214
+ }
1215
+ const factPackPayload = {
1216
+ extractorSha256,
1217
+ factPackId,
1218
+ factPackRevision,
1219
+ semantics: OH_PROJECTION_SEMANTICS_V1,
1220
+ v: 1
1221
+ };
1222
+ const factPackSha256 = canonicalSha256(factPackPayload);
1223
+ const factsSha256 = canonicalSha256(facts);
1224
+ const payload = {
1225
+ extractorSha256,
1226
+ factPackId,
1227
+ factPackRevision,
1228
+ factPackSha256,
1229
+ facts,
1230
+ factsSha256,
1231
+ snapshotSha256: snapshot.snapshotSha256,
1232
+ v: 1
1233
+ };
1234
+ return { ...payload, datasetSha256: canonicalSha256(payload) };
1235
+ }
1236
+ function parseOhProjectionDatasetV1(value, snapshot) {
1237
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
1238
+ "datasetSha256",
1239
+ "extractorSha256",
1240
+ "factPackId",
1241
+ "factPackRevision",
1242
+ "factPackSha256",
1243
+ "facts",
1244
+ "factsSha256",
1245
+ "snapshotSha256",
1246
+ "v"
1247
+ ]) || value.v !== 1 || !Array.isArray(value.facts))
1248
+ return null;
1249
+ const datasetSha256 = parseSha256Hex(value.datasetSha256);
1250
+ const declaredFactPackSha256 = parseSha256Hex(value.factPackSha256);
1251
+ const declaredFactsSha256 = parseSha256Hex(value.factsSha256);
1252
+ try {
1253
+ const dataset = createOhProjectionDatasetV1({
1254
+ extractorSha256: value.extractorSha256,
1255
+ factPackId: value.factPackId,
1256
+ factPackRevision: value.factPackRevision,
1257
+ facts: value.facts,
1258
+ snapshot
1259
+ });
1260
+ return datasetSha256 !== null && declaredFactPackSha256 === dataset.factPackSha256 && declaredFactsSha256 === dataset.factsSha256 && value.snapshotSha256 === dataset.snapshotSha256 && dataset.datasetSha256 === datasetSha256 ? dataset : null;
1261
+ } catch {
1262
+ return null;
1263
+ }
1264
+ }
1265
+ function ohProjectionVariableV1(name) {
1266
+ const parsed = projectionName(name);
1267
+ if (parsed === null)
1268
+ throw new TypeError("Invalid projection variable name.");
1269
+ return { kind: "variable", name: parsed, v: 1 };
1270
+ }
1271
+ function ohProjectionConstantV1(value) {
1272
+ const parsed = atom(value);
1273
+ if (parsed === INVALID_PROJECTION_ATOM)
1274
+ throw new TypeError("Invalid projection constant.");
1275
+ return { kind: "constant", v: 1, value: parsed };
1276
+ }
1277
+ function createOhProjectionLiteralV1(input) {
1278
+ const relation = projectionName(input.relation);
1279
+ if (relation === null || input.terms.length < 1 || input.terms.length > OH_PROJECTION_LIMITS_V1.arity) {
1280
+ throw new TypeError("Invalid projection literal.");
1281
+ }
1282
+ const terms = input.terms.map((term) => parseOhProjectionTermV1(term));
1283
+ if (terms.some((term) => term === null))
1284
+ throw new TypeError("Invalid term in projection literal.");
1285
+ return { relation, terms, v: 1 };
1286
+ }
1287
+ function parseOhProjectionTermV1(value) {
1288
+ if (!isPlainRecord(value) || value.v !== 1)
1289
+ return null;
1290
+ if (value.kind === "variable" && hasExactKeys(value, ["kind", "name", "v"])) {
1291
+ const name = projectionName(value.name);
1292
+ return name === null ? null : { kind: "variable", name, v: 1 };
1293
+ }
1294
+ if (value.kind === "constant" && hasExactKeys(value, ["kind", "v", "value"])) {
1295
+ const parsed = atom(value.value);
1296
+ return parsed === INVALID_PROJECTION_ATOM ? null : { kind: "constant", v: 1, value: parsed };
1297
+ }
1298
+ return null;
1299
+ }
1300
+ function parseOhProjectionLiteralV1(value) {
1301
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["relation", "terms", "v"]) || value.v !== 1 || !Array.isArray(value.terms))
1302
+ return null;
1303
+ try {
1304
+ return createOhProjectionLiteralV1({
1305
+ relation: value.relation,
1306
+ terms: value.terms
1307
+ });
1308
+ } catch {
1309
+ return null;
1310
+ }
1311
+ }
1312
+ function literalVariables(literal) {
1313
+ return literal.terms.flatMap((term) => term.kind === "variable" ? [term.name] : []);
1314
+ }
1315
+ function createOhProjectionRuleV1(input) {
1316
+ const ruleId = projectionName(input.ruleId);
1317
+ const head = parseOhProjectionLiteralV1(input.head);
1318
+ if (ruleId === null || head === null || input.body.length < 1 || input.body.length > OH_PROJECTION_LIMITS_V1.literalsPerRule)
1319
+ throw new TypeError("Invalid projection rule.");
1320
+ const body = input.body.map(parseOhProjectionLiteralV1);
1321
+ if (body.some((literal) => literal === null))
1322
+ throw new TypeError("Invalid body literal in projection rule.");
1323
+ const bound = new Set(body.flatMap(literalVariables));
1324
+ if (literalVariables(head).some((variable) => !bound.has(variable)) || bound.size > OH_PROJECTION_LIMITS_V1.variables) {
1325
+ throw new TypeError("Every projection rule head variable must be bound in its body.");
1326
+ }
1327
+ const payload = { body, head, ruleId, v: 1 };
1328
+ return { ...payload, ruleSha256: canonicalSha256(payload) };
1329
+ }
1330
+ function parseOhProjectionRuleV1(value) {
1331
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["body", "head", "ruleId", "ruleSha256", "v"]) || value.v !== 1 || !Array.isArray(value.body))
1332
+ return null;
1333
+ const ruleSha256 = parseSha256Hex(value.ruleSha256);
1334
+ try {
1335
+ const rule = createOhProjectionRuleV1({
1336
+ body: value.body,
1337
+ head: value.head,
1338
+ ruleId: value.ruleId
1339
+ });
1340
+ return ruleSha256 !== null && rule.ruleSha256 === ruleSha256 ? rule : null;
1341
+ } catch {
1342
+ return null;
1343
+ }
1344
+ }
1345
+ function createOhProjectionRulePackV1(input) {
1346
+ const rulePackId = projectionName(input.rulePackId);
1347
+ const rulePackRevision = positiveInteger(input.rulePackRevision);
1348
+ if (rulePackId === null || rulePackRevision === null || input.rules.length < 1 || input.rules.length > OH_PROJECTION_LIMITS_V1.rules)
1349
+ throw new TypeError("Invalid projection rule pack.");
1350
+ const parsedRules = input.rules.map(parseOhProjectionRuleV1);
1351
+ if (parsedRules.some((rule) => rule === null))
1352
+ throw new TypeError("Invalid rule in projection rule pack.");
1353
+ const rules = sortUnique(parsedRules, (rule) => rule.ruleId);
1354
+ const rulesSha256 = canonicalSha256(rules);
1355
+ const payload = {
1356
+ rulePackId,
1357
+ rulePackRevision,
1358
+ rules,
1359
+ rulesSha256,
1360
+ semantics: OH_PROJECTION_SEMANTICS_V1,
1361
+ v: 1
1362
+ };
1363
+ return { ...payload, rulePackSha256: canonicalSha256(payload) };
1364
+ }
1365
+ function parseOhProjectionRulePackV1(value) {
1366
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
1367
+ "rulePackId",
1368
+ "rulePackRevision",
1369
+ "rulePackSha256",
1370
+ "rules",
1371
+ "rulesSha256",
1372
+ "semantics",
1373
+ "v"
1374
+ ]) || value.v !== 1 || value.semantics !== OH_PROJECTION_SEMANTICS_V1 || !Array.isArray(value.rules))
1375
+ return null;
1376
+ const rulePackSha256 = parseSha256Hex(value.rulePackSha256);
1377
+ const rulesSha256 = parseSha256Hex(value.rulesSha256);
1378
+ try {
1379
+ const pack = createOhProjectionRulePackV1({
1380
+ rulePackId: value.rulePackId,
1381
+ rulePackRevision: value.rulePackRevision,
1382
+ rules: value.rules
1383
+ });
1384
+ return rulePackSha256 === pack.rulePackSha256 && rulesSha256 === pack.rulesSha256 ? pack : null;
1385
+ } catch {
1386
+ return null;
1387
+ }
1388
+ }
1389
+ function createOhProjectionQueryV1(input) {
1390
+ const queryId = projectionName(input.queryId);
1391
+ const limit = positiveInteger(input.limit ?? 1000, OH_PROJECTION_LIMITS_V1.queryResults);
1392
+ if (queryId === null || limit === null || input.find.length < 1 || input.find.length > OH_PROJECTION_LIMITS_V1.arity || input.where.length < 1 || input.where.length > OH_PROJECTION_LIMITS_V1.queryLiterals)
1393
+ throw new TypeError("Invalid projection query.");
1394
+ const find = input.find.map((name) => projectionName(name));
1395
+ const where = input.where.map(parseOhProjectionLiteralV1);
1396
+ if (find.some((name) => name === null) || !orderedUnique([...find].sort(), String) || where.some((literal) => literal === null))
1397
+ throw new TypeError("Invalid projection query variables or literals.");
1398
+ const bound = new Set(where.flatMap(literalVariables));
1399
+ if (find.some((name) => !bound.has(name)) || bound.size > OH_PROJECTION_LIMITS_V1.variables) {
1400
+ throw new TypeError("Every projected query variable must be bound in the query body.");
1401
+ }
1402
+ const payload = {
1403
+ find,
1404
+ limit,
1405
+ queryId,
1406
+ where,
1407
+ v: 1
1408
+ };
1409
+ return { ...payload, querySha256: canonicalSha256(payload) };
1410
+ }
1411
+ function parseOhProjectionQueryV1(value) {
1412
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["find", "limit", "queryId", "querySha256", "where", "v"]) || value.v !== 1 || !Array.isArray(value.find) || !Array.isArray(value.where))
1413
+ return null;
1414
+ const querySha256 = parseSha256Hex(value.querySha256);
1415
+ try {
1416
+ const query = createOhProjectionQueryV1({
1417
+ find: value.find,
1418
+ limit: value.limit,
1419
+ queryId: value.queryId,
1420
+ where: value.where
1421
+ });
1422
+ return querySha256 !== null && query.querySha256 === querySha256 ? query : null;
1423
+ } catch {
1424
+ return null;
1425
+ }
1426
+ }
1427
+ function createOhProjectionIdentityV1(input) {
1428
+ const snapshot = parseOhProjectionSnapshotV1(input.snapshot);
1429
+ const dataset = snapshot === null ? null : parseOhProjectionDatasetV1(input.dataset, snapshot);
1430
+ const query = parseOhProjectionQueryV1(input.query);
1431
+ const rulePack = parseOhProjectionRulePackV1(input.rulePack);
1432
+ if (snapshot === null || dataset === null || query === null || rulePack === null) {
1433
+ throw new TypeError("Invalid projection identity input.");
1434
+ }
1435
+ const engine = safeCode(input.engine ?? OH_PROJECTION_INTERNAL_ENGINE_V1, 256);
1436
+ if (engine === null)
1437
+ throw new TypeError("Invalid projection engine identity.");
1438
+ const evaluation = { ...resolveEvaluationOptions(input.options ?? {}), v: 1 };
1439
+ const payload = {
1440
+ contractSha256: OH_CONTRACT_MANIFEST_V1.contractSha256,
1441
+ datasetSha256: dataset.datasetSha256,
1442
+ engineSha256: canonicalSha256({ engine, v: 1 }),
1443
+ evaluationSha256: canonicalSha256(evaluation),
1444
+ querySha256: query.querySha256,
1445
+ rulePackSha256: rulePack.rulePackSha256,
1446
+ semantics: OH_PROJECTION_SEMANTICS_V1,
1447
+ snapshotSha256: snapshot.snapshotSha256,
1448
+ v: 1
1449
+ };
1450
+ return { ...payload, projectionSha256: canonicalSha256(payload) };
1451
+ }
1452
+ function parseOhProjectionIdentityV1(value) {
1453
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
1454
+ "contractSha256",
1455
+ "datasetSha256",
1456
+ "engineSha256",
1457
+ "evaluationSha256",
1458
+ "projectionSha256",
1459
+ "querySha256",
1460
+ "rulePackSha256",
1461
+ "semantics",
1462
+ "snapshotSha256",
1463
+ "v"
1464
+ ]) || value.v !== 1 || value.semantics !== OH_PROJECTION_SEMANTICS_V1)
1465
+ return null;
1466
+ const contractSha256 = parseSha256Hex(value.contractSha256);
1467
+ const datasetSha256 = parseSha256Hex(value.datasetSha256);
1468
+ const engineSha256 = parseSha256Hex(value.engineSha256);
1469
+ const evaluationSha256 = parseSha256Hex(value.evaluationSha256);
1470
+ const projectionSha256 = parseSha256Hex(value.projectionSha256);
1471
+ const querySha256 = parseSha256Hex(value.querySha256);
1472
+ const rulePackSha256 = parseSha256Hex(value.rulePackSha256);
1473
+ const snapshotSha256 = parseSha256Hex(value.snapshotSha256);
1474
+ if (contractSha256 !== OH_CONTRACT_MANIFEST_V1.contractSha256 || datasetSha256 === null || engineSha256 === null || evaluationSha256 === null || projectionSha256 === null || querySha256 === null || rulePackSha256 === null || snapshotSha256 === null)
1475
+ return null;
1476
+ const payload = {
1477
+ contractSha256,
1478
+ datasetSha256,
1479
+ engineSha256,
1480
+ evaluationSha256,
1481
+ querySha256,
1482
+ rulePackSha256,
1483
+ semantics: OH_PROJECTION_SEMANTICS_V1,
1484
+ snapshotSha256,
1485
+ v: 1
1486
+ };
1487
+ return canonicalSha256(payload) === projectionSha256 ? { ...payload, projectionSha256 } : null;
1488
+ }
1489
+ function invalidationForOhProjectionV1(previous, next) {
1490
+ const parsedPrevious = parseOhProjectionIdentityV1(previous);
1491
+ const parsedNext = parseOhProjectionIdentityV1(next);
1492
+ if (parsedPrevious === null || parsedNext === null)
1493
+ throw new TypeError("Invalid projection identity.");
1494
+ if (parsedPrevious.projectionSha256 === parsedNext.projectionSha256)
1495
+ return { kind: "reusable", v: 1 };
1496
+ const reasons = [];
1497
+ if (parsedPrevious.snapshotSha256 !== parsedNext.snapshotSha256)
1498
+ reasons.push("snapshot-changed");
1499
+ if (parsedPrevious.datasetSha256 !== parsedNext.datasetSha256)
1500
+ reasons.push("dataset-changed");
1501
+ if (parsedPrevious.engineSha256 !== parsedNext.engineSha256)
1502
+ reasons.push("engine-changed");
1503
+ if (parsedPrevious.evaluationSha256 !== parsedNext.evaluationSha256)
1504
+ reasons.push("evaluation-changed");
1505
+ if (parsedPrevious.rulePackSha256 !== parsedNext.rulePackSha256)
1506
+ reasons.push("rule-pack-changed");
1507
+ if (parsedPrevious.querySha256 !== parsedNext.querySha256)
1508
+ reasons.push("query-changed");
1509
+ return { kind: "full-rebuild", reasons, v: 1 };
1510
+ }
1511
+ function tupleKey(value) {
1512
+ return canonicalJson(value);
1513
+ }
1514
+ function referenceKey(reference) {
1515
+ return canonicalJson([reference.relation, reference.tuple]);
1516
+ }
1517
+ function relationTuples(relations, relation) {
1518
+ return [...relations.get(relation)?.values() ?? []].sort((left, right) => compareCanonical(left.tuple, right.tuple));
1519
+ }
1520
+ function setArity(arities, relation, arity) {
1521
+ const existing = arities.get(relation);
1522
+ if (existing !== undefined && existing !== arity) {
1523
+ throw new TypeError(`Projection relation ${relation} is used with conflicting arities.`);
1524
+ }
1525
+ arities.set(relation, arity);
1526
+ if (arities.size > OH_PROJECTION_LIMITS_V1.relations)
1527
+ throw new RangeError("Projection uses too many relations.");
1528
+ }
1529
+ function validateProgramArities(dataset, rulePack, query) {
1530
+ const arities = new Map;
1531
+ for (const fact of dataset.facts)
1532
+ setArity(arities, fact.relation, fact.tuple.length);
1533
+ for (const rule of rulePack.rules) {
1534
+ setArity(arities, rule.head.relation, rule.head.terms.length);
1535
+ for (const literal of rule.body)
1536
+ setArity(arities, literal.relation, literal.terms.length);
1537
+ }
1538
+ for (const literal of query.where)
1539
+ setArity(arities, literal.relation, literal.terms.length);
1540
+ }
1541
+ function sameAtom(left, right) {
1542
+ return left === right;
1543
+ }
1544
+ function unifyLiteral(literal, state, binding) {
1545
+ const next = new Map(binding);
1546
+ for (let index = 0;index < literal.terms.length; index += 1) {
1547
+ const term = literal.terms[index];
1548
+ const value = state.tuple[index];
1549
+ if (term.kind === "constant") {
1550
+ if (!sameAtom(term.value, value))
1551
+ return null;
1552
+ continue;
1553
+ }
1554
+ if (next.has(term.name)) {
1555
+ if (!sameAtom(next.get(term.name), value))
1556
+ return null;
1557
+ } else
1558
+ next.set(term.name, value);
1559
+ }
1560
+ return next;
1561
+ }
1562
+ function consumeWorkUnit(budget) {
1563
+ if (budget.units >= budget.maximum)
1564
+ throw new RangeError("Projection exceeds its work-unit bound.");
1565
+ budget.units += 1;
1566
+ }
1567
+ function matchBody(relations, body, maximumMatches, work) {
1568
+ let matches = [{ binding: new Map, premises: [] }];
1569
+ for (const literal of body) {
1570
+ const next = [];
1571
+ const candidates = relationTuples(relations, literal.relation);
1572
+ for (const match of matches) {
1573
+ for (const candidate of candidates) {
1574
+ consumeWorkUnit(work);
1575
+ const binding = unifyLiteral(literal, candidate, match.binding);
1576
+ if (binding === null)
1577
+ continue;
1578
+ next.push({ binding, premises: [...match.premises, {
1579
+ relation: literal.relation,
1580
+ tuple: candidate.tuple
1581
+ }] });
1582
+ if (next.length > maximumMatches)
1583
+ throw new RangeError("Projection join exceeds its match bound.");
1584
+ }
1585
+ }
1586
+ matches = next;
1587
+ if (matches.length === 0)
1588
+ break;
1589
+ }
1590
+ return matches;
1591
+ }
1592
+ function instantiateHead(head, binding) {
1593
+ return head.terms.map((term) => term.kind === "constant" ? term.value : binding.get(term.name));
1594
+ }
1595
+ function canonicalWitness(witness) {
1596
+ if (witness.kind === "fact")
1597
+ return canonicalJson(witness);
1598
+ return canonicalJson({ kind: witness.kind, premises: witness.premises, ruleSha256: witness.rule.ruleSha256 });
1599
+ }
1600
+ function materializeNaive(input) {
1601
+ const relations = new Map;
1602
+ for (const fact of input.dataset.facts) {
1603
+ let relation = relations.get(fact.relation);
1604
+ if (relation === undefined) {
1605
+ relation = new Map;
1606
+ relations.set(fact.relation, relation);
1607
+ }
1608
+ relation.set(tupleKey(fact.tuple), { tuple: fact.tuple, witness: { kind: "fact", sources: fact.sources } });
1609
+ }
1610
+ let derivedFacts = 0;
1611
+ let rounds = 0;
1612
+ while (true) {
1613
+ const candidates = new Map;
1614
+ for (const rule of input.rulePack.rules) {
1615
+ for (const match of matchBody(relations, rule.body, OH_PROJECTION_LIMITS_V1.queryMatches, input.work)) {
1616
+ const derivedTuple = instantiateHead(rule.head, match.binding);
1617
+ const relation = relations.get(rule.head.relation);
1618
+ const key = tupleKey(derivedTuple);
1619
+ if (relation?.has(key) === true)
1620
+ continue;
1621
+ const state = {
1622
+ tuple: derivedTuple,
1623
+ witness: { kind: "derived", premises: match.premises, rule }
1624
+ };
1625
+ const identity = referenceKey({ relation: rule.head.relation, tuple: derivedTuple });
1626
+ const existing = candidates.get(identity);
1627
+ if (existing === undefined || canonicalWitness(state.witness) < canonicalWitness(existing.state.witness)) {
1628
+ candidates.set(identity, { relation: rule.head.relation, state });
1629
+ }
1630
+ }
1631
+ }
1632
+ if (candidates.size === 0)
1633
+ break;
1634
+ if (rounds >= input.maximumRounds)
1635
+ throw new RangeError("Projection exceeds its evaluation round bound.");
1636
+ if (derivedFacts + candidates.size > input.maximumDerivedTuples) {
1637
+ throw new RangeError("Projection exceeds its derived tuple bound.");
1638
+ }
1639
+ const ordered = [...candidates.values()].sort((left, right) => compareCanonical([left.relation, left.state.tuple], [right.relation, right.state.tuple]));
1640
+ for (const candidate of ordered) {
1641
+ let relation = relations.get(candidate.relation);
1642
+ if (relation === undefined) {
1643
+ relation = new Map;
1644
+ relations.set(candidate.relation, relation);
1645
+ }
1646
+ relation.set(tupleKey(candidate.state.tuple), candidate.state);
1647
+ }
1648
+ derivedFacts += candidates.size;
1649
+ rounds += 1;
1650
+ }
1651
+ return { baseFacts: input.dataset.facts.length, derivedFacts, relations, rounds };
1652
+ }
1653
+ function boundedOption(value, fallback, maximum, label) {
1654
+ const parsed = positiveInteger(value ?? fallback, maximum);
1655
+ if (parsed === null)
1656
+ throw new RangeError(`${label} must be an integer from 1 through ${maximum}.`);
1657
+ return parsed;
1658
+ }
1659
+ function resolveEvaluationOptions(options) {
1660
+ const resolved = {
1661
+ maximumDerivedTuples: boundedOption(options.maximumDerivedTuples, OH_PROJECTION_LIMITS_V1.derivedTuples, OH_PROJECTION_LIMITS_V1.derivedTuples, "maximumDerivedTuples"),
1662
+ maximumProofDepth: boundedOption(options.maximumProofDepth, 32, OH_PROJECTION_LIMITS_V1.proofDepth, "maximumProofDepth"),
1663
+ maximumProofNodes: boundedOption(options.maximumProofNodes, 1024, OH_PROJECTION_LIMITS_V1.proofNodes, "maximumProofNodes"),
1664
+ maximumResultBytes: boundedOption(options.maximumResultBytes, OH_PROJECTION_LIMITS_V1.resultBytes, OH_PROJECTION_LIMITS_V1.resultBytes, "maximumResultBytes"),
1665
+ maximumRounds: boundedOption(options.maximumRounds, OH_PROJECTION_LIMITS_V1.rounds, OH_PROJECTION_LIMITS_V1.rounds, "maximumRounds"),
1666
+ maximumTotalProofNodes: boundedOption(options.maximumTotalProofNodes, OH_PROJECTION_LIMITS_V1.totalProofNodes, OH_PROJECTION_LIMITS_V1.totalProofNodes, "maximumTotalProofNodes"),
1667
+ maximumWorkUnits: boundedOption(options.maximumWorkUnits, OH_PROJECTION_LIMITS_V1.workUnits, OH_PROJECTION_LIMITS_V1.workUnits, "maximumWorkUnits")
1668
+ };
1669
+ if (resolved.maximumResultBytes < 64 * 1024) {
1670
+ throw new RangeError("maximumResultBytes must be at least 65536.");
1671
+ }
1672
+ return resolved;
1673
+ }
1674
+ function reserveResultBytes(budget, value) {
1675
+ const bytes = utf8ByteLength(canonicalJson(value));
1676
+ if (budget.bytes + bytes > budget.maximumBytes)
1677
+ return false;
1678
+ budget.bytes += bytes;
1679
+ return true;
1680
+ }
1681
+ function reserveProofNode(budget, options, envelope) {
1682
+ if (budget.nodes >= options.maximumProofNodes || budget.result.nodes >= options.maximumTotalProofNodes || !reserveResultBytes(budget.result, envelope))
1683
+ return false;
1684
+ budget.nodes += 1;
1685
+ budget.result.nodes += 1;
1686
+ return true;
1687
+ }
1688
+ function proofForReference(relations, reference, budget, options, depth, visiting) {
1689
+ if (depth >= options.maximumProofDepth) {
1690
+ const proof = {
1691
+ kind: "truncated",
1692
+ reason: "depth",
1693
+ relation: reference.relation,
1694
+ tuple: reference.tuple,
1695
+ v: 1
1696
+ };
1697
+ return reserveProofNode(budget, options, proof) ? proof : null;
1698
+ }
1699
+ const identity = referenceKey(reference);
1700
+ if (visiting.has(identity)) {
1701
+ const proof = {
1702
+ kind: "truncated",
1703
+ reason: "cycle",
1704
+ relation: reference.relation,
1705
+ tuple: reference.tuple,
1706
+ v: 1
1707
+ };
1708
+ return reserveProofNode(budget, options, proof) ? proof : null;
1709
+ }
1710
+ const state = relations.get(reference.relation)?.get(tupleKey(reference.tuple));
1711
+ if (state === undefined)
1712
+ throw new Error("Projection proof references a tuple outside the materialized result.");
1713
+ if (state.witness.kind === "fact") {
1714
+ const proof = {
1715
+ kind: "fact",
1716
+ relation: reference.relation,
1717
+ sources: state.witness.sources,
1718
+ tuple: reference.tuple,
1719
+ v: 1
1720
+ };
1721
+ return reserveProofNode(budget, options, proof) ? proof : null;
1722
+ }
1723
+ const envelope = {
1724
+ kind: "derived",
1725
+ premises: [],
1726
+ premisesTruncated: false,
1727
+ relation: reference.relation,
1728
+ ruleId: state.witness.rule.ruleId,
1729
+ ruleSha256: state.witness.rule.ruleSha256,
1730
+ tuple: reference.tuple,
1731
+ v: 1
1732
+ };
1733
+ if (!reserveProofNode(budget, options, envelope))
1734
+ return null;
1735
+ visiting.add(identity);
1736
+ try {
1737
+ const premises = [];
1738
+ let premisesTruncated = false;
1739
+ for (const premise of state.witness.premises) {
1740
+ const proof = proofForReference(relations, premise, budget, options, depth + 1, visiting);
1741
+ if (proof === null) {
1742
+ premisesTruncated = true;
1743
+ break;
1744
+ }
1745
+ premises.push(proof);
1746
+ }
1747
+ return {
1748
+ kind: "derived",
1749
+ premises,
1750
+ premisesTruncated,
1751
+ relation: reference.relation,
1752
+ ruleId: state.witness.rule.ruleId,
1753
+ ruleSha256: state.witness.rule.ruleSha256,
1754
+ tuple: reference.tuple,
1755
+ v: 1
1756
+ };
1757
+ } finally {
1758
+ visiting.delete(identity);
1759
+ }
1760
+ }
1761
+ function proofIsTruncated(proof) {
1762
+ return proof.kind === "truncated" || proof.kind === "derived" && (proof.premisesTruncated || proof.premises.some(proofIsTruncated));
1763
+ }
1764
+ function reserveProjectionParseBytes(budget, value) {
1765
+ const bytes = utf8ByteLength(canonicalJson(value));
1766
+ if (budget.bytes + bytes > budget.maximumBytes)
1767
+ return false;
1768
+ budget.bytes += bytes;
1769
+ return true;
1770
+ }
1771
+ function parseProjectionFactSource(value) {
1772
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["key", "recordSha256", "v"]) || value.v !== 1) {
1773
+ return null;
1774
+ }
1775
+ const key = safeCode(value.key, 512);
1776
+ const recordSha256 = parseSha256Hex(value.recordSha256);
1777
+ return key === null || recordSha256 === null ? null : { key, recordSha256, v: 1 };
1778
+ }
1779
+ function parseProjectionProofWithBudget(value, budget, depth) {
1780
+ if (depth > budget.maximumDepth || budget.nodes >= budget.maximumNodes || !isPlainRecord(value) || value.v !== 1)
1781
+ return null;
1782
+ const relation = projectionName(value.relation);
1783
+ const parsedTuple = tuple(value.tuple);
1784
+ if (relation === null || parsedTuple === null)
1785
+ return null;
1786
+ if (value.kind === "fact") {
1787
+ if (!hasExactKeys(value, ["kind", "relation", "sources", "tuple", "v"]) || !Array.isArray(value.sources) || value.sources.length < 1 || value.sources.length > OH_PROJECTION_LIMITS_V1.sourcesPerFact)
1788
+ return null;
1789
+ const sources = value.sources.map(parseProjectionFactSource);
1790
+ if (sources.some((source) => source === null))
1791
+ return null;
1792
+ const parsedSources = sources;
1793
+ if (!orderedUnique(parsedSources, (source) => source.key))
1794
+ return null;
1795
+ const proof = {
1796
+ kind: "fact",
1797
+ relation,
1798
+ sources: parsedSources,
1799
+ tuple: parsedTuple,
1800
+ v: 1
1801
+ };
1802
+ if (!reserveProjectionParseBytes(budget, proof))
1803
+ return null;
1804
+ budget.nodes += 1;
1805
+ return proof;
1806
+ }
1807
+ if (value.kind === "truncated") {
1808
+ if (!hasExactKeys(value, ["kind", "reason", "relation", "tuple", "v"]) || value.reason !== "cycle" && value.reason !== "depth" && value.reason !== "nodes")
1809
+ return null;
1810
+ const reason = value.reason;
1811
+ const proof = {
1812
+ kind: "truncated",
1813
+ reason,
1814
+ relation,
1815
+ tuple: parsedTuple,
1816
+ v: 1
1817
+ };
1818
+ if (!reserveProjectionParseBytes(budget, proof))
1819
+ return null;
1820
+ budget.nodes += 1;
1821
+ return proof;
1822
+ }
1823
+ if (value.kind !== "derived" || !hasExactKeys(value, [
1824
+ "kind",
1825
+ "premises",
1826
+ "premisesTruncated",
1827
+ "relation",
1828
+ "ruleId",
1829
+ "ruleSha256",
1830
+ "tuple",
1831
+ "v"
1832
+ ]) || !Array.isArray(value.premises) || value.premises.length > OH_PROJECTION_LIMITS_V1.literalsPerRule || typeof value.premisesTruncated !== "boolean")
1833
+ return null;
1834
+ const ruleId = projectionName(value.ruleId);
1835
+ const ruleSha256 = parseSha256Hex(value.ruleSha256);
1836
+ if (ruleId === null || ruleSha256 === null || !value.premisesTruncated && value.premises.length === 0 || value.premisesTruncated && value.premises.length === OH_PROJECTION_LIMITS_V1.literalsPerRule) {
1837
+ return null;
1838
+ }
1839
+ const skeleton = {
1840
+ kind: "derived",
1841
+ premises: [],
1842
+ premisesTruncated: value.premisesTruncated,
1843
+ relation,
1844
+ ruleId,
1845
+ ruleSha256,
1846
+ tuple: parsedTuple,
1847
+ v: 1
1848
+ };
1849
+ if (!reserveProjectionParseBytes(budget, skeleton))
1850
+ return null;
1851
+ budget.nodes += 1;
1852
+ const premises = [];
1853
+ for (const premise of value.premises) {
1854
+ const parsed = parseProjectionProofWithBudget(premise, budget, depth + 1);
1855
+ if (parsed === null)
1856
+ return null;
1857
+ premises.push(parsed);
1858
+ }
1859
+ return { ...skeleton, premises };
1860
+ }
1861
+ function parseOhProjectionProofV1(value) {
1862
+ try {
1863
+ const budget = {
1864
+ bytes: 0,
1865
+ maximumBytes: OH_PROJECTION_LIMITS_V1.resultBytes,
1866
+ maximumDepth: OH_PROJECTION_LIMITS_V1.proofDepth,
1867
+ maximumNodes: OH_PROJECTION_LIMITS_V1.proofNodes,
1868
+ nodes: 0
1869
+ };
1870
+ const proof = parseProjectionProofWithBudget(value, budget, 0);
1871
+ return proof !== null && utf8ByteLength(canonicalJson(proof)) <= budget.maximumBytes ? proof : null;
1872
+ } catch {
1873
+ return null;
1874
+ }
1875
+ }
1876
+ function parseProjectionEvaluation(value) {
1877
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
1878
+ "maximumDerivedTuples",
1879
+ "maximumProofDepth",
1880
+ "maximumProofNodes",
1881
+ "maximumResultBytes",
1882
+ "maximumRounds",
1883
+ "maximumTotalProofNodes",
1884
+ "maximumWorkUnits",
1885
+ "v"
1886
+ ]) || value.v !== 1)
1887
+ return null;
1888
+ const maximumDerivedTuples = positiveInteger(value.maximumDerivedTuples, OH_PROJECTION_LIMITS_V1.derivedTuples);
1889
+ const maximumProofDepth = positiveInteger(value.maximumProofDepth, OH_PROJECTION_LIMITS_V1.proofDepth);
1890
+ const maximumProofNodes = positiveInteger(value.maximumProofNodes, OH_PROJECTION_LIMITS_V1.proofNodes);
1891
+ const maximumResultBytes = positiveInteger(value.maximumResultBytes, OH_PROJECTION_LIMITS_V1.resultBytes);
1892
+ const maximumRounds = positiveInteger(value.maximumRounds, OH_PROJECTION_LIMITS_V1.rounds);
1893
+ const maximumTotalProofNodes = positiveInteger(value.maximumTotalProofNodes, OH_PROJECTION_LIMITS_V1.totalProofNodes);
1894
+ const maximumWorkUnits = positiveInteger(value.maximumWorkUnits, OH_PROJECTION_LIMITS_V1.workUnits);
1895
+ if (maximumDerivedTuples === null || maximumProofDepth === null || maximumProofNodes === null || maximumResultBytes === null || maximumResultBytes < 64 * 1024 || maximumRounds === null || maximumTotalProofNodes === null || maximumWorkUnits === null)
1896
+ return null;
1897
+ return {
1898
+ maximumDerivedTuples,
1899
+ maximumProofDepth,
1900
+ maximumProofNodes,
1901
+ maximumResultBytes,
1902
+ maximumRounds,
1903
+ maximumTotalProofNodes,
1904
+ maximumWorkUnits,
1905
+ v: 1
1906
+ };
1907
+ }
1908
+ function parseProjectionResultRow(value, evaluation, resultBudget) {
1909
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["proofs", "proofsTruncated", "supportCount", "values", "v"]) || value.v !== 1 || !Array.isArray(value.proofs) || value.proofs.length > OH_PROJECTION_LIMITS_V1.queryLiterals || typeof value.proofsTruncated !== "boolean")
1910
+ return null;
1911
+ const values = tuple(value.values);
1912
+ const supportCount = positiveInteger(value.supportCount, OH_PROJECTION_LIMITS_V1.queryMatches);
1913
+ if (values === null || supportCount === null || !value.proofsTruncated && value.proofs.length === 0)
1914
+ return null;
1915
+ if (!reserveProjectionParseBytes(resultBudget, {
1916
+ proofs: [],
1917
+ proofsTruncated: value.proofsTruncated,
1918
+ supportCount,
1919
+ values,
1920
+ v: 1
1921
+ }))
1922
+ return null;
1923
+ const before = resultBudget.nodes;
1924
+ resultBudget.maximumNodes = Math.min(resultBudget.maximumNodes, before + evaluation.maximumProofNodes);
1925
+ const proofs = [];
1926
+ for (const proof of value.proofs) {
1927
+ const parsed = parseProjectionProofWithBudget(proof, resultBudget, 0);
1928
+ if (parsed === null)
1929
+ return null;
1930
+ proofs.push(parsed);
1931
+ }
1932
+ resultBudget.maximumNodes = evaluation.maximumTotalProofNodes;
1933
+ const containsTruncation = proofs.some(proofIsTruncated);
1934
+ if (!value.proofsTruncated && containsTruncation || value.proofsTruncated && proofs.length === OH_PROJECTION_LIMITS_V1.queryLiterals && !containsTruncation)
1935
+ return null;
1936
+ return {
1937
+ nodes: resultBudget.nodes - before,
1938
+ row: { proofs, proofsTruncated: value.proofsTruncated, supportCount, values, v: 1 }
1939
+ };
1940
+ }
1941
+ function parseOhProjectionResultV1(value, expectedProjectionSha256) {
1942
+ try {
1943
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
1944
+ "authority",
1945
+ "cache",
1946
+ "engine",
1947
+ "evaluation",
1948
+ "identity",
1949
+ "resultSha256",
1950
+ "rows",
1951
+ "stats",
1952
+ "v"
1953
+ ]) || value.v !== 1 || value.authority !== "derived" || !isPlainRecord(value.cache) || !hasExactKeys(value.cache, ["strategy", "v"]) || value.cache.strategy !== "full-rebuild" || value.cache.v !== 1 || !Array.isArray(value.rows) || value.rows.length > OH_PROJECTION_LIMITS_V1.queryResults || !isPlainRecord(value.stats) || !hasExactKeys(value.stats, [
1954
+ "baseFacts",
1955
+ "derivedFacts",
1956
+ "proofNodes",
1957
+ "proofsTruncated",
1958
+ "queryMatches",
1959
+ "relations",
1960
+ "rounds",
1961
+ "truncated",
1962
+ "truncationReasons",
1963
+ "v",
1964
+ "workUnits"
1965
+ ]) || value.stats.v !== 1 || !Array.isArray(value.stats.truncationReasons) || typeof value.stats.proofsTruncated !== "boolean" || typeof value.stats.truncated !== "boolean")
1966
+ return null;
1967
+ const engine = safeCode(value.engine, 256);
1968
+ const evaluation = parseProjectionEvaluation(value.evaluation);
1969
+ const identity = parseOhProjectionIdentityV1(value.identity);
1970
+ const resultSha256 = parseSha256Hex(value.resultSha256);
1971
+ const expected = expectedProjectionSha256 === undefined ? undefined : parseSha256Hex(expectedProjectionSha256);
1972
+ if (engine === null || evaluation === null || identity === null || resultSha256 === null || expectedProjectionSha256 !== undefined && expected === null || expected !== undefined && identity.projectionSha256 !== expected || identity.engineSha256 !== canonicalSha256({ engine, v: 1 }) || identity.evaluationSha256 !== canonicalSha256(evaluation))
1973
+ return null;
1974
+ const baseFacts = nonnegativeInteger(value.stats.baseFacts);
1975
+ const derivedFacts = nonnegativeInteger(value.stats.derivedFacts);
1976
+ const proofNodes = nonnegativeInteger(value.stats.proofNodes);
1977
+ const queryMatches = nonnegativeInteger(value.stats.queryMatches);
1978
+ const relations = nonnegativeInteger(value.stats.relations);
1979
+ const rounds = nonnegativeInteger(value.stats.rounds);
1980
+ const workUnits = nonnegativeInteger(value.stats.workUnits);
1981
+ if (baseFacts === null || baseFacts > OH_PROJECTION_LIMITS_V1.facts || derivedFacts === null || derivedFacts > evaluation.maximumDerivedTuples || proofNodes === null || proofNodes > evaluation.maximumTotalProofNodes || queryMatches === null || queryMatches > OH_PROJECTION_LIMITS_V1.queryMatches || relations === null || relations > OH_PROJECTION_LIMITS_V1.relations || rounds === null || rounds > evaluation.maximumRounds || workUnits === null || workUnits > evaluation.maximumWorkUnits || relations > baseFacts + derivedFacts || rounds > derivedFacts || rounds === 0 !== (derivedFacts === 0) || queryMatches > workUnits)
1982
+ return null;
1983
+ const truncationReasons = value.stats.truncationReasons;
1984
+ if (truncationReasons.length > 2 || !orderedUnique(truncationReasons, (reason) => reason === "query-limit" ? "0" : reason === "result-bytes" ? "1" : "x") || truncationReasons.some((reason) => reason !== "query-limit" && reason !== "result-bytes") || value.stats.truncated !== truncationReasons.length > 0)
1985
+ return null;
1986
+ const budget = {
1987
+ bytes: 0,
1988
+ maximumBytes: evaluation.maximumResultBytes,
1989
+ maximumDepth: evaluation.maximumProofDepth,
1990
+ maximumNodes: evaluation.maximumTotalProofNodes,
1991
+ nodes: 0
1992
+ };
1993
+ const rows = [];
1994
+ let supportCount = 0;
1995
+ for (const row of value.rows) {
1996
+ const parsed = parseProjectionResultRow(row, evaluation, budget);
1997
+ if (parsed === null)
1998
+ return null;
1999
+ rows.push(parsed.row);
2000
+ supportCount += parsed.row.supportCount;
2001
+ if (supportCount > queryMatches)
2002
+ return null;
2003
+ }
2004
+ if (!orderedUnique(rows, (row) => canonicalJson(row.values)) || budget.nodes !== proofNodes || value.stats.proofsTruncated !== rows.some((row) => row.proofsTruncated) || (value.stats.truncated ? supportCount >= queryMatches : supportCount !== queryMatches))
2005
+ return null;
2006
+ const reasons = truncationReasons;
2007
+ const payload = {
2008
+ authority: "derived",
2009
+ cache: { strategy: "full-rebuild", v: 1 },
2010
+ engine,
2011
+ evaluation,
2012
+ identity,
2013
+ rows,
2014
+ stats: {
2015
+ baseFacts,
2016
+ derivedFacts,
2017
+ proofNodes,
2018
+ proofsTruncated: value.stats.proofsTruncated,
2019
+ queryMatches,
2020
+ relations,
2021
+ rounds,
2022
+ truncated: value.stats.truncated,
2023
+ truncationReasons: reasons,
2024
+ v: 1,
2025
+ workUnits
2026
+ },
2027
+ v: 1
2028
+ };
2029
+ const serialized = canonicalJson(payload);
2030
+ return utf8ByteLength(serialized) <= evaluation.maximumResultBytes && sha256Hex(serialized) === resultSha256 ? { ...payload, resultSha256 } : null;
2031
+ } catch {
2032
+ return null;
2033
+ }
2034
+ }
2035
+ function buildProjectionResult(input) {
2036
+ const matches = matchBody(input.materialized.relations, input.query.where, OH_PROJECTION_LIMITS_V1.queryMatches, input.work);
2037
+ const byValues = new Map;
2038
+ for (const match of matches) {
2039
+ const values = input.query.find.map((name) => match.binding.get(name));
2040
+ const key = tupleKey(values);
2041
+ const existing = byValues.get(key);
2042
+ if (existing === undefined)
2043
+ byValues.set(key, { match, supportCount: 1 });
2044
+ else
2045
+ byValues.set(key, { match: compareCanonical(match.premises, existing.match.premises) < 0 ? match : existing.match, supportCount: existing.supportCount + 1 });
2046
+ }
2047
+ const ordered = [...byValues.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
2048
+ const resultBudget = {
2049
+ bytes: 0,
2050
+ maximumBytes: input.options.maximumResultBytes - 64 * 1024,
2051
+ nodes: 0
2052
+ };
2053
+ const rows = [];
2054
+ let resultBytesTruncated = false;
2055
+ for (const [key, support] of ordered.slice(0, input.query.limit)) {
2056
+ const values = JSON.parse(key);
2057
+ if (!reserveResultBytes(resultBudget, {
2058
+ proofs: [],
2059
+ proofsTruncated: false,
2060
+ supportCount: support.supportCount,
2061
+ values,
2062
+ v: 1
2063
+ })) {
2064
+ resultBytesTruncated = true;
2065
+ break;
2066
+ }
2067
+ const budget = { nodes: 0, result: resultBudget };
2068
+ const proofs = [];
2069
+ for (const premise of support.match.premises) {
2070
+ const proof = proofForReference(input.materialized.relations, premise, budget, input.options, 0, new Set);
2071
+ if (proof === null)
2072
+ break;
2073
+ proofs.push(proof);
2074
+ }
2075
+ const proofsTruncated = proofs.length !== support.match.premises.length || proofs.some(proofIsTruncated);
2076
+ rows.push({ proofs, proofsTruncated, supportCount: support.supportCount, values, v: 1 });
2077
+ }
2078
+ const queryLimitTruncated = ordered.length > input.query.limit;
2079
+ const truncationReasons = [
2080
+ ...queryLimitTruncated ? ["query-limit"] : [],
2081
+ ...resultBytesTruncated ? ["result-bytes"] : []
2082
+ ];
2083
+ const truncated = truncationReasons.length > 0;
2084
+ const identity = createOhProjectionIdentityV1({
2085
+ dataset: input.dataset,
2086
+ query: input.query,
2087
+ engine: input.engine,
2088
+ options: input.options,
2089
+ rulePack: input.rulePack,
2090
+ snapshot: input.snapshot
2091
+ });
2092
+ const payload = {
2093
+ authority: "derived",
2094
+ cache: { strategy: "full-rebuild", v: 1 },
2095
+ engine: input.engine,
2096
+ evaluation: { ...input.options, v: 1 },
2097
+ identity,
2098
+ rows,
2099
+ stats: {
2100
+ baseFacts: input.materialized.baseFacts,
2101
+ derivedFacts: input.materialized.derivedFacts,
2102
+ proofNodes: resultBudget.nodes,
2103
+ proofsTruncated: rows.some((row) => row.proofsTruncated),
2104
+ queryMatches: matches.length,
2105
+ relations: input.materialized.relations.size,
2106
+ rounds: input.materialized.rounds,
2107
+ truncated,
2108
+ truncationReasons,
2109
+ v: 1,
2110
+ workUnits: input.work.units
2111
+ },
2112
+ v: 1
2113
+ };
2114
+ const serialized = canonicalJson(payload);
2115
+ if (utf8ByteLength(serialized) > input.options.maximumResultBytes) {
2116
+ throw new RangeError("Projection result exceeds its canonical byte bound.");
2117
+ }
2118
+ return { ...payload, resultSha256: sha256Hex(serialized) };
2119
+ }
2120
+ function evaluateOhProjectionV1(input) {
2121
+ const snapshot = parseOhProjectionSnapshotV1(input.snapshot);
2122
+ const dataset = snapshot === null ? null : parseOhProjectionDatasetV1(input.dataset, snapshot);
2123
+ const rulePack = parseOhProjectionRulePackV1(input.rulePack);
2124
+ const query = parseOhProjectionQueryV1(input.query);
2125
+ if (snapshot === null || dataset === null || rulePack === null || query === null) {
2126
+ throw new TypeError("Invalid projection snapshot, dataset, rule pack, or query.");
2127
+ }
2128
+ const options = resolveEvaluationOptions(input.options ?? {});
2129
+ validateProgramArities(dataset, rulePack, query);
2130
+ const work = { maximum: options.maximumWorkUnits, units: 0 };
2131
+ const materialized = materializeNaive({
2132
+ dataset,
2133
+ maximumDerivedTuples: options.maximumDerivedTuples,
2134
+ maximumRounds: options.maximumRounds,
2135
+ rulePack,
2136
+ work
2137
+ });
2138
+ return buildProjectionResult({
2139
+ dataset,
2140
+ engine: OH_PROJECTION_INTERNAL_ENGINE_V1,
2141
+ materialized,
2142
+ options,
2143
+ query,
2144
+ rulePack,
2145
+ snapshot,
2146
+ work
2147
+ });
2148
+ }
2149
+ function evaluateOhProjectionWithMaterializerV1(input) {
2150
+ const snapshot = parseOhProjectionSnapshotV1(input.snapshot);
2151
+ const dataset = snapshot === null ? null : parseOhProjectionDatasetV1(input.dataset, snapshot);
2152
+ const rulePack = parseOhProjectionRulePackV1(input.rulePack);
2153
+ const query = parseOhProjectionQueryV1(input.query);
2154
+ const engine = safeCode(input.engine, 256);
2155
+ if (snapshot === null || dataset === null || rulePack === null || query === null || engine === null) {
2156
+ throw new TypeError("Invalid projection adapter input.");
2157
+ }
2158
+ const options = resolveEvaluationOptions(input.options ?? {});
2159
+ validateProgramArities(dataset, rulePack, query);
2160
+ const work = { maximum: options.maximumWorkUnits, units: 0 };
2161
+ const witnessMaterialization = materializeNaive({
2162
+ dataset,
2163
+ maximumDerivedTuples: options.maximumDerivedTuples,
2164
+ maximumRounds: options.maximumRounds,
2165
+ rulePack,
2166
+ work
2167
+ });
2168
+ const external = input.materialize({
2169
+ dataset,
2170
+ maximumDerivedTuples: options.maximumDerivedTuples,
2171
+ maximumRounds: options.maximumRounds,
2172
+ query,
2173
+ rulePack
2174
+ });
2175
+ const externalCanonical = new Map;
2176
+ for (const [relationName, tuples] of external.relationFacts) {
2177
+ const relation = projectionName(relationName);
2178
+ if (relation === null || tuples.length > OH_PROJECTION_LIMITS_V1.facts + options.maximumDerivedTuples) {
2179
+ throw new TypeError("Projection adapter returned an invalid relation.");
2180
+ }
2181
+ const parsed = tuples.map(tuple);
2182
+ if (parsed.some((value) => value === null))
2183
+ throw new TypeError("Projection adapter returned an invalid tuple.");
2184
+ const keys = [];
2185
+ for (const value of parsed) {
2186
+ if (value === null)
2187
+ throw new TypeError("Projection adapter returned an invalid tuple.");
2188
+ keys.push(tupleKey(value));
2189
+ }
2190
+ externalCanonical.set(relation, [...new Set(keys)].sort());
2191
+ }
2192
+ const expectedCanonical = new Map([...witnessMaterialization.relations.entries()].map(([relation, states]) => [relation, [...states.values()].map((state) => tupleKey(state.tuple)).sort()]));
2193
+ const relationNames = [...new Set([...externalCanonical.keys(), ...expectedCanonical.keys()])].sort();
2194
+ for (const relation of relationNames) {
2195
+ if (canonicalJson(externalCanonical.get(relation) ?? []) !== canonicalJson(expectedCanonical.get(relation) ?? [])) {
2196
+ throw new Error(`Projection adapter disagrees with Oh semantics for relation ${relation}.`);
2197
+ }
2198
+ }
2199
+ return buildProjectionResult({
2200
+ dataset,
2201
+ engine,
2202
+ materialized: witnessMaterialization,
2203
+ options,
2204
+ query,
2205
+ rulePack,
2206
+ snapshot,
2207
+ work
2208
+ });
2209
+ }
2210
+ function createOhProjectionRecordFactsV1(records, options = {}) {
2211
+ if (records.length > OH_GRAPH_LIMITS_V1.recordsPerSnapshot)
2212
+ throw new RangeError("Too many records for projection facts.");
2213
+ const parsedRecords = [...records].sort((left, right) => left.key < right.key ? -1 : left.key > right.key ? 1 : 0).map((candidate) => {
2214
+ const record = parseKnowledgeGraphRecordV1(candidate);
2215
+ if (record === null)
2216
+ throw new TypeError("Invalid graph record for projection facts.");
2217
+ return record;
2218
+ });
2219
+ let projectedFactCount = 0;
2220
+ for (const record of parsedRecords) {
2221
+ if (options.includeRecords !== false)
2222
+ projectedFactCount += 1;
2223
+ if (options.includeDependencies !== false)
2224
+ projectedFactCount += record.dependencies.length;
2225
+ if (projectedFactCount > OH_PROJECTION_LIMITS_V1.facts) {
2226
+ throw new RangeError("Structural projection exceeds its fact bound.");
2227
+ }
2228
+ }
2229
+ const facts = [];
2230
+ for (const record of parsedRecords) {
2231
+ const source = [{ key: record.key, recordSha256: record.recordSha256, v: 1 }];
2232
+ if (options.includeRecords !== false) {
2233
+ facts.push(createOhProjectionFactV1({
2234
+ relation: "oh.record",
2235
+ sources: source,
2236
+ tuple: [record.key, record.kind, record.recordSha256]
2237
+ }));
2238
+ }
2239
+ if (options.includeDependencies !== false) {
2240
+ for (const dependency of record.dependencies) {
2241
+ facts.push(createOhProjectionFactV1({
2242
+ relation: "oh.dependency",
2243
+ sources: source,
2244
+ tuple: [record.key, dependency]
2245
+ }));
2246
+ }
2247
+ }
2248
+ }
2249
+ return facts.sort(compareProjectionFacts);
2250
+ }
2251
+ function isOhProjectionRecordKindV1(value) {
2252
+ return OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.some((kind) => kind === value);
2253
+ }
2254
+
2255
+ // src/memory.ts
2256
+ var OH_MEMORY_FORMAT_VERSION_V1 = 1;
2257
+ var OH_MEMORY_CONFLICT_POLICY_V1 = "visible-conflicts.v1";
2258
+ var OH_MEMORY_LIMITS_V1 = Object.freeze({
2259
+ explainCapabilityEntryBytes: 32 * 1024 * 1024,
2260
+ explainCapabilities: 256,
2261
+ explainCapabilityLifetimeMs: 15 * 60 * 1000,
2262
+ explainCapabilityTotalBytes: 64 * 1024 * 1024,
2263
+ factsPerRecordPerExtractor: 512,
2264
+ maximumExtractorInvocations: 262144,
2265
+ maximumExtractors: 32,
2266
+ maximumNominationRoutes: 64,
2267
+ maximumPrograms: 128,
2268
+ maximumRecordsPerLane: 8192,
2269
+ maximumSyntheticRecords: 16384,
2270
+ rememberBytes: 8 * 1024 * 1024,
2271
+ resultBytes: 32 * 1024 * 1024,
2272
+ snapshotBytesPerLane: 32 * 1024 * 1024,
2273
+ relationsPerExtractor: 64
2274
+ });
2275
+ var memoryFactPackPayload = Object.freeze({
2276
+ factPackId: "oh.memory.composite-facts",
2277
+ factPackRevision: 1,
2278
+ relations: Object.freeze(["memory.agreement", "memory.conflict", "memory.dependency", "memory.record"]),
2279
+ semantics: OH_PROJECTION_SEMANTICS_V1,
2280
+ v: 1
2281
+ });
2282
+ var OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1 = Object.freeze({
2283
+ ...memoryFactPackPayload,
2284
+ extractorSha256: canonicalSha256(memoryFactPackPayload)
2285
+ });
2286
+ var OH_MEMORY_QUERY_LIMITS_V2 = Object.freeze({
2287
+ bindingBytes: 64 * 1024,
2288
+ bindings: 32,
2289
+ continuationBytes: 4 * 1024,
2290
+ continuationKeyMaximumBytes: 64,
2291
+ continuationKeyMinimumBytes: 32,
2292
+ maximumPageBytes: 8 * 1024 * 1024,
2293
+ maximumPageRows: 256,
2294
+ maximumProgramRows: OH_PROJECTION_LIMITS_V1.queryResults,
2295
+ minimumPageBytes: 64 * 1024,
2296
+ requestBytes: 80 * 1024
2297
+ });
2298
+ var builtInFactPolicy = Object.freeze({
2299
+ extractorSha256: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.extractorSha256,
2300
+ factPackId: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.factPackId,
2301
+ kind: "built-in",
2302
+ v: 1
2303
+ });
2304
+ function immutableClone(value) {
2305
+ if (Array.isArray(value)) {
2306
+ return Object.freeze(value.map((item) => immutableClone(item)));
2307
+ }
2308
+ if (value !== null && typeof value === "object") {
2309
+ if (!isPlainRecord(value))
2310
+ throw new TypeError("Memory output contains a non-JSON object.");
2311
+ const cloned = {};
2312
+ for (const key of Object.keys(value)) {
2313
+ Object.defineProperty(cloned, key, {
2314
+ configurable: false,
2315
+ enumerable: true,
2316
+ value: immutableClone(value[key]),
2317
+ writable: false
2318
+ });
2319
+ }
2320
+ return Object.freeze(cloned);
2321
+ }
2322
+ return value;
2323
+ }
2324
+ function compareText(left, right) {
2325
+ return left < right ? -1 : left > right ? 1 : 0;
2326
+ }
2327
+ function exactHead(left, right) {
2328
+ return canonicalJson(left) === canonicalJson(right);
2329
+ }
2330
+ function authorityId(value) {
2331
+ const parsed = safeCode(value, 128);
2332
+ if (parsed === null)
2333
+ throw new TypeError("Invalid memory authority ID.");
2334
+ return parsed;
2335
+ }
2336
+ function bindingFor(store, expected, lane) {
2337
+ const binding = parseOhStoreBindingV1(store.binding);
2338
+ if (binding === null || binding.bindingSha256 !== parseSha256Hex(expected)) {
2339
+ throw new OhIntegrityError(`The ${lane} store is not the host-bound authority.`);
2340
+ }
2341
+ if (binding.profile.profileKind !== lane) {
2342
+ throw new OhProfileError(`The ${lane} memory lane has the wrong store profile.`);
2343
+ }
2344
+ return binding;
2345
+ }
2346
+ function laneIdentity(value) {
2347
+ return Object.freeze({
2348
+ authorityId: value.authorityId,
2349
+ bindingSha256: value.binding.bindingSha256,
2350
+ datasetSha256: value.dataset.datasetSha256,
2351
+ head: value.snapshot.head,
2352
+ lane: value.lane,
2353
+ snapshotSha256: value.projectionSnapshot.snapshotSha256,
2354
+ v: 1
2355
+ });
2356
+ }
2357
+ function datasetForSnapshot(binding, snapshot) {
2358
+ const projectionSnapshot = createOhProjectionSnapshotV1({
2359
+ head: snapshot.head,
2360
+ records: snapshot.records,
2361
+ spaceId: binding.spaceId
2362
+ });
2363
+ const dataset = createOhProjectionDatasetV1({
2364
+ extractorSha256: canonicalSha256({ extractor: "oh.memory.lane-structural", v: 1 }),
2365
+ factPackId: "oh.memory.lane-structural",
2366
+ factPackRevision: 1,
2367
+ facts: createOhProjectionRecordFactsV1(snapshot.records),
2368
+ snapshot: projectionSnapshot
2369
+ });
2370
+ return { dataset, projectionSnapshot };
2371
+ }
2372
+ async function readLane(authority, lane, expectedHead) {
2373
+ const returnedHead = expectedHead ?? await authority.store.head();
2374
+ const head = parseOhHeadV1(immutableClone(returnedHead));
2375
+ if (head === null)
2376
+ throw new OhIntegrityError(`The ${lane} store returned an invalid head.`);
2377
+ const returnedSnapshot = await authority.store.snapshot({
2378
+ head: { operationSha256: head.operationSha256, sequence: head.sequence },
2379
+ maximumRecords: OH_MEMORY_LIMITS_V1.maximumRecordsPerLane
2380
+ });
2381
+ if (!isPlainRecord(returnedSnapshot) || !hasExactKeys(returnedSnapshot, ["head", "records", "v"]) || returnedSnapshot.v !== 1 || !Array.isArray(returnedSnapshot.records)) {
2382
+ throw new OhIntegrityError(`The ${lane} store returned an invalid snapshot envelope.`);
2383
+ }
2384
+ const detached = immutableClone(returnedSnapshot);
2385
+ const detachedHead = parseOhHeadV1(detached.head);
2386
+ if (detachedHead === null)
2387
+ throw new OhIntegrityError(`The ${lane} store returned an invalid snapshot head.`);
2388
+ const snapshot = immutableClone({
2389
+ head: detachedHead,
2390
+ records: detached.records,
2391
+ v: 1
2392
+ });
2393
+ if (!exactHead(snapshot.head, head)) {
2394
+ throw new OhIntegrityError(`The ${lane} snapshot differs from its pinned head.`);
2395
+ }
2396
+ if (utf8ByteLength(canonicalJson(snapshot)) > OH_MEMORY_LIMITS_V1.snapshotBytesPerLane) {
2397
+ throw new RangeError(`The ${lane} memory snapshot exceeds its canonical byte bound.`);
2398
+ }
2399
+ const projected = datasetForSnapshot(authority.binding, snapshot);
2400
+ return Object.freeze({
2401
+ authorityId: authority.authorityId,
2402
+ binding: authority.binding,
2403
+ dataset: projected.dataset,
2404
+ lane,
2405
+ projectionSnapshot: projected.projectionSnapshot,
2406
+ snapshot
2407
+ });
2408
+ }
2409
+ function syntheticKey(lane, recordSha256) {
2410
+ return `memory-source:${lane}:${recordSha256}`;
2411
+ }
2412
+ function createSyntheticSources(lanes) {
2413
+ const sources = new Map;
2414
+ for (const lane of lanes) {
2415
+ for (const physicalRecord of lane.snapshot.records) {
2416
+ const key = syntheticKey(lane.lane, physicalRecord.recordSha256);
2417
+ const record = createKnowledgeGraphRecordV1({
2418
+ dependencies: [],
2419
+ key,
2420
+ kind: "view",
2421
+ v: 1,
2422
+ value: {
2423
+ authorityId: lane.authorityId,
2424
+ bindingSha256: lane.binding.bindingSha256,
2425
+ key: physicalRecord.key,
2426
+ lane: lane.lane,
2427
+ recordSha256: physicalRecord.recordSha256,
2428
+ snapshotSha256: lane.projectionSnapshot.snapshotSha256,
2429
+ v: 1
2430
+ }
2431
+ });
2432
+ const physical = Object.freeze({
2433
+ authorityId: lane.authorityId,
2434
+ bindingSha256: lane.binding.bindingSha256,
2435
+ head: lane.snapshot.head,
2436
+ key: physicalRecord.key,
2437
+ lane: lane.lane,
2438
+ recordSha256: physicalRecord.recordSha256,
2439
+ snapshotSha256: lane.projectionSnapshot.snapshotSha256,
2440
+ v: 1
2441
+ });
2442
+ if (sources.has(key))
2443
+ throw new OhIntegrityError("A memory lane contains a duplicate source digest.");
2444
+ sources.set(key, Object.freeze({ physical, record }));
2445
+ }
2446
+ }
2447
+ if (sources.size > OH_MEMORY_LIMITS_V1.maximumSyntheticRecords) {
2448
+ throw new RangeError("The composite memory snapshot has too many records.");
2449
+ }
2450
+ const records = [...sources.values()].map(({ record }) => record).sort((left, right) => compareText(left.key, right.key));
2451
+ return { records, sources };
2452
+ }
2453
+ function sourceFor(sources, lane, record) {
2454
+ const source = sources.get(syntheticKey(lane, record.recordSha256));
2455
+ if (source === undefined)
2456
+ throw new OhIntegrityError("A composite memory source is missing.");
2457
+ return [{ key: source.record.key, recordSha256: source.record.recordSha256, v: 1 }];
2458
+ }
2459
+ function createCompositeDataset(canonical, working, extractors) {
2460
+ const synthetic = createSyntheticSources([canonical, working]);
2461
+ const extractorInvocations = synthetic.records.length * extractors.length;
2462
+ if (extractorInvocations > OH_MEMORY_LIMITS_V1.maximumExtractorInvocations) {
2463
+ throw new RangeError("The composite memory extractor invocation count exceeds its explicit bound.");
2464
+ }
2465
+ const facts = [];
2466
+ const factDigests = new Set;
2467
+ const factPolicies = new Map;
2468
+ const addFact = (fact, policy) => {
2469
+ if (facts.length >= OH_PROJECTION_LIMITS_V1.facts) {
2470
+ throw new RangeError("The composite memory fact set exceeds its explicit bound.");
2471
+ }
2472
+ if (factDigests.has(fact.factSha256)) {
2473
+ throw new OhIntegrityError("A memory fact extractor emitted the same exact fact twice.");
2474
+ }
2475
+ const priorPolicy = factPolicies.get(fact.relation);
2476
+ if (priorPolicy !== undefined && canonicalJson(priorPolicy) !== canonicalJson(policy)) {
2477
+ throw new OhIntegrityError("A memory relation has more than one fact policy.");
2478
+ }
2479
+ facts.push(fact);
2480
+ factDigests.add(fact.factSha256);
2481
+ factPolicies.set(fact.relation, policy);
2482
+ };
2483
+ const byLane = new Map([
2484
+ ["canonical", new Map(canonical.snapshot.records.map((record) => [record.key, record]))],
2485
+ ["working", new Map(working.snapshot.records.map((record) => [record.key, record]))]
2486
+ ]);
2487
+ for (const lane of [canonical, working]) {
2488
+ for (const record of lane.snapshot.records) {
2489
+ const extractorRecord = immutableClone(record);
2490
+ const source = sourceFor(synthetic.sources, lane.lane, record);
2491
+ addFact(createOhProjectionFactV1({
2492
+ relation: "memory.record",
2493
+ sources: source,
2494
+ tuple: [lane.lane, record.key, record.kind, record.recordSha256]
2495
+ }), builtInFactPolicy);
2496
+ for (const dependency of record.dependencies) {
2497
+ addFact(createOhProjectionFactV1({
2498
+ relation: "memory.dependency",
2499
+ sources: source,
2500
+ tuple: [lane.lane, record.key, dependency]
2501
+ }), builtInFactPolicy);
2502
+ }
2503
+ for (const extractor of extractors) {
2504
+ const declared = extractor.extract(Object.freeze({ lane: lane.lane, record: extractorRecord }));
2505
+ if (!Array.isArray(declared) || declared.length > OH_MEMORY_LIMITS_V1.factsPerRecordPerExtractor) {
2506
+ throw new RangeError("A memory fact extractor exceeded its per-record bound.");
2507
+ }
2508
+ for (const fact of declared) {
2509
+ if (!isPlainRecord(fact) || !hasExactKeys(fact, ["relation", "tuple", "v"]) || fact.v !== 1 || !Array.isArray(fact.tuple) || typeof fact.relation !== "string" || !extractor.relations.includes(fact.relation)) {
2510
+ throw new TypeError("A memory fact extractor returned an invalid or reserved fact.");
2511
+ }
2512
+ addFact(createOhProjectionFactV1({ relation: fact.relation, sources: source, tuple: fact.tuple }), Object.freeze({
2513
+ extractorId: extractor.extractorId,
2514
+ extractorSha256: extractor.extractorSha256,
2515
+ kind: "domain",
2516
+ v: 1
2517
+ }));
2518
+ }
2519
+ }
2520
+ }
2521
+ }
2522
+ const conflicts = [];
2523
+ const canonicalByKey = byLane.get("canonical");
2524
+ const workingByKey = byLane.get("working");
2525
+ for (const key of [...canonicalByKey.keys()].filter((candidate) => workingByKey.has(candidate)).sort()) {
2526
+ const canonicalRecord = canonicalByKey.get(key);
2527
+ const workingRecord = workingByKey.get(key);
2528
+ const sources = [
2529
+ ...sourceFor(synthetic.sources, "canonical", canonicalRecord),
2530
+ ...sourceFor(synthetic.sources, "working", workingRecord)
2531
+ ];
2532
+ if (canonicalRecord.recordSha256 === workingRecord.recordSha256) {
2533
+ addFact(createOhProjectionFactV1({
2534
+ relation: "memory.agreement",
2535
+ sources,
2536
+ tuple: [key, canonicalRecord.recordSha256]
2537
+ }), builtInFactPolicy);
2538
+ } else {
2539
+ addFact(createOhProjectionFactV1({
2540
+ relation: "memory.conflict",
2541
+ sources,
2542
+ tuple: [key, canonicalRecord.recordSha256, workingRecord.recordSha256]
2543
+ }), builtInFactPolicy);
2544
+ conflicts.push(Object.freeze({
2545
+ canonicalRecordSha256: canonicalRecord.recordSha256,
2546
+ key,
2547
+ v: 1,
2548
+ workingRecordSha256: workingRecord.recordSha256
2549
+ }));
2550
+ }
2551
+ }
2552
+ const recordRefs = synthetic.records.map(knowledgeGraphRecordRefV1).sort((left, right) => compareText(left.key, right.key));
2553
+ const sourceIdentity = {
2554
+ canonical: laneIdentity(canonical),
2555
+ conflictPolicy: OH_MEMORY_CONFLICT_POLICY_V1,
2556
+ recordRefs,
2557
+ v: 1,
2558
+ working: laneIdentity(working)
2559
+ };
2560
+ const head = Object.freeze({
2561
+ generation: 1,
2562
+ graphRevisionSha256: canonicalSha256({ kind: "oh.memory.composite-graph", sourceIdentity }),
2563
+ operationSha256: canonicalSha256({ kind: "oh.memory.composite-operation", sourceIdentity }),
2564
+ recordsSha256: canonicalSha256(recordRefs),
2565
+ sequence: 1,
2566
+ v: 1
2567
+ });
2568
+ const snapshot = createOhProjectionSnapshotV1({
2569
+ head,
2570
+ records: synthetic.records,
2571
+ spaceId: "oh.memory.composite"
2572
+ });
2573
+ const dataset = createOhProjectionDatasetV1({
2574
+ extractorSha256: canonicalSha256({
2575
+ builtIn: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.extractorSha256,
2576
+ extensions: extractors.map(({ extractorId, extractorSha256, relations }) => ({
2577
+ extractorId,
2578
+ extractorSha256,
2579
+ relations
2580
+ })),
2581
+ v: 1
2582
+ }),
2583
+ factPackId: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.factPackId,
2584
+ factPackRevision: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.factPackRevision,
2585
+ facts,
2586
+ snapshot
2587
+ });
2588
+ return Object.freeze({
2589
+ conflicts: Object.freeze(conflicts),
2590
+ dataset,
2591
+ factPolicies,
2592
+ snapshot,
2593
+ sources: synthetic.sources
2594
+ });
2595
+ }
2596
+ function mapProof(proof, sources, factPolicies) {
2597
+ if (proof.kind === "truncated")
2598
+ return Object.freeze({ ...proof });
2599
+ if (proof.kind === "derived") {
2600
+ return Object.freeze({
2601
+ ...proof,
2602
+ premises: Object.freeze(proof.premises.map((premise) => mapProof(premise, sources, factPolicies)))
2603
+ });
2604
+ }
2605
+ const physical = proof.sources.map((source) => {
2606
+ const mapped = sources.get(source.key);
2607
+ if (mapped === undefined || mapped.record.recordSha256 !== source.recordSha256) {
2608
+ throw new OhIntegrityError("A projection proof has no exact physical memory source.");
2609
+ }
2610
+ return mapped.physical;
2611
+ }).sort((left, right) => compareText(canonicalJson(left), canonicalJson(right)));
2612
+ const factPolicy = factPolicies.get(proof.relation);
2613
+ if (factPolicy === undefined)
2614
+ throw new OhIntegrityError("A projection proof has no memory fact policy.");
2615
+ return Object.freeze({
2616
+ factPolicy,
2617
+ kind: "fact",
2618
+ relation: proof.relation,
2619
+ sources: Object.freeze(physical),
2620
+ tuple: proof.tuple,
2621
+ v: 1
2622
+ });
2623
+ }
2624
+ function collectLanes(proof, lanes) {
2625
+ if (proof.kind === "truncated")
2626
+ return true;
2627
+ if (proof.kind === "fact") {
2628
+ for (const source of proof.sources)
2629
+ lanes.add(source.lane);
2630
+ return false;
2631
+ }
2632
+ let unknown = proof.premisesTruncated;
2633
+ for (const premise of proof.premises)
2634
+ unknown = collectLanes(premise, lanes) || unknown;
2635
+ return unknown;
2636
+ }
2637
+ function publicRow(row, proofs) {
2638
+ const lanes = new Set;
2639
+ let unknown = row.proofsTruncated;
2640
+ for (const proof of proofs)
2641
+ unknown = collectLanes(proof, lanes) || unknown;
2642
+ const premiseLanes = [...lanes].sort();
2643
+ const premiseAuthority = unknown || premiseLanes.length === 0 ? "unknown" : premiseLanes.includes("working") ? "working" : "canonical";
2644
+ const payload = {
2645
+ premiseAuthority,
2646
+ premiseLanes,
2647
+ proofsTruncated: row.proofsTruncated,
2648
+ supportCount: row.supportCount,
2649
+ v: 1,
2650
+ values: row.values
2651
+ };
2652
+ return Object.freeze({ ...payload, resultRowSha256: canonicalSha256(payload) });
2653
+ }
2654
+ function resolvePrograms(programs) {
2655
+ if (programs.length < 1 || programs.length > OH_MEMORY_LIMITS_V1.maximumPrograms) {
2656
+ throw new RangeError("Memory requires a bounded nonempty named program registry.");
2657
+ }
2658
+ const resolved = new Map;
2659
+ for (const program of programs) {
2660
+ const programId = safeCode(program.programId, 128);
2661
+ const purpose = safeCode(program.purpose, 256);
2662
+ const query = parseOhProjectionQueryV1(program.query);
2663
+ const rulePack = parseOhProjectionRulePackV1(program.rulePack);
2664
+ if (programId === null || purpose === null || query === null || rulePack === null || resolved.has(programId)) {
2665
+ throw new TypeError("Invalid or duplicate named memory program.");
2666
+ }
2667
+ resolved.set(programId, immutableClone({ ...program.evaluation === undefined ? {} : { evaluation: { ...program.evaluation } }, programId, purpose, query, rulePack }));
2668
+ }
2669
+ return resolved;
2670
+ }
2671
+ function resolveExtractors(extractors) {
2672
+ if (extractors.length > OH_MEMORY_LIMITS_V1.maximumExtractors) {
2673
+ throw new RangeError("The memory domain extractor registry is too large.");
2674
+ }
2675
+ const claimedRelations = new Set;
2676
+ const resolved = extractors.map((extractor) => {
2677
+ const extractorId = safeCode(extractor.extractorId, 128);
2678
+ const extractorSha256 = parseSha256Hex(extractor.extractorSha256);
2679
+ if (extractorId === null || extractorSha256 === null || typeof extractor.extract !== "function" || !Array.isArray(extractor.relations) || extractor.relations.length < 1 || extractor.relations.length > OH_MEMORY_LIMITS_V1.relationsPerExtractor) {
2680
+ throw new TypeError("Invalid memory domain fact extractor.");
2681
+ }
2682
+ const relations = extractor.relations.map((relation) => safeCode(relation, 128)).sort();
2683
+ if (relations.some((relation) => relation === null || relation.startsWith("memory.") || relation.startsWith("oh.")) || new Set(relations).size !== relations.length) {
2684
+ throw new TypeError("A memory domain fact extractor has invalid or reserved relations.");
2685
+ }
2686
+ for (const relation of relations) {
2687
+ if (claimedRelations.has(relation)) {
2688
+ throw new TypeError("Memory domain fact extractor relations must have one owner.");
2689
+ }
2690
+ claimedRelations.add(relation);
2691
+ }
2692
+ return Object.freeze({
2693
+ extract: extractor.extract,
2694
+ extractorId,
2695
+ extractorSha256,
2696
+ relations: Object.freeze(relations)
2697
+ });
2698
+ }).sort((left, right) => compareText(left.extractorId, right.extractorId));
2699
+ if (new Set(resolved.map(({ extractorId }) => extractorId)).size !== resolved.length) {
2700
+ throw new TypeError("Duplicate memory domain fact extractor ID.");
2701
+ }
2702
+ return Object.freeze(resolved);
2703
+ }
2704
+ function resolveNominationRoutes(routes) {
2705
+ if (routes.length > OH_MEMORY_LIMITS_V1.maximumNominationRoutes) {
2706
+ throw new RangeError("The memory nomination route registry is too large.");
2707
+ }
2708
+ const resolved = new Map;
2709
+ for (const route of routes) {
2710
+ const nominationId = safeCode(route.nominationId, 128);
2711
+ const destinationPurpose = safeCode(route.destinationPurpose, 256);
2712
+ if (nominationId === null || destinationPurpose === null || resolved.has(nominationId)) {
2713
+ throw new TypeError("Invalid or duplicate memory nomination route.");
2714
+ }
2715
+ resolved.set(nominationId, Object.freeze({ destinationPurpose, nominationId }));
2716
+ }
2717
+ return resolved;
2718
+ }
2719
+ function parseQueryRequest(value) {
2720
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["programId", "v"]) || value.v !== 1)
2721
+ throw new TypeError("Invalid named memory query.");
2722
+ const programId = safeCode(value.programId, 128);
2723
+ if (programId === null)
2724
+ throw new TypeError("Invalid named memory query identity.");
2725
+ return { programId };
2726
+ }
2727
+ function parseExplainRequest(value) {
2728
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["resultSha256", "row", "token", "v"]) || value.v !== 1 || typeof value.token !== "string" || value.token.length !== 43 || !Number.isSafeInteger(value.row) || value.row < 0) {
2729
+ throw new TypeError("Invalid memory explanation request.");
2730
+ }
2731
+ const resultSha256 = parseSha256Hex(value.resultSha256);
2732
+ if (resultSha256 === null)
2733
+ throw new TypeError("Invalid memory explanation result identity.");
2734
+ return { resultSha256, row: value.row, token: value.token };
2735
+ }
2736
+ function parseNominationRequest(value) {
2737
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["nominationId", "roots", "v"]) || value.v !== 1 || !Array.isArray(value.roots) || value.roots.length < 1 || value.roots.length > OH_DEPENDENCY_CLOSURE_LIMITS_V1.roots) {
2738
+ throw new TypeError("Invalid memory nomination request.");
2739
+ }
2740
+ const nominationId = safeCode(value.nominationId, 128);
2741
+ const roots = value.roots.map((root) => safeCode(root, 512)).sort();
2742
+ if (nominationId === null || roots.some((root) => root === null) || new Set(roots).size !== roots.length)
2743
+ throw new TypeError("Invalid memory nomination identity.");
2744
+ return { nominationId, roots };
2745
+ }
2746
+ function isoInstant(date) {
2747
+ const value = date.toISOString();
2748
+ if (parseCanonicalInstantV1(value) === null)
2749
+ throw new TypeError("The memory clock returned an invalid instant.");
2750
+ return value;
2751
+ }
2752
+ function clockMilliseconds(now) {
2753
+ const milliseconds = now().getTime();
2754
+ if (!Number.isFinite(milliseconds))
2755
+ throw new TypeError("The memory clock returned an invalid date.");
2756
+ return milliseconds;
2757
+ }
2758
+ function monotonicMilliseconds(now) {
2759
+ const milliseconds = now();
2760
+ if (!Number.isFinite(milliseconds) || milliseconds < 0) {
2761
+ throw new TypeError("The memory monotonic clock returned an invalid value.");
2762
+ }
2763
+ return milliseconds;
2764
+ }
2765
+ async function createOhMemoryAgentV1(options) {
2766
+ const memoryActorId = safeCode(options.actorId, 128);
2767
+ if (memoryActorId === null)
2768
+ throw new TypeError("Invalid host-bound memory actor ID.");
2769
+ const canonicalStore = options.canonical.store;
2770
+ const workingStore = options.working.store;
2771
+ const workingCodecs = options.working.codecs;
2772
+ const canonicalAuthorityId = authorityId(options.canonical.authorityId);
2773
+ const workingAuthorityId = authorityId(options.working.authorityId);
2774
+ if (canonicalAuthorityId === workingAuthorityId) {
2775
+ throw new OhProfileError("Working and canonical memory must be distinct physical authorities.");
2776
+ }
2777
+ const canonicalBinding = bindingFor(canonicalStore, options.canonical.expectedBindingSha256, "canonical");
2778
+ const workingBinding = bindingFor(workingStore, options.working.expectedBindingSha256, "working");
2779
+ const expectedCanonicalHead = parseOhHeadV1(options.canonical.expectedHead);
2780
+ if (expectedCanonicalHead === null)
2781
+ throw new TypeError("Invalid pinned canonical memory head.");
2782
+ const programs = resolvePrograms(options.programs);
2783
+ const extractors = resolveExtractors(options.extractors ?? []);
2784
+ const nominationRoutes = resolveNominationRoutes(options.nominationRoutes ?? []);
2785
+ const ingress = new OhSemanticBundleIngressV1(workingStore, workingCodecs);
2786
+ const now = options.now ?? (() => new Date);
2787
+ const monotonicNow = options.monotonicNow ?? (() => performance.now());
2788
+ const capabilityLifetime = options.explainCapabilityLifetimeMs ?? OH_MEMORY_LIMITS_V1.explainCapabilityLifetimeMs;
2789
+ if (!Number.isSafeInteger(capabilityLifetime) || capabilityLifetime < 1000 || capabilityLifetime > 60 * 60 * 1000) {
2790
+ throw new RangeError("Invalid memory explanation capability lifetime.");
2791
+ }
2792
+ const canonical = await readLane({
2793
+ authorityId: canonicalAuthorityId,
2794
+ binding: canonicalBinding,
2795
+ store: canonicalStore
2796
+ }, "canonical", expectedCanonicalHead);
2797
+ const explanations = new Map;
2798
+ let explanationBytes = 0;
2799
+ let lastMonotonicMs = -1;
2800
+ let lastWallClockMs = Number.NEGATIVE_INFINITY;
2801
+ const wallClock = () => {
2802
+ const milliseconds = clockMilliseconds(now);
2803
+ if (milliseconds < lastWallClockMs)
2804
+ throw new OhProfileError("The memory wall clock regressed.");
2805
+ lastWallClockMs = milliseconds;
2806
+ return milliseconds;
2807
+ };
2808
+ const monotonicClock = () => {
2809
+ const milliseconds = monotonicMilliseconds(monotonicNow);
2810
+ if (milliseconds < lastMonotonicMs)
2811
+ throw new OhProfileError("The memory monotonic clock regressed.");
2812
+ lastMonotonicMs = milliseconds;
2813
+ return milliseconds;
2814
+ };
2815
+ const deleteExplanation = (token) => {
2816
+ const stored = explanations.get(token);
2817
+ if (stored !== undefined && explanations.delete(token))
2818
+ explanationBytes -= stored.bytes;
2819
+ };
2820
+ const remember = async (value) => {
2821
+ if (utf8ByteLength(canonicalJson(value)) > OH_MEMORY_LIMITS_V1.rememberBytes) {
2822
+ throw new RangeError("The memory semantic bundle exceeds its canonical byte bound.");
2823
+ }
2824
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["expectedHead", "puts", "requestId", "tombstones", "v"]) || value.v !== 1) {
2825
+ throw new TypeError("Invalid memory remember request.");
2826
+ }
2827
+ const requestId = safeCode(value.requestId, 128);
2828
+ if (requestId === null)
2829
+ throw new TypeError("Invalid memory remember request identity.");
2830
+ const operationId = `memory_${canonicalSha256({
2831
+ actorId: memoryActorId,
2832
+ bindingSha256: workingBinding.bindingSha256,
2833
+ requestId,
2834
+ v: 1
2835
+ }).slice(0, 48)}`;
2836
+ const operation = await ingress.commit({
2837
+ actorId: memoryActorId,
2838
+ expectedHead: value.expectedHead,
2839
+ instant: isoInstant(new Date(wallClock())),
2840
+ operationId,
2841
+ puts: value.puts,
2842
+ tombstones: value.tombstones,
2843
+ v: 1
2844
+ });
2845
+ const head = {
2846
+ generation: operation.sequence,
2847
+ graphRevisionSha256: operation.graphRevisionSha256,
2848
+ operationSha256: operation.operationSha256,
2849
+ recordsSha256: operation.recordsSha256,
2850
+ sequence: operation.sequence,
2851
+ v: 1
2852
+ };
2853
+ const payload = {
2854
+ actorId: operation.actorId,
2855
+ authorityId: workingAuthorityId,
2856
+ bindingSha256: workingBinding.bindingSha256,
2857
+ head,
2858
+ instant: operation.instant,
2859
+ lane: "working",
2860
+ operationSha256: operation.operationSha256,
2861
+ requestId,
2862
+ status: "committed",
2863
+ v: 1
2864
+ };
2865
+ return immutableClone({ ...payload, receiptSha256: canonicalSha256(payload) });
2866
+ };
2867
+ const query = async (value) => {
2868
+ const request = parseQueryRequest(value);
2869
+ const program = programs.get(request.programId);
2870
+ if (program === undefined)
2871
+ throw new TypeError("Unknown named memory program.");
2872
+ const working = await readLane({
2873
+ authorityId: workingAuthorityId,
2874
+ binding: workingBinding,
2875
+ store: workingStore
2876
+ }, "working");
2877
+ const composite = createCompositeDataset(canonical, working, extractors);
2878
+ const projection = evaluateOhProjectionV1({
2879
+ dataset: composite.dataset,
2880
+ ...program.evaluation === undefined ? {} : { options: program.evaluation },
2881
+ query: program.query,
2882
+ rulePack: program.rulePack,
2883
+ snapshot: composite.snapshot
2884
+ });
2885
+ const identityPayload = {
2886
+ canonical: laneIdentity(canonical),
2887
+ compositeDatasetSha256: composite.dataset.datasetSha256,
2888
+ conflictPolicy: OH_MEMORY_CONFLICT_POLICY_V1,
2889
+ evaluationSha256: projection.identity.evaluationSha256,
2890
+ programId: program.programId,
2891
+ projectionSha256: projection.identity.projectionSha256,
2892
+ purpose: program.purpose,
2893
+ querySha256: program.query.querySha256,
2894
+ rulePackSha256: program.rulePack.rulePackSha256,
2895
+ v: 1,
2896
+ working: laneIdentity(working)
2897
+ };
2898
+ const identity = immutableClone({
2899
+ ...identityPayload,
2900
+ memorySha256: canonicalSha256(identityPayload)
2901
+ });
2902
+ const proofs = immutableClone(projection.rows.map((row) => row.proofs.map((proof) => mapProof(proof, composite.sources, composite.factPolicies))));
2903
+ const rows = immutableClone(projection.rows.map((row, index) => publicRow(row, proofs[index])));
2904
+ const resultPayload = immutableClone({
2905
+ authority: "derived",
2906
+ conflicts: composite.conflicts,
2907
+ identity,
2908
+ projectionResultSha256: projection.resultSha256,
2909
+ rows,
2910
+ v: 1
2911
+ });
2912
+ const resultSha256 = canonicalSha256(resultPayload);
2913
+ const issuedAt = wallClock();
2914
+ const issuedAtMonotonic = monotonicClock();
2915
+ const expiresAtMs = issuedAt + capabilityLifetime;
2916
+ const expiresAtMonotonicMs = issuedAtMonotonic + capabilityLifetime;
2917
+ const expiresAt = isoInstant(new Date(expiresAtMs));
2918
+ for (const [existingToken, stored] of explanations) {
2919
+ if (issuedAtMonotonic >= stored.expiresAtMonotonicMs)
2920
+ deleteExplanation(existingToken);
2921
+ }
2922
+ const storedPayload = immutableClone({ expiresAtMonotonicMs, identity, proofs, resultSha256, rows });
2923
+ const storedBytes = utf8ByteLength(canonicalJson(storedPayload)) + 128;
2924
+ if (storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityEntryBytes || storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
2925
+ throw new RangeError("The memory explanation exceeds its retained capability bound.");
2926
+ }
2927
+ while (explanations.size >= OH_MEMORY_LIMITS_V1.explainCapabilities || explanationBytes + storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
2928
+ const oldest = explanations.keys().next().value;
2929
+ if (oldest === undefined)
2930
+ break;
2931
+ deleteExplanation(oldest);
2932
+ }
2933
+ let token = randomBytes2(32).toString("base64url");
2934
+ while (explanations.has(token))
2935
+ token = randomBytes2(32).toString("base64url");
2936
+ explanations.set(token, immutableClone({ ...storedPayload, bytes: storedBytes }));
2937
+ explanationBytes += storedBytes;
2938
+ const result = immutableClone({
2939
+ ...resultPayload,
2940
+ explainCapability: { expiresAt, token, v: 1 },
2941
+ resultSha256
2942
+ });
2943
+ if (utf8ByteLength(canonicalJson(result)) > OH_MEMORY_LIMITS_V1.resultBytes) {
2944
+ deleteExplanation(token);
2945
+ throw new RangeError("The composite memory result exceeds its canonical byte bound.");
2946
+ }
2947
+ return result;
2948
+ };
2949
+ const explain = async (value) => {
2950
+ const request = parseExplainRequest(value);
2951
+ const stored = explanations.get(request.token);
2952
+ const currentTime = monotonicClock();
2953
+ if (stored === undefined || stored.resultSha256 !== request.resultSha256 || currentTime >= stored.expiresAtMonotonicMs) {
2954
+ deleteExplanation(request.token);
2955
+ throw new OhProfileError("The memory explanation capability is absent, expired, or misbound.");
2956
+ }
2957
+ const row = stored.rows[request.row];
2958
+ const proofs = stored.proofs[request.row];
2959
+ if (row === undefined || proofs === undefined)
2960
+ throw new RangeError("The explanation row is out of bounds.");
2961
+ const payload = {
2962
+ authority: "derived",
2963
+ identity: stored.identity,
2964
+ premiseAuthority: row.premiseAuthority,
2965
+ premiseLanes: row.premiseLanes,
2966
+ proofs,
2967
+ proofsTruncated: row.proofsTruncated,
2968
+ resultRowSha256: row.resultRowSha256,
2969
+ resultSha256: stored.resultSha256,
2970
+ supportCount: row.supportCount,
2971
+ v: 1,
2972
+ values: row.values
2973
+ };
2974
+ return immutableClone({ ...payload, explanationSha256: canonicalSha256(payload) });
2975
+ };
2976
+ const nominate = async (value) => {
2977
+ const request = parseNominationRequest(value);
2978
+ const route = nominationRoutes.get(request.nominationId);
2979
+ if (route === undefined)
2980
+ throw new TypeError("Unknown named memory nomination route.");
2981
+ const head = parseOhHeadV1(immutableClone(await workingStore.head()));
2982
+ if (head === null)
2983
+ throw new OhIntegrityError("The working nomination store returned an invalid head.");
2984
+ const closure = await workingStore.exportDependencyClosure({ head: {
2985
+ operationSha256: head.operationSha256,
2986
+ sequence: head.sequence
2987
+ }, roots: request.roots });
2988
+ const verified = verifyOhDependencyClosureAgainstV1(closure, { binding: workingBinding, head });
2989
+ if (!verified.ok)
2990
+ throw new OhIntegrityError("The working nomination closure failed exact verification.");
2991
+ if (canonicalJson(verified.closure.roots) !== canonicalJson(request.roots)) {
2992
+ throw new OhIntegrityError("The working nomination closure substituted different roots.");
2993
+ }
2994
+ const source = Object.freeze({
2995
+ authorityId: workingAuthorityId,
2996
+ bindingSha256: workingBinding.bindingSha256,
2997
+ head,
2998
+ lane: "working",
2999
+ v: 1
3000
+ });
3001
+ const payload = {
3002
+ closure: verified.closure,
3003
+ destinationPurpose: route.destinationPurpose,
3004
+ nominationId: route.nominationId,
3005
+ source,
3006
+ status: "prepared",
3007
+ v: 1
3008
+ };
3009
+ return immutableClone({ ...payload, nominationSha256: canonicalSha256(payload) });
3010
+ };
3011
+ return Object.freeze({ explain, nominate, query, remember });
3012
+ }
3013
+ function ownDataKeysV2(value, maximum, label) {
3014
+ if (!isPlainRecord(value))
3015
+ throw new TypeError(`${label} must be a plain data object.`);
3016
+ const ownKeys = Reflect.ownKeys(value);
3017
+ if (ownKeys.length > maximum)
3018
+ throw new RangeError(`${label} has too many entries.`);
3019
+ if (ownKeys.some((key) => typeof key !== "string")) {
3020
+ throw new TypeError(`${label} must have only string data properties.`);
3021
+ }
3022
+ const keys = ownKeys;
3023
+ for (const key of keys) {
3024
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
3025
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
3026
+ throw new TypeError(`${label} must have only enumerable data properties.`);
3027
+ }
3028
+ }
3029
+ return keys;
3030
+ }
3031
+ function continuationKeyV2(value) {
3032
+ if (value === undefined)
3033
+ return Uint8Array.from(randomBytes2(32));
3034
+ if (!(value instanceof Uint8Array) || value.byteLength < OH_MEMORY_QUERY_LIMITS_V2.continuationKeyMinimumBytes || value.byteLength > OH_MEMORY_QUERY_LIMITS_V2.continuationKeyMaximumBytes) {
3035
+ throw new RangeError("The V2 memory continuation key must be 32 through 64 raw bytes.");
3036
+ }
3037
+ return Uint8Array.from(value);
3038
+ }
3039
+ function positiveBounded(value, maximum, label, minimum = 1) {
3040
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
3041
+ throw new RangeError(`${label} must be an integer from ${minimum} through ${maximum}.`);
3042
+ }
3043
+ return value;
3044
+ }
3045
+ function resolveEvaluationV2(value) {
3046
+ if (!isPlainRecord(value) || !hasExactKeys(value, [
3047
+ "maximumDerivedTuples",
3048
+ "maximumProofDepth",
3049
+ "maximumProofNodes",
3050
+ "maximumResultBytes",
3051
+ "maximumRounds",
3052
+ "maximumTotalProofNodes",
3053
+ "maximumWorkUnits"
3054
+ ])) {
3055
+ throw new TypeError("A V2 memory program must declare every projection evaluation limit.");
3056
+ }
3057
+ return Object.freeze({
3058
+ maximumDerivedTuples: positiveBounded(value.maximumDerivedTuples, OH_PROJECTION_LIMITS_V1.derivedTuples, "maximumDerivedTuples"),
3059
+ maximumProofDepth: positiveBounded(value.maximumProofDepth, OH_PROJECTION_LIMITS_V1.proofDepth, "maximumProofDepth"),
3060
+ maximumProofNodes: positiveBounded(value.maximumProofNodes, OH_PROJECTION_LIMITS_V1.proofNodes, "maximumProofNodes"),
3061
+ maximumResultBytes: positiveBounded(value.maximumResultBytes, OH_PROJECTION_LIMITS_V1.resultBytes, "maximumResultBytes", 64 * 1024),
3062
+ maximumRounds: positiveBounded(value.maximumRounds, OH_PROJECTION_LIMITS_V1.rounds, "maximumRounds"),
3063
+ maximumTotalProofNodes: positiveBounded(value.maximumTotalProofNodes, OH_PROJECTION_LIMITS_V1.totalProofNodes, "maximumTotalProofNodes"),
3064
+ maximumWorkUnits: positiveBounded(value.maximumWorkUnits, OH_PROJECTION_LIMITS_V1.workUnits, "maximumWorkUnits")
3065
+ });
3066
+ }
3067
+ function resolveProgramsV2(programs) {
3068
+ if (!Array.isArray(programs) || programs.length < 1 || programs.length > OH_MEMORY_LIMITS_V1.maximumPrograms) {
3069
+ throw new RangeError("Memory requires a bounded nonempty V2 named program registry.");
3070
+ }
3071
+ const resolved = new Map;
3072
+ for (const candidate of programs) {
3073
+ if (!isPlainRecord(candidate) || !hasExactKeys(candidate, [
3074
+ "evaluation",
3075
+ "maximumPageBytes",
3076
+ "maximumRows",
3077
+ "pageSize",
3078
+ "parameters",
3079
+ "programId",
3080
+ "purpose",
3081
+ "query",
3082
+ "rulePack",
3083
+ "v"
3084
+ ]) || candidate.v !== 2 || !Array.isArray(candidate.parameters)) {
3085
+ throw new TypeError("Invalid V2 named memory program.");
3086
+ }
3087
+ const programId = safeCode(candidate.programId, 128);
3088
+ const purpose = safeCode(candidate.purpose, 256);
3089
+ const query = parseOhProjectionQueryV1(candidate.query);
3090
+ const rulePack = parseOhProjectionRulePackV1(candidate.rulePack);
3091
+ const evaluation = resolveEvaluationV2(candidate.evaluation);
3092
+ const maximumRows = positiveBounded(candidate.maximumRows, OH_MEMORY_QUERY_LIMITS_V2.maximumProgramRows, "maximumRows");
3093
+ const pageSize = positiveBounded(candidate.pageSize, Math.min(maximumRows, OH_MEMORY_QUERY_LIMITS_V2.maximumPageRows), "pageSize");
3094
+ const maximumPageBytes = positiveBounded(candidate.maximumPageBytes, OH_MEMORY_QUERY_LIMITS_V2.maximumPageBytes, "maximumPageBytes", OH_MEMORY_QUERY_LIMITS_V2.minimumPageBytes);
3095
+ if (programId === null || purpose === null || query === null || rulePack === null || resolved.has(programId) || query.limit !== maximumRows || candidate.parameters.length > OH_MEMORY_QUERY_LIMITS_V2.bindings) {
3096
+ throw new TypeError("Invalid or duplicate V2 named memory program.");
3097
+ }
3098
+ const parameters = candidate.parameters.map((parameter) => safeCode(parameter, 128)).sort();
3099
+ if (parameters.some((parameter) => parameter === null) || new Set(parameters).size !== parameters.length) {
3100
+ throw new TypeError("A V2 memory program has invalid or duplicate parameters.");
3101
+ }
3102
+ const queryVariables = new Set(query.where.flatMap((literal) => literal.terms.flatMap((term) => term.kind === "variable" ? [term.name] : [])));
3103
+ if (parameters.some((parameter) => !queryVariables.has(parameter) || query.find.includes(parameter))) {
3104
+ throw new TypeError("V2 parameters must be query-body variables that are not projected outputs.");
3105
+ }
3106
+ const detachedParameters = Object.freeze(parameters);
3107
+ const programPayload = {
3108
+ evaluation,
3109
+ maximumPageBytes,
3110
+ maximumRows,
3111
+ pageSize,
3112
+ parameters: detachedParameters,
3113
+ programId,
3114
+ purpose,
3115
+ querySha256: query.querySha256,
3116
+ rulePackSha256: rulePack.rulePackSha256,
3117
+ v: 2
3118
+ };
3119
+ const program = immutableClone({
3120
+ evaluation,
3121
+ maximumPageBytes,
3122
+ maximumRows,
3123
+ pageSize,
3124
+ parameters: detachedParameters,
3125
+ programId,
3126
+ programSha256: canonicalSha256(programPayload),
3127
+ purpose,
3128
+ query,
3129
+ rulePack,
3130
+ v: 2
3131
+ });
3132
+ resolved.set(programId, program);
3133
+ }
3134
+ return resolved;
3135
+ }
3136
+ function parsePrimitiveBindingV2(value) {
3137
+ if (value !== null && typeof value !== "boolean" && typeof value !== "number" && typeof value !== "string")
3138
+ throw new TypeError("Memory bindings must be JSON primitives.");
3139
+ if (typeof value === "string" && value.length > OH_PROJECTION_LIMITS_V1.atomBytes) {
3140
+ throw new RangeError("A memory binding exceeds the projection atom byte bound.");
3141
+ }
3142
+ if (typeof value === "number" && (!Number.isFinite(value) || Object.is(value, -0))) {
3143
+ throw new TypeError("Memory bindings must be canonical finite JSON numbers.");
3144
+ }
3145
+ const serialized = canonicalJson(value);
3146
+ if (utf8ByteLength(serialized) > OH_PROJECTION_LIMITS_V1.atomBytes) {
3147
+ throw new RangeError("A memory binding exceeds the projection atom byte bound.");
3148
+ }
3149
+ return value;
3150
+ }
3151
+ function parseQueryRequestV2(value) {
3152
+ let keys;
3153
+ try {
3154
+ keys = ownDataKeysV2(value, 4, "The parameterized memory query");
3155
+ } catch {
3156
+ throw new TypeError("Invalid parameterized memory query.");
3157
+ }
3158
+ if (keys.length !== 4 || !["bindings", "continuation", "programId", "v"].every((key) => keys.includes(key))) {
3159
+ throw new TypeError("Invalid parameterized memory query.");
3160
+ }
3161
+ const record = value;
3162
+ if (record.v !== 2 || record.continuation !== null && typeof record.continuation !== "string") {
3163
+ throw new TypeError("Invalid parameterized memory query.");
3164
+ }
3165
+ const programId = safeCode(record.programId, 128);
3166
+ if (programId === null)
3167
+ throw new TypeError("Invalid parameterized memory query identity.");
3168
+ const continuation = record.continuation;
3169
+ if (typeof continuation === "string" && (continuation.length < 1 || continuation.length > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes || utf8ByteLength(continuation) > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes)) {
3170
+ throw new RangeError("The memory continuation exceeds its byte bound.");
3171
+ }
3172
+ const bindingKeys = ownDataKeysV2(record.bindings, OH_MEMORY_QUERY_LIMITS_V2.bindings, "The parameterized memory query bindings");
3173
+ const bindingRecord = record.bindings;
3174
+ const bindings = {};
3175
+ for (const key of bindingKeys) {
3176
+ if (safeCode(key, 128) === null)
3177
+ throw new TypeError("Invalid memory binding name.");
3178
+ bindings[key] = parsePrimitiveBindingV2(bindingRecord[key]);
3179
+ }
3180
+ const boundedRequest = { bindings, continuation, programId, v: 2 };
3181
+ if (utf8ByteLength(canonicalJson(boundedRequest)) > OH_MEMORY_QUERY_LIMITS_V2.requestBytes) {
3182
+ throw new RangeError("The parameterized memory query exceeds its canonical byte bound.");
3183
+ }
3184
+ return { bindingsValue: immutableClone(bindings), continuation, programId };
3185
+ }
3186
+ function parseBindingsV2(value, parameters) {
3187
+ if (!isPlainRecord(value) || !hasExactKeys(value, parameters)) {
3188
+ throw new TypeError("Memory query bindings must exactly match the host-declared parameters.");
3189
+ }
3190
+ const bindings = {};
3191
+ for (const parameter of parameters)
3192
+ bindings[parameter] = value[parameter];
3193
+ if (utf8ByteLength(canonicalJson(bindings)) > OH_MEMORY_QUERY_LIMITS_V2.bindingBytes) {
3194
+ throw new RangeError("Memory query bindings exceed their canonical byte bound.");
3195
+ }
3196
+ const detached = immutableClone(bindings);
3197
+ return Object.freeze({
3198
+ bindings: detached,
3199
+ bindingsSha256: canonicalSha256({ bindings: detached, parameters, v: 2 })
3200
+ });
3201
+ }
3202
+ function bindQueryV2(query, bindings) {
3203
+ const where = query.where.map((literal) => createOhProjectionLiteralV1({
3204
+ relation: literal.relation,
3205
+ terms: literal.terms.map((term) => term.kind === "variable" && Object.hasOwn(bindings, term.name) ? ohProjectionConstantV1(bindings[term.name]) : term)
3206
+ }));
3207
+ return createOhProjectionQueryV1({
3208
+ find: query.find,
3209
+ limit: query.limit,
3210
+ queryId: query.queryId,
3211
+ where
3212
+ });
3213
+ }
3214
+ function publicRowV2(row, proofs) {
3215
+ const lanes = new Set;
3216
+ let unknown = row.proofsTruncated;
3217
+ for (const proof of proofs)
3218
+ unknown = collectLanes(proof, lanes) || unknown;
3219
+ const premiseLanes = [...lanes].sort();
3220
+ const premiseAuthority = unknown || premiseLanes.length === 0 ? "unknown" : premiseLanes.includes("working") ? "working" : "canonical";
3221
+ const payload = {
3222
+ premiseAuthority,
3223
+ premiseLanes,
3224
+ proofsTruncated: row.proofsTruncated,
3225
+ supportCount: row.supportCount,
3226
+ v: 2,
3227
+ values: row.values
3228
+ };
3229
+ return Object.freeze({ ...payload, resultRowSha256: canonicalSha256(payload) });
3230
+ }
3231
+ function continuationHmacV2(key, value) {
3232
+ return createHmac("sha256", key).update("oh.memory.continuation.v2\x00", "utf8").update(canonicalJson(value), "utf8").digest();
3233
+ }
3234
+ function encodeContinuationV2(value, key) {
3235
+ const identity = immutableClone(value);
3236
+ const continuationSha256 = canonicalSha256(identity);
3237
+ const signed = immutableClone({ ...identity, continuationSha256 });
3238
+ const envelope = immutableClone({
3239
+ ...signed,
3240
+ continuationHmacSha256: continuationHmacV2(key, signed).toString("hex")
3241
+ });
3242
+ const continuation = Buffer.from(canonicalJson(envelope), "utf8").toString("base64url");
3243
+ if (utf8ByteLength(continuation) > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes) {
3244
+ throw new RangeError("The issued memory continuation exceeds its byte bound.");
3245
+ }
3246
+ return Object.freeze({ continuation, continuationSha256 });
3247
+ }
3248
+ function parseContinuationV2(value, key) {
3249
+ if (value.length < 1 || utf8ByteLength(value) > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes || !/^[A-Za-z0-9_-]+$/u.test(value))
3250
+ throw new TypeError("Invalid memory continuation encoding.");
3251
+ const bytes = Buffer.from(value, "base64url");
3252
+ if (bytes.toString("base64url") !== value || bytes.byteLength > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes) {
3253
+ throw new TypeError("Invalid memory continuation encoding.");
3254
+ }
3255
+ const text = bytes.toString("utf8");
3256
+ let decoded;
3257
+ try {
3258
+ decoded = JSON.parse(text);
3259
+ } catch {
3260
+ throw new TypeError("Invalid memory continuation JSON.");
3261
+ }
3262
+ if (!isPlainRecord(decoded) || !hasExactKeys(decoded, [
3263
+ "bindingsSha256",
3264
+ "continuationHmacSha256",
3265
+ "continuationSha256",
3266
+ "memorySha256",
3267
+ "nextOffset",
3268
+ "pageSize",
3269
+ "programSha256",
3270
+ "projectionResultSha256",
3271
+ "totalRows",
3272
+ "v"
3273
+ ]) || decoded.v !== 2)
3274
+ throw new TypeError("Invalid memory continuation payload.");
3275
+ const bindingsSha256 = parseSha256Hex(decoded.bindingsSha256);
3276
+ const continuationHmacSha256 = parseSha256Hex(decoded.continuationHmacSha256);
3277
+ const continuationSha256 = parseSha256Hex(decoded.continuationSha256);
3278
+ const memorySha256 = parseSha256Hex(decoded.memorySha256);
3279
+ const programSha256 = parseSha256Hex(decoded.programSha256);
3280
+ const projectionResultSha256 = parseSha256Hex(decoded.projectionResultSha256);
3281
+ if (bindingsSha256 === null || continuationHmacSha256 === null || continuationSha256 === null || memorySha256 === null || programSha256 === null || projectionResultSha256 === null || !Number.isSafeInteger(decoded.nextOffset) || decoded.nextOffset < 1 || decoded.nextOffset > OH_MEMORY_QUERY_LIMITS_V2.maximumProgramRows || !Number.isSafeInteger(decoded.pageSize) || decoded.pageSize < 1 || decoded.pageSize > OH_MEMORY_QUERY_LIMITS_V2.maximumPageRows || !Number.isSafeInteger(decoded.totalRows) || decoded.totalRows < 1 || decoded.totalRows > OH_MEMORY_QUERY_LIMITS_V2.maximumProgramRows || decoded.nextOffset >= decoded.totalRows || decoded.nextOffset % decoded.pageSize !== 0) {
3282
+ throw new TypeError("Invalid memory continuation identity.");
3283
+ }
3284
+ const identity = {
3285
+ bindingsSha256,
3286
+ memorySha256,
3287
+ nextOffset: decoded.nextOffset,
3288
+ pageSize: decoded.pageSize,
3289
+ programSha256,
3290
+ projectionResultSha256,
3291
+ totalRows: decoded.totalRows,
3292
+ v: 2
3293
+ };
3294
+ const signed = { ...identity, continuationSha256 };
3295
+ const envelope = { ...signed, continuationHmacSha256 };
3296
+ if (canonicalJson(envelope) !== text)
3297
+ throw new TypeError("Invalid memory continuation payload.");
3298
+ const expectedHmac = continuationHmacV2(key, signed);
3299
+ const receivedHmac = Buffer.from(continuationHmacSha256, "hex");
3300
+ if (!timingSafeEqual(expectedHmac, receivedHmac)) {
3301
+ throw new OhIntegrityError("The memory continuation is not an issued capability.");
3302
+ }
3303
+ if (canonicalSha256(identity) !== continuationSha256) {
3304
+ throw new OhIntegrityError("The memory continuation digest is invalid.");
3305
+ }
3306
+ return Object.freeze(signed);
3307
+ }
3308
+ function parseExplainRequestV2(value) {
3309
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["pageRow", "resultSha256", "token", "v"]) || value.v !== 2 || typeof value.token !== "string" || value.token.length !== 43 || !Number.isSafeInteger(value.pageRow) || value.pageRow < 0) {
3310
+ throw new TypeError("Invalid V2 memory explanation request.");
3311
+ }
3312
+ const resultSha256 = parseSha256Hex(value.resultSha256);
3313
+ if (resultSha256 === null)
3314
+ throw new TypeError("Invalid V2 memory explanation result identity.");
3315
+ return { pageRow: value.pageRow, resultSha256, token: value.token };
3316
+ }
3317
+ async function createOhMemoryAgentV2(options) {
3318
+ const memoryActorId = safeCode(options.actorId, 128);
3319
+ if (memoryActorId === null)
3320
+ throw new TypeError("Invalid host-bound memory actor ID.");
3321
+ const continuationKey = continuationKeyV2(options.continuationKey);
3322
+ const canonicalStore = options.canonical.store;
3323
+ const workingStore = options.working.store;
3324
+ const workingCodecs = options.working.codecs;
3325
+ const canonicalAuthorityId = authorityId(options.canonical.authorityId);
3326
+ const workingAuthorityId = authorityId(options.working.authorityId);
3327
+ if (canonicalAuthorityId === workingAuthorityId) {
3328
+ throw new OhProfileError("Working and canonical memory must be distinct physical authorities.");
3329
+ }
3330
+ const canonicalBinding = bindingFor(canonicalStore, options.canonical.expectedBindingSha256, "canonical");
3331
+ const workingBinding = bindingFor(workingStore, options.working.expectedBindingSha256, "working");
3332
+ const expectedCanonicalHead = parseOhHeadV1(options.canonical.expectedHead);
3333
+ if (expectedCanonicalHead === null)
3334
+ throw new TypeError("Invalid pinned canonical memory head.");
3335
+ const programs = resolveProgramsV2(options.programs);
3336
+ const extractors = resolveExtractors(options.extractors ?? []);
3337
+ const nominationRoutes = resolveNominationRoutes(options.nominationRoutes ?? []);
3338
+ const ingress = new OhSemanticBundleIngressV1(workingStore, workingCodecs);
3339
+ const now = options.now ?? (() => new Date);
3340
+ const monotonicNow = options.monotonicNow ?? (() => performance.now());
3341
+ const capabilityLifetime = options.explainCapabilityLifetimeMs ?? OH_MEMORY_LIMITS_V1.explainCapabilityLifetimeMs;
3342
+ if (!Number.isSafeInteger(capabilityLifetime) || capabilityLifetime < 1000 || capabilityLifetime > 60 * 60 * 1000) {
3343
+ throw new RangeError("Invalid memory explanation capability lifetime.");
3344
+ }
3345
+ const canonical = await readLane({
3346
+ authorityId: canonicalAuthorityId,
3347
+ binding: canonicalBinding,
3348
+ store: canonicalStore
3349
+ }, "canonical", expectedCanonicalHead);
3350
+ const explanations = new Map;
3351
+ let explanationBytes = 0;
3352
+ let lastMonotonicMs = -1;
3353
+ let lastWallClockMs = Number.NEGATIVE_INFINITY;
3354
+ const wallClock = () => {
3355
+ const milliseconds = clockMilliseconds(now);
3356
+ if (milliseconds < lastWallClockMs)
3357
+ throw new OhProfileError("The memory wall clock regressed.");
3358
+ lastWallClockMs = milliseconds;
3359
+ return milliseconds;
3360
+ };
3361
+ const monotonicClock = () => {
3362
+ const milliseconds = monotonicMilliseconds(monotonicNow);
3363
+ if (milliseconds < lastMonotonicMs)
3364
+ throw new OhProfileError("The memory monotonic clock regressed.");
3365
+ lastMonotonicMs = milliseconds;
3366
+ return milliseconds;
3367
+ };
3368
+ const deleteExplanation = (token) => {
3369
+ const stored = explanations.get(token);
3370
+ if (stored !== undefined && explanations.delete(token))
3371
+ explanationBytes -= stored.bytes;
3372
+ };
3373
+ const remember = async (value) => {
3374
+ if (utf8ByteLength(canonicalJson(value)) > OH_MEMORY_LIMITS_V1.rememberBytes) {
3375
+ throw new RangeError("The memory semantic bundle exceeds its canonical byte bound.");
3376
+ }
3377
+ if (!isPlainRecord(value) || !hasExactKeys(value, ["expectedHead", "puts", "requestId", "tombstones", "v"]) || value.v !== 1) {
3378
+ throw new TypeError("Invalid memory remember request.");
3379
+ }
3380
+ const requestId = safeCode(value.requestId, 128);
3381
+ if (requestId === null)
3382
+ throw new TypeError("Invalid memory remember request identity.");
3383
+ const operationId = `memory_${canonicalSha256({
3384
+ actorId: memoryActorId,
3385
+ bindingSha256: workingBinding.bindingSha256,
3386
+ requestId,
3387
+ v: 1
3388
+ }).slice(0, 48)}`;
3389
+ const operation = await ingress.commit({
3390
+ actorId: memoryActorId,
3391
+ expectedHead: value.expectedHead,
3392
+ instant: isoInstant(new Date(wallClock())),
3393
+ operationId,
3394
+ puts: value.puts,
3395
+ tombstones: value.tombstones,
3396
+ v: 1
3397
+ });
3398
+ const head = {
3399
+ generation: operation.sequence,
3400
+ graphRevisionSha256: operation.graphRevisionSha256,
3401
+ operationSha256: operation.operationSha256,
3402
+ recordsSha256: operation.recordsSha256,
3403
+ sequence: operation.sequence,
3404
+ v: 1
3405
+ };
3406
+ const payload = {
3407
+ actorId: operation.actorId,
3408
+ authorityId: workingAuthorityId,
3409
+ bindingSha256: workingBinding.bindingSha256,
3410
+ head,
3411
+ instant: operation.instant,
3412
+ lane: "working",
3413
+ operationSha256: operation.operationSha256,
3414
+ requestId,
3415
+ status: "committed",
3416
+ v: 1
3417
+ };
3418
+ return immutableClone({ ...payload, receiptSha256: canonicalSha256(payload) });
3419
+ };
3420
+ const query = async (value) => {
3421
+ const request = parseQueryRequestV2(value);
3422
+ const program = programs.get(request.programId);
3423
+ if (program === undefined)
3424
+ throw new TypeError("Unknown named V2 memory program.");
3425
+ const bound = parseBindingsV2(request.bindingsValue, program.parameters);
3426
+ const requestedContinuation = request.continuation === null ? null : parseContinuationV2(request.continuation, continuationKey);
3427
+ if (requestedContinuation !== null && (requestedContinuation.bindingsSha256 !== bound.bindingsSha256 || requestedContinuation.pageSize !== program.pageSize || requestedContinuation.programSha256 !== program.programSha256 || requestedContinuation.totalRows > program.maximumRows || requestedContinuation.nextOffset >= requestedContinuation.totalRows || requestedContinuation.nextOffset % program.pageSize !== 0)) {
3428
+ throw new OhIntegrityError("The memory continuation does not match this exact program, binding, and page identity.");
3429
+ }
3430
+ const boundQuery = bindQueryV2(program.query, bound.bindings);
3431
+ const working = await readLane({
3432
+ authorityId: workingAuthorityId,
3433
+ binding: workingBinding,
3434
+ store: workingStore
3435
+ }, "working");
3436
+ const composite = createCompositeDataset(canonical, working, extractors);
3437
+ const projection = evaluateOhProjectionV1({
3438
+ dataset: composite.dataset,
3439
+ options: program.evaluation,
3440
+ query: boundQuery,
3441
+ rulePack: program.rulePack,
3442
+ snapshot: composite.snapshot
3443
+ });
3444
+ if (projection.stats.truncated) {
3445
+ const reasons = projection.stats.truncationReasons.join(", ");
3446
+ throw new RangeError(`The V2 memory projection was truncated (${reasons}); no page was returned.`);
3447
+ }
3448
+ if (projection.rows.length > program.maximumRows) {
3449
+ throw new RangeError("The V2 memory projection exceeds its host-declared row bound.");
3450
+ }
3451
+ const identityPayload = {
3452
+ bindings: bound.bindings,
3453
+ bindingsSha256: bound.bindingsSha256,
3454
+ boundQuerySha256: boundQuery.querySha256,
3455
+ canonical: laneIdentity(canonical),
3456
+ compositeDatasetSha256: composite.dataset.datasetSha256,
3457
+ conflictPolicy: OH_MEMORY_CONFLICT_POLICY_V1,
3458
+ evaluationSha256: projection.identity.evaluationSha256,
3459
+ programId: program.programId,
3460
+ programSha256: program.programSha256,
3461
+ projectionSha256: projection.identity.projectionSha256,
3462
+ purpose: program.purpose,
3463
+ rulePackSha256: program.rulePack.rulePackSha256,
3464
+ templateQuerySha256: program.query.querySha256,
3465
+ v: 2,
3466
+ working: laneIdentity(working)
3467
+ };
3468
+ const identity = immutableClone({
3469
+ ...identityPayload,
3470
+ memorySha256: canonicalSha256(identityPayload)
3471
+ });
3472
+ if (requestedContinuation !== null && (requestedContinuation.memorySha256 !== identity.memorySha256 || requestedContinuation.projectionResultSha256 !== projection.resultSha256)) {
3473
+ throw new OhIntegrityError("The memory continuation does not match this exact source and projection identity.");
3474
+ }
3475
+ if (requestedContinuation !== null && (requestedContinuation.totalRows !== projection.rows.length || requestedContinuation.nextOffset >= projection.rows.length || requestedContinuation.nextOffset % program.pageSize !== 0)) {
3476
+ throw new OhIntegrityError("The memory continuation does not match this exact row identity.");
3477
+ }
3478
+ const start = requestedContinuation?.nextOffset ?? 0;
3479
+ const endExclusive = Math.min(start + program.pageSize, projection.rows.length);
3480
+ const projectionRows = projection.rows.slice(start, endExclusive);
3481
+ const proofs = immutableClone(projectionRows.map((row) => row.proofs.map((proof) => mapProof(proof, composite.sources, composite.factPolicies))));
3482
+ const rows = immutableClone(projectionRows.map((row, index) => publicRowV2(row, proofs[index])));
3483
+ const hasMore = endExclusive < projection.rows.length;
3484
+ const page = immutableClone({
3485
+ completeness: hasMore ? "partial" : "complete",
3486
+ endExclusive,
3487
+ hasMore,
3488
+ maximumPageBytes: program.maximumPageBytes,
3489
+ pageSize: program.pageSize,
3490
+ returnedRows: rows.length,
3491
+ start,
3492
+ totalRows: projection.rows.length,
3493
+ truncation: { reasons: [], truncated: false, v: 2 },
3494
+ v: 2
3495
+ });
3496
+ const issuedContinuation = hasMore ? encodeContinuationV2({
3497
+ bindingsSha256: bound.bindingsSha256,
3498
+ memorySha256: identity.memorySha256,
3499
+ nextOffset: endExclusive,
3500
+ pageSize: program.pageSize,
3501
+ programSha256: program.programSha256,
3502
+ projectionResultSha256: projection.resultSha256,
3503
+ totalRows: projection.rows.length,
3504
+ v: 2
3505
+ }, continuationKey) : null;
3506
+ const continuation = issuedContinuation?.continuation ?? null;
3507
+ const continuationSha256 = issuedContinuation?.continuationSha256 ?? null;
3508
+ const conflicts = immutableClone({
3509
+ count: composite.conflicts.length,
3510
+ conflictsSha256: canonicalSha256(composite.conflicts),
3511
+ v: 2
3512
+ });
3513
+ const resultIdentityPayload = immutableClone({
3514
+ authority: "derived",
3515
+ conflicts,
3516
+ continuationSha256,
3517
+ identity,
3518
+ page,
3519
+ projectionResultSha256: projection.resultSha256,
3520
+ rows,
3521
+ v: 2
3522
+ });
3523
+ const resultSha256 = canonicalSha256(resultIdentityPayload);
3524
+ const resultPayload = immutableClone({ ...resultIdentityPayload, continuation });
3525
+ const issuedAt = wallClock();
3526
+ const issuedAtMonotonic = monotonicClock();
3527
+ const expiresAtMs = issuedAt + capabilityLifetime;
3528
+ const expiresAtMonotonicMs = issuedAtMonotonic + capabilityLifetime;
3529
+ const expiresAt = isoInstant(new Date(expiresAtMs));
3530
+ const pageBytePreflight = {
3531
+ ...resultPayload,
3532
+ explainCapability: { expiresAt, token: "A".repeat(43), v: 2 },
3533
+ resultSha256
3534
+ };
3535
+ if (utf8ByteLength(canonicalJson(pageBytePreflight)) > program.maximumPageBytes) {
3536
+ throw new RangeError("The V2 memory page exceeds its host-declared canonical byte bound.");
3537
+ }
3538
+ for (const [existingToken, stored] of explanations) {
3539
+ if (issuedAtMonotonic >= stored.expiresAtMonotonicMs)
3540
+ deleteExplanation(existingToken);
3541
+ }
3542
+ const storedPayload = immutableClone({
3543
+ expiresAtMonotonicMs,
3544
+ identity,
3545
+ page,
3546
+ proofs,
3547
+ resultSha256,
3548
+ rows
3549
+ });
3550
+ const storedBytes = utf8ByteLength(canonicalJson(storedPayload)) + 128;
3551
+ if (storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityEntryBytes || storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
3552
+ throw new RangeError("The V2 memory explanation exceeds its retained capability bound.");
3553
+ }
3554
+ while (explanations.size >= OH_MEMORY_LIMITS_V1.explainCapabilities || explanationBytes + storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
3555
+ const oldest = explanations.keys().next().value;
3556
+ if (oldest === undefined)
3557
+ break;
3558
+ deleteExplanation(oldest);
3559
+ }
3560
+ let token = randomBytes2(32).toString("base64url");
3561
+ while (explanations.has(token))
3562
+ token = randomBytes2(32).toString("base64url");
3563
+ explanations.set(token, immutableClone({ ...storedPayload, bytes: storedBytes }));
3564
+ explanationBytes += storedBytes;
3565
+ const result = immutableClone({
3566
+ ...resultPayload,
3567
+ explainCapability: { expiresAt, token, v: 2 },
3568
+ resultSha256
3569
+ });
3570
+ if (utf8ByteLength(canonicalJson(result)) > program.maximumPageBytes) {
3571
+ deleteExplanation(token);
3572
+ throw new RangeError("The V2 memory page exceeds its host-declared canonical byte bound.");
3573
+ }
3574
+ return result;
3575
+ };
3576
+ const explain = async (value) => {
3577
+ const request = parseExplainRequestV2(value);
3578
+ const stored = explanations.get(request.token);
3579
+ const currentTime = monotonicClock();
3580
+ if (stored === undefined || stored.resultSha256 !== request.resultSha256 || currentTime >= stored.expiresAtMonotonicMs) {
3581
+ deleteExplanation(request.token);
3582
+ throw new OhProfileError("The V2 memory explanation capability is absent, expired, or misbound.");
3583
+ }
3584
+ const row = stored.rows[request.pageRow];
3585
+ const proofs = stored.proofs[request.pageRow];
3586
+ if (row === undefined || proofs === undefined)
3587
+ throw new RangeError("The explanation page row is out of bounds.");
3588
+ const payload = {
3589
+ authority: "derived",
3590
+ identity: stored.identity,
3591
+ page: stored.page,
3592
+ pageRow: request.pageRow,
3593
+ premiseAuthority: row.premiseAuthority,
3594
+ premiseLanes: row.premiseLanes,
3595
+ proofs,
3596
+ proofsTruncated: row.proofsTruncated,
3597
+ resultRowSha256: row.resultRowSha256,
3598
+ resultSha256: stored.resultSha256,
3599
+ supportCount: row.supportCount,
3600
+ v: 2,
3601
+ values: row.values
3602
+ };
3603
+ return immutableClone({ ...payload, explanationSha256: canonicalSha256(payload) });
3604
+ };
3605
+ const nominate = async (value) => {
3606
+ const request = parseNominationRequest(value);
3607
+ const route = nominationRoutes.get(request.nominationId);
3608
+ if (route === undefined)
3609
+ throw new TypeError("Unknown named memory nomination route.");
3610
+ const head = parseOhHeadV1(immutableClone(await workingStore.head()));
3611
+ if (head === null)
3612
+ throw new OhIntegrityError("The working nomination store returned an invalid head.");
3613
+ const closure = await workingStore.exportDependencyClosure({ head: {
3614
+ operationSha256: head.operationSha256,
3615
+ sequence: head.sequence
3616
+ }, roots: request.roots });
3617
+ const verified = verifyOhDependencyClosureAgainstV1(closure, { binding: workingBinding, head });
3618
+ if (!verified.ok)
3619
+ throw new OhIntegrityError("The working nomination closure failed exact verification.");
3620
+ if (canonicalJson(verified.closure.roots) !== canonicalJson(request.roots)) {
3621
+ throw new OhIntegrityError("The working nomination closure substituted different roots.");
3622
+ }
3623
+ const source = Object.freeze({
3624
+ authorityId: workingAuthorityId,
3625
+ bindingSha256: workingBinding.bindingSha256,
3626
+ head,
3627
+ lane: "working",
3628
+ v: 1
3629
+ });
3630
+ const payload = {
3631
+ closure: verified.closure,
3632
+ destinationPurpose: route.destinationPurpose,
3633
+ nominationId: route.nominationId,
3634
+ source,
3635
+ status: "prepared",
3636
+ v: 1
3637
+ };
3638
+ return immutableClone({ ...payload, nominationSha256: canonicalSha256(payload) });
3639
+ };
3640
+ return Object.freeze({ explain, nominate, query, remember });
3641
+ }
3642
+ export {
3643
+ createOhMemoryAgentV2,
3644
+ createOhMemoryAgentV1,
3645
+ OH_MEMORY_QUERY_LIMITS_V2,
3646
+ OH_MEMORY_LIMITS_V1,
3647
+ OH_MEMORY_FORMAT_VERSION_V1,
3648
+ OH_MEMORY_CONFLICT_POLICY_V1,
3649
+ OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1
3650
+ };