@hraness/oh 0.4.1 → 0.4.2

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 (92) hide show
  1. package/LICENSE +3 -0
  2. package/README.md +4 -1
  3. package/dist/cli.d.ts +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +15590 -1705
  6. package/dist/index.js +15209 -261
  7. package/dist/libsql-model.d.ts +75 -0
  8. package/dist/libsql-model.d.ts.map +1 -0
  9. package/dist/libsql-platform.d.ts +24 -0
  10. package/dist/libsql-platform.d.ts.map +1 -0
  11. package/dist/libsql-program.d.ts +64 -0
  12. package/dist/libsql-program.d.ts.map +1 -0
  13. package/dist/libsql-runtime.d.ts +13 -0
  14. package/dist/libsql-runtime.d.ts.map +1 -0
  15. package/dist/libsql.d.ts +4 -36
  16. package/dist/libsql.d.ts.map +1 -1
  17. package/dist/libsql.js +16583 -1426
  18. package/dist/memory-authority-platform.d.ts +210 -0
  19. package/dist/memory-authority-platform.d.ts.map +1 -0
  20. package/dist/memory-authority-program.d.ts +15 -0
  21. package/dist/memory-authority-program.d.ts.map +1 -0
  22. package/dist/memory-authority-runtime.d.ts +4 -0
  23. package/dist/memory-authority-runtime.d.ts.map +1 -0
  24. package/dist/memory-core.d.ts +522 -0
  25. package/dist/memory-core.d.ts.map +1 -0
  26. package/dist/memory.d.ts +5 -445
  27. package/dist/memory.d.ts.map +1 -1
  28. package/dist/memory.js +18232 -4143
  29. package/dist/sdk.js +15266 -318
  30. package/dist/semantic-cloud.js +1 -260
  31. package/dist/semantic-model.d.ts +75 -0
  32. package/dist/semantic-model.d.ts.map +1 -0
  33. package/dist/semantic-platform.d.ts +32 -0
  34. package/dist/semantic-platform.d.ts.map +1 -0
  35. package/dist/semantic-program.d.ts +18 -0
  36. package/dist/semantic-program.d.ts.map +1 -0
  37. package/dist/semantic-runtime.d.ts +6 -0
  38. package/dist/semantic-runtime.d.ts.map +1 -0
  39. package/dist/semantic.d.ts +6 -57
  40. package/dist/semantic.d.ts.map +1 -1
  41. package/dist/semantic.js +15541 -514
  42. package/dist/sqlite/index.js +1148 -1378
  43. package/dist/sqlite/port.d.ts +1 -1
  44. package/dist/sqlite/port.d.ts.map +1 -1
  45. package/dist/sync-libsql-program.d.ts +5 -0
  46. package/dist/sync-libsql-program.d.ts.map +1 -0
  47. package/dist/sync-model.d.ts +60 -0
  48. package/dist/sync-model.d.ts.map +1 -0
  49. package/dist/sync-platform.d.ts +41 -0
  50. package/dist/sync-platform.d.ts.map +1 -0
  51. package/dist/sync-program.d.ts +11 -0
  52. package/dist/sync-program.d.ts.map +1 -0
  53. package/dist/sync-runtime.d.ts +6 -0
  54. package/dist/sync-runtime.d.ts.map +1 -0
  55. package/dist/sync.d.ts +5 -55
  56. package/dist/sync.d.ts.map +1 -1
  57. package/dist/sync.js +15174 -226
  58. package/package.json +7 -5
  59. package/skills/oh/SKILL.md +2 -1
  60. package/spec/v1/store.md +14 -0
  61. package/src/cli.test.ts +35 -0
  62. package/src/cli.ts +4 -3
  63. package/src/cloudflare-embedding.ts +1 -1
  64. package/src/libsql-lifecycle.test.ts +76 -0
  65. package/src/libsql-model.ts +350 -0
  66. package/src/libsql-platform.ts +51 -0
  67. package/src/libsql-program.ts +1583 -0
  68. package/src/libsql-runtime.ts +89 -0
  69. package/src/libsql-semantic-v2.ts +1 -1
  70. package/src/libsql-semantic.ts +1 -1
  71. package/src/libsql.test.ts +245 -0
  72. package/src/libsql.ts +26 -1698
  73. package/src/memory-authority-platform.ts +133 -0
  74. package/src/memory-authority-program.ts +309 -0
  75. package/src/memory-authority-runtime.ts +40 -0
  76. package/src/memory-core.ts +2401 -0
  77. package/src/memory.test.ts +55 -0
  78. package/src/memory.ts +68 -2759
  79. package/src/semantic-model.ts +142 -0
  80. package/src/semantic-platform.ts +130 -0
  81. package/src/semantic-program.ts +113 -0
  82. package/src/semantic-runtime.ts +53 -0
  83. package/src/semantic.test.ts +115 -1
  84. package/src/semantic.ts +14 -324
  85. package/src/sqlite/port.ts +1 -1
  86. package/src/sync-libsql-program.ts +170 -0
  87. package/src/sync-lifecycle.test.ts +110 -0
  88. package/src/sync-model.ts +530 -0
  89. package/src/sync-platform.ts +87 -0
  90. package/src/sync-program.ts +117 -0
  91. package/src/sync-runtime.ts +33 -0
  92. package/src/sync.ts +10 -765
package/src/memory.ts CHANGED
@@ -1,2763 +1,72 @@
1
- import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
2
- import { isProxy } from "node:util/types";
3
-
1
+ /** Stable public memory protocol and Promise facade. Internal helpers stay private. */
4
2
  export * from "./memory-pages";
5
-
6
- import {
7
- canonicalJson,
8
- canonicalSha256,
9
- hasExactKeys,
10
- isPlainRecord,
11
- orderedUnique,
12
- parseCanonicalInstantV1,
13
- parseSha256Hex,
14
- safeCode,
15
- utf8ByteLength,
16
- type JsonPrimitive,
17
- type Sha256Hex,
18
- } from "./canonical";
19
- import { type OhRecordCodecRegistry } from "./contract";
20
- import {
21
- canonicalKnowledgeGraphChangesV1,
22
- createKnowledgeGraphRecordV1,
23
- knowledgeGraphRecordRefV1,
24
- type KnowledgeGraphChangeV1,
25
- type KnowledgeGraphRecordV1,
26
- } from "./graph";
27
- import { OH_OPERATION_MAX_BYTES_V1, parseOhOperationV1 } from "./operation";
28
3
  import {
29
- OH_PROJECTION_SEMANTICS_V1,
30
- OH_PROJECTION_LIMITS_V1,
31
- createOhProjectionDatasetV1,
32
- createOhProjectionFactV1,
33
- createOhProjectionLiteralV1,
34
- createOhProjectionQueryV1,
35
- createOhProjectionRecordFactsV1,
36
- createOhProjectionSnapshotV1,
37
- evaluateOhProjectionV1,
38
- ohProjectionConstantV1,
39
- parseOhProjectionQueryV1,
40
- parseOhProjectionRulePackV1,
41
- type OhProjectionAtomV1,
42
- type OhProjectionDatasetV1,
43
- type OhProjectionEvaluationOptionsV1,
44
- type OhProjectionFactV1,
45
- type OhProjectionProofV1,
46
- type OhProjectionQueryV1,
47
- type OhProjectionResultRowV1,
48
- type OhProjectionRulePackV1,
49
- type OhProjectionSnapshotV1,
50
- } from "./projection";
51
- import {
52
- OhConflictError,
53
- OhIntegrityError,
54
- OH_DEPENDENCY_CLOSURE_LIMITS_V1,
55
- OhProfileError,
56
- OhSemanticBundleIngressV1,
57
- parseOhDependencyClosureV1,
58
- parseOhHeadV1,
59
- parseOhHeadRefV1,
60
- parseOhStoreBindingV1,
61
- verifyOhDependencyClosureAgainstV1,
62
- type OhDependencyClosureV1,
63
- type OhHeadV1,
64
- type OhHeadRefV1,
65
- type OhSnapshotV1,
66
- type OhStoreBindingV1,
67
- type OhStoreV1,
68
- } from "./store";
69
- export const OH_MEMORY_FORMAT_VERSION_V1 = 1 as const;
70
- export const OH_MEMORY_CONFLICT_POLICY_V1 = "visible-conflicts.v1" as const;
71
- export const OH_MEMORY_LIMITS_V1 = Object.freeze({
72
- detachedCanonicalBreadth: 65_536,
73
- detachedCanonicalDepth: 128,
74
- detachedCanonicalNodes: 1_048_576,
75
- explainCapabilityEntryBytes: 32 * 1024 * 1024,
76
- explainCapabilities: 256,
77
- explainCapabilityLifetimeMs: 15 * 60 * 1_000,
78
- explainCapabilityTotalBytes: 64 * 1024 * 1024,
79
- factsPerRecordPerExtractor: 512,
80
- maximumExtractorInvocations: 262_144,
81
- maximumExtractors: 32,
82
- maximumNominationRoutes: 64,
83
- maximumPrograms: 128,
84
- maximumRecordsPerLane: 8_192,
85
- maximumSyntheticRecords: 16_384,
86
- rememberBytes: 8 * 1024 * 1024,
87
- resultBytes: 32 * 1024 * 1024,
88
- snapshotBytesPerLane: 32 * 1024 * 1024,
89
- relationsPerExtractor: 64,
90
- });
91
-
92
- const memoryFactPackPayload = Object.freeze({
93
- factPackId: "oh.memory.composite-facts",
94
- factPackRevision: 1,
95
- relations: Object.freeze(["memory.agreement", "memory.conflict", "memory.dependency", "memory.record"]),
96
- semantics: OH_PROJECTION_SEMANTICS_V1,
97
- v: 1 as const,
98
- });
99
-
100
- export const OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1 = Object.freeze({
101
- ...memoryFactPackPayload,
102
- extractorSha256: canonicalSha256(memoryFactPackPayload),
103
- });
104
-
105
- export type OhMemoryLaneV1 = "canonical" | "working";
106
-
107
- export type OhMemoryAuthoritySourceV1 = Readonly<{
108
- authorityId: string;
109
- bindingSha256: Sha256Hex;
110
- head: OhHeadV1;
111
- key: string;
112
- lane: OhMemoryLaneV1;
113
- recordSha256: Sha256Hex;
114
- snapshotSha256: Sha256Hex;
115
- v: 1;
116
- }>;
117
-
118
- export type OhMemoryProofV1 =
119
- | Readonly<{
120
- factPolicy: OhMemoryFactPolicyV1;
121
- kind: "fact";
122
- relation: string;
123
- sources: readonly OhMemoryAuthoritySourceV1[];
124
- tuple: readonly OhProjectionAtomV1[];
125
- v: 1;
126
- }>
127
- | Readonly<{
128
- kind: "derived";
129
- premises: readonly OhMemoryProofV1[];
130
- premisesTruncated: boolean;
131
- relation: string;
132
- ruleId: string;
133
- ruleSha256: Sha256Hex;
134
- tuple: readonly OhProjectionAtomV1[];
135
- v: 1;
136
- }>
137
- | Readonly<{
138
- kind: "truncated";
139
- reason: "cycle" | "depth" | "nodes";
140
- relation: string;
141
- tuple: readonly OhProjectionAtomV1[];
142
- v: 1;
143
- }>;
144
-
145
- export type OhMemoryLaneIdentityV1 = Readonly<{
146
- authorityId: string;
147
- bindingSha256: Sha256Hex;
148
- datasetSha256: Sha256Hex;
149
- head: OhHeadV1;
150
- lane: OhMemoryLaneV1;
151
- snapshotSha256: Sha256Hex;
152
- v: 1;
153
- }>;
154
-
155
- export type OhMemoryIdentityV1 = Readonly<{
156
- canonical: OhMemoryLaneIdentityV1;
157
- compositeDatasetSha256: Sha256Hex;
158
- conflictPolicy: typeof OH_MEMORY_CONFLICT_POLICY_V1;
159
- evaluationSha256: Sha256Hex;
160
- memorySha256: Sha256Hex;
161
- programId: string;
162
- projectionSha256: Sha256Hex;
163
- purpose: string;
164
- querySha256: Sha256Hex;
165
- rulePackSha256: Sha256Hex;
166
- v: 1;
167
- working: OhMemoryLaneIdentityV1;
168
- }>;
169
-
170
- export type OhMemoryConflictV1 = Readonly<{
171
- canonicalRecordSha256: Sha256Hex;
172
- key: string;
173
- v: 1;
174
- workingRecordSha256: Sha256Hex;
175
- }>;
176
-
177
- export type OhMemoryResultRowV1 = Readonly<{
178
- premiseAuthority: "canonical" | "unknown" | "working";
179
- premiseLanes: readonly OhMemoryLaneV1[];
180
- proofsTruncated: boolean;
181
- resultRowSha256: Sha256Hex;
182
- supportCount: number;
183
- v: 1;
184
- values: readonly OhProjectionAtomV1[];
185
- }>;
186
-
187
- export type OhMemoryQueryResultV1 = Readonly<{
188
- authority: "derived";
189
- conflicts: readonly OhMemoryConflictV1[];
190
- explainCapability: Readonly<{ expiresAt: string; token: string; v: 1 }>;
191
- identity: OhMemoryIdentityV1;
192
- projectionResultSha256: Sha256Hex;
193
- resultSha256: Sha256Hex;
194
- rows: readonly OhMemoryResultRowV1[];
195
- v: 1;
196
- }>;
197
-
198
- export type OhMemoryRememberReceiptV1 = Readonly<{
199
- actorId: string;
200
- authorityId: string;
201
- bindingSha256: Sha256Hex;
202
- head: OhHeadV1;
203
- instant: string;
204
- lane: "working";
205
- operationSha256: Sha256Hex;
206
- receiptSha256: Sha256Hex;
207
- requestId: string;
208
- status: "committed";
209
- v: 1;
210
- }>;
211
-
212
- export type OhMemoryExplanationV1 = Readonly<{
213
- authority: "derived";
214
- explanationSha256: Sha256Hex;
215
- identity: OhMemoryIdentityV1;
216
- premiseAuthority: OhMemoryResultRowV1["premiseAuthority"];
217
- premiseLanes: readonly OhMemoryLaneV1[];
218
- proofs: readonly OhMemoryProofV1[];
219
- proofsTruncated: boolean;
220
- resultRowSha256: Sha256Hex;
221
- resultSha256: Sha256Hex;
222
- supportCount: number;
223
- v: 1;
224
- values: readonly OhProjectionAtomV1[];
225
- }>;
226
-
227
- export type OhMemoryNominationV1 = Readonly<{
228
- closure: OhDependencyClosureV1;
229
- destinationPurpose: string;
230
- nominationId: string;
231
- nominationSha256: Sha256Hex;
232
- source: Readonly<{
233
- authorityId: string;
234
- bindingSha256: Sha256Hex;
235
- head: OhHeadV1;
236
- lane: "working";
237
- v: 1;
238
- }>;
239
- status: "prepared";
240
- v: 1;
241
- }>;
242
-
243
- export type OhMemoryNamedProgramV1 = Readonly<{
244
- evaluation?: OhProjectionEvaluationOptionsV1;
245
- programId: string;
246
- purpose: string;
247
- query: OhProjectionQueryV1;
248
- rulePack: OhProjectionRulePackV1;
249
- }>;
250
-
251
- export type OhMemoryNominationRouteV1 = Readonly<{
252
- destinationPurpose: string;
253
- nominationId: string;
254
- }>;
255
-
256
- export type OhMemoryFactPolicyV1 =
257
- | Readonly<{
258
- extractorSha256: Sha256Hex;
259
- factPackId: string;
260
- kind: "built-in";
261
- v: 1;
262
- }>
263
- | Readonly<{
264
- extractorId: string;
265
- extractorSha256: Sha256Hex;
266
- kind: "domain";
267
- v: 1;
268
- }>;
269
-
270
- export type OhMemoryFactDeclarationV1 = Readonly<{
271
- relation: string;
272
- tuple: readonly JsonPrimitive[];
273
- v: 1;
274
- }>;
275
-
276
- /** Host-owned, digest-identified domain projection; it cannot choose sources. */
277
- export type OhMemoryFactExtractorV1 = Readonly<{
278
- extract(input: Readonly<{
279
- lane: OhMemoryLaneV1;
280
- record: KnowledgeGraphRecordV1;
281
- }>): readonly OhMemoryFactDeclarationV1[];
282
- extractorId: string;
283
- extractorSha256: Sha256Hex;
284
- relations: readonly string[];
285
- }>;
286
-
287
- export type OhMemoryFacadeOptionsV1 = Readonly<{
288
- actorId: string;
289
- canonical: Readonly<{
290
- authorityId: string;
291
- expectedBindingSha256: Sha256Hex;
292
- expectedHead: OhHeadV1;
293
- store: OhStoreV1;
294
- }>;
295
- explainCapabilityLifetimeMs?: number;
296
- extractors?: readonly OhMemoryFactExtractorV1[];
297
- monotonicNow?: () => number;
298
- nominationRoutes?: readonly OhMemoryNominationRouteV1[];
299
- now?: () => Date;
300
- programs: readonly OhMemoryNamedProgramV1[];
301
- working: Readonly<{
302
- authorityId: string;
303
- codecs: OhRecordCodecRegistry;
304
- expectedBindingSha256: Sha256Hex;
305
- store: OhStoreV1;
306
- }>;
307
- }>;
308
-
309
- export interface OhMemoryAgentV1 {
310
- explain(value: unknown): Promise<OhMemoryExplanationV1>;
311
- nominate(value: unknown): Promise<OhMemoryNominationV1>;
312
- query(value: unknown): Promise<OhMemoryQueryResultV1>;
313
- remember(value: unknown): Promise<OhMemoryRememberReceiptV1>;
314
- }
315
-
316
- /** Additive query and pagination limits; V1 contracts are unchanged. */
317
- export const OH_MEMORY_QUERY_LIMITS_V2 = Object.freeze({
318
- bindingBytes: 64 * 1024,
319
- bindings: 32,
320
- continuationBytes: 4 * 1024,
321
- continuationKeyMaximumBytes: 64,
322
- continuationKeyMinimumBytes: 32,
323
- maximumPageBytes: 8 * 1024 * 1024,
324
- maximumPageRows: 256,
325
- maximumProgramRows: OH_PROJECTION_LIMITS_V1.queryResults,
326
- minimumPageBytes: 64 * 1024,
327
- requestBytes: 80 * 1024,
328
- });
329
-
330
- export type OhMemoryEvaluationLimitsV2 = Readonly<{
331
- maximumDerivedTuples: number;
332
- maximumProofDepth: number;
333
- maximumProofNodes: number;
334
- maximumResultBytes: number;
335
- maximumRounds: number;
336
- maximumTotalProofNodes: number;
337
- maximumWorkUnits: number;
338
- }>;
339
-
340
- /**
341
- * A host-owned parameterized program. Parameter names refer only to variables
342
- * in the query body, never to rule variables or projected output variables.
343
- */
344
- export type OhMemoryNamedProgramV2 = Readonly<{
345
- evaluation: OhMemoryEvaluationLimitsV2;
346
- maximumPageBytes: number;
347
- maximumRows: number;
348
- pageSize: number;
349
- parameters: readonly string[];
350
- programId: string;
351
- purpose: string;
352
- query: OhProjectionQueryV1;
353
- rulePack: OhProjectionRulePackV1;
354
- v: 2;
355
- }>;
356
-
357
- export type OhMemoryFacadeOptionsV2 = Readonly<
358
- Omit<OhMemoryFacadeOptionsV1, "programs"> & Readonly<{
359
- /** Raw HMAC key for continuations that must survive agent reconstruction. */
360
- continuationKey?: Uint8Array;
361
- programs: readonly OhMemoryNamedProgramV2[];
362
- }>
363
- >;
364
-
365
- export type OhMemoryIdentityV2 = Readonly<{
366
- bindings: Readonly<Record<string, JsonPrimitive>>;
367
- bindingsSha256: Sha256Hex;
368
- boundQuerySha256: Sha256Hex;
369
- canonical: OhMemoryLaneIdentityV1;
370
- compositeDatasetSha256: Sha256Hex;
371
- conflictPolicy: typeof OH_MEMORY_CONFLICT_POLICY_V1;
372
- evaluationSha256: Sha256Hex;
373
- memorySha256: Sha256Hex;
374
- programId: string;
375
- programSha256: Sha256Hex;
376
- projectionSha256: Sha256Hex;
377
- purpose: string;
378
- rulePackSha256: Sha256Hex;
379
- templateQuerySha256: Sha256Hex;
380
- v: 2;
381
- working: OhMemoryLaneIdentityV1;
382
- }>;
383
-
384
- export type OhMemoryResultRowV2 = Readonly<{
385
- premiseAuthority: "canonical" | "unknown" | "working";
386
- premiseLanes: readonly OhMemoryLaneV1[];
387
- proofsTruncated: boolean;
388
- resultRowSha256: Sha256Hex;
389
- supportCount: number;
390
- v: 2;
391
- values: readonly OhProjectionAtomV1[];
392
- }>;
393
-
394
- export type OhMemoryPageV2 = Readonly<{
395
- completeness: "complete" | "partial";
396
- endExclusive: number;
397
- hasMore: boolean;
398
- maximumPageBytes: number;
399
- pageSize: number;
400
- returnedRows: number;
401
- start: number;
402
- totalRows: number;
403
- truncation: Readonly<{
404
- reasons: readonly [];
405
- truncated: false;
406
- v: 2;
407
- }>;
408
- v: 2;
409
- }>;
410
-
411
- export type OhMemoryQueryResultV2 = Readonly<{
412
- authority: "derived";
413
- conflicts: Readonly<{ count: number; conflictsSha256: Sha256Hex; v: 2 }>;
414
- continuation: string | null;
415
- continuationSha256: Sha256Hex | null;
416
- explainCapability: Readonly<{ expiresAt: string; token: string; v: 2 }>;
417
- identity: OhMemoryIdentityV2;
418
- page: OhMemoryPageV2;
419
- projectionResultSha256: Sha256Hex;
420
- resultSha256: Sha256Hex;
421
- rows: readonly OhMemoryResultRowV2[];
422
- v: 2;
423
- }>;
424
-
425
- export type OhMemoryContinuationErrorReasonV2 = "authentication" | "encoding" | "identity";
426
-
427
- /** A caller-supplied V2 continuation cannot be decoded, authenticated, or rebound exactly. */
428
- export class OhMemoryContinuationError extends OhIntegrityError {
429
- declare readonly code: "memory-continuation";
430
- declare readonly reason: OhMemoryContinuationErrorReasonV2;
431
-
432
- constructor(reason: OhMemoryContinuationErrorReasonV2, message: string) {
433
- super(message);
434
- this.name = "OhMemoryContinuationError";
435
- Object.defineProperties(this, {
436
- code: { configurable: false, enumerable: true, value: "memory-continuation", writable: false },
437
- reason: { configurable: false, enumerable: true, value: reason, writable: false },
438
- });
439
- }
440
- }
441
-
442
- export type OhMemoryExplanationV2 = Readonly<{
443
- authority: "derived";
444
- explanationSha256: Sha256Hex;
445
- identity: OhMemoryIdentityV2;
446
- page: OhMemoryPageV2;
447
- pageRow: number;
448
- premiseAuthority: OhMemoryResultRowV2["premiseAuthority"];
449
- premiseLanes: readonly OhMemoryLaneV1[];
450
- proofs: readonly OhMemoryProofV1[];
451
- proofsTruncated: boolean;
452
- resultRowSha256: Sha256Hex;
453
- resultSha256: Sha256Hex;
454
- supportCount: number;
455
- v: 2;
456
- values: readonly OhProjectionAtomV1[];
457
- }>;
458
-
459
- export interface OhMemoryAgentV2 {
460
- explain(value: unknown): Promise<OhMemoryExplanationV2>;
461
- nominate(value: unknown): Promise<OhMemoryNominationV1>;
462
- query(value: unknown): Promise<OhMemoryQueryResultV2>;
463
- remember(value: unknown): Promise<OhMemoryRememberReceiptV1>;
464
- }
465
-
466
- export const OH_MEMORY_AUTHORITY_LIMITS_V1 = Object.freeze({
467
- adoptionReplacements: 128,
468
- adoptionRequestBytes: OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes + 192 * 1024,
469
- canonicalAdvanceOperations: 16_384,
470
- canonicalAdvancePages: 64,
471
- canonicalChangeFeedPage: 1_000,
472
- canonicalChangeFeedPageBytes: 64 * 1024 * 1024,
473
- retainedExplanationRoutes: OH_MEMORY_LIMITS_V1.explainCapabilities,
474
- reportedAdoptionConflicts: 128,
475
- });
476
-
477
- export type OhMemoryCanonicalAdvanceReceiptV1 = Readonly<{
478
- authorityId: string;
479
- bindingSha256: Sha256Hex;
480
- head: OhHeadV1;
481
- priorHead: OhHeadV1;
482
- receiptSha256: Sha256Hex;
483
- status: "advanced" | "unchanged";
484
- v: 1;
485
- }>;
486
-
487
- export type OhMemoryAdoptionConflictEntryV1 = Readonly<{
488
- canonicalRecordSha256: Sha256Hex | null;
489
- key: string;
490
- nominatedRecordSha256: Sha256Hex;
491
- v: 1;
492
- }>;
493
-
494
- /** Host-only compare-and-swap evidence for one intentional canonical replacement. */
495
- export type OhMemoryAdoptionReplacementV1 = Readonly<{
496
- expectedPriorRecordSha256: Sha256Hex;
497
- key: string;
498
- v: 1;
499
- }>;
500
-
501
- export type OhMemoryAdoptionConflictV1 = Readonly<{
502
- actualHead: OhHeadV1;
503
- conflicts: readonly OhMemoryAdoptionConflictEntryV1[];
504
- conflictsSha256: Sha256Hex;
505
- expectedHead: OhHeadV1;
506
- reportedConflicts: number;
507
- totalConflicts: number;
508
- truncated: boolean;
509
- v: 1;
510
- }>;
511
-
512
- export class OhMemoryAdoptionConflictError extends OhConflictError {
513
- declare readonly conflict: OhMemoryAdoptionConflictV1;
514
-
515
- constructor(conflict: OhMemoryAdoptionConflictV1) {
516
- super("The nominated records conflict with the current canonical memory head.");
517
- this.name = "OhMemoryAdoptionConflictError";
518
- Object.defineProperty(this, "conflict", { configurable: false, enumerable: true,
519
- value: immutableClone(conflict), writable: false });
520
- }
521
- }
522
-
523
- export type OhMemoryAdoptionReceiptV1 = Readonly<{
524
- actorId: string;
525
- authorityId: string;
526
- bindingSha256: Sha256Hex;
527
- head: OhHeadV1;
528
- nominationSha256: Sha256Hex;
529
- operationSha256: Sha256Hex | null;
530
- priorHead: OhHeadV1;
531
- receiptSha256: Sha256Hex;
532
- status: "adopted" | "already-present";
533
- v: 1;
534
- }>;
535
-
536
- export interface OhMemoryHostControlV1 {
537
- adoptNomination(value: unknown): Promise<OhMemoryAdoptionReceiptV1>;
538
- advanceCanonical(value: unknown): Promise<OhMemoryCanonicalAdvanceReceiptV1>;
539
- }
540
-
541
- export type OhMemoryAuthorityV1 = Readonly<{
542
- agent: OhMemoryAgentV2;
543
- host: OhMemoryHostControlV1;
544
- }>;
545
-
546
- export type OhMemoryAuthorityOptionsV1 = OhMemoryFacadeOptionsV2 & Readonly<{
547
- adoptionActorId: string;
548
- maximumCanonicalOperationBytes?: number;
549
- }>;
550
-
551
- type LaneSnapshot = Readonly<{
552
- authorityId: string;
553
- binding: OhStoreBindingV1;
554
- dataset: OhProjectionDatasetV1;
555
- lane: OhMemoryLaneV1;
556
- projectionSnapshot: OhProjectionSnapshotV1;
557
- snapshot: OhSnapshotV1;
558
- }>;
559
-
560
- type SyntheticSource = Readonly<{
561
- physical: OhMemoryAuthoritySourceV1;
562
- record: KnowledgeGraphRecordV1;
563
- }>;
564
-
565
- type StoredExplanation = Readonly<{
566
- bytes: number;
567
- expiresAtMonotonicMs: number;
568
- identity: OhMemoryIdentityV1;
569
- proofs: readonly (readonly OhMemoryProofV1[])[];
570
- resultSha256: Sha256Hex;
571
- rows: readonly OhMemoryResultRowV1[];
572
- }>;
573
-
574
- const builtInFactPolicy: OhMemoryFactPolicyV1 = Object.freeze({
575
- extractorSha256: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.extractorSha256,
576
- factPackId: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.factPackId,
577
- kind: "built-in",
578
- v: 1,
579
- });
580
-
581
- function immutableClone<T>(value: T): T {
582
- if (Array.isArray(value)) {
583
- return Object.freeze(value.map((item) => immutableClone(item))) as T;
584
- }
585
- if (value !== null && typeof value === "object") {
586
- if (!isPlainRecord(value)) throw new TypeError("Memory output contains a non-JSON object.");
587
- const cloned: Record<string, unknown> = {};
588
- for (const key of Object.keys(value)) {
589
- Object.defineProperty(cloned, key, { configurable: false, enumerable: true,
590
- value: immutableClone(value[key]), writable: false });
591
- }
592
- return Object.freeze(cloned) as T;
593
- }
594
- return value;
595
- }
596
-
597
- type DetachedCanonicalData = Readonly<{
598
- canonical: string;
599
- value: unknown;
600
- }>;
601
-
602
- function canonicalStringByteLength(
603
- value: string,
604
- label: string,
605
- path: string,
606
- maximumBytes: number,
607
- ): number {
608
- let bytes = 2;
609
- for (let index = 0; index < value.length; index += 1) {
610
- const code = value.charCodeAt(index);
611
- if (code >= 0xd800 && code <= 0xdbff) {
612
- const next = value.charCodeAt(index + 1);
613
- if (!(next >= 0xdc00 && next <= 0xdfff)) {
614
- throw new TypeError(`${label} contains invalid Unicode at ${path}.`);
615
- }
616
- bytes += 4;
617
- index += 1;
618
- } else if (code >= 0xdc00 && code <= 0xdfff) {
619
- throw new TypeError(`${label} contains invalid Unicode at ${path}.`);
620
- } else if (code === 0x22 || code === 0x5c || code === 0x08 || code === 0x09
621
- || code === 0x0a || code === 0x0c || code === 0x0d) {
622
- bytes += 2;
623
- } else if (code <= 0x1f) {
624
- bytes += 6;
625
- } else if (code <= 0x7f) {
626
- bytes += 1;
627
- } else if (code <= 0x7ff) {
628
- bytes += 2;
629
- } else {
630
- bytes += 3;
631
- }
632
- if (bytes > maximumBytes) {
633
- throw new RangeError(`${label} exceeds its canonical byte bound.`);
634
- }
635
- }
636
- return bytes;
637
- }
638
-
639
- /**
640
- * Takes one descriptor-based JSON snapshot of an external value. No accessor is
641
- * invoked, proxies are refused at every depth, and callers validate and execute
642
- * only the returned frozen graph and its matching canonical bytes.
643
- */
644
- function detachCanonicalData(
645
- value: unknown,
646
- label: string,
647
- maximumBytes: number,
648
- ): DetachedCanonicalData {
649
- const ancestors = new Set<object>();
650
- let bytes = 0;
651
- let nodes = 0;
652
- const spendBytes = (count: number): void => {
653
- bytes += count;
654
- if (bytes > maximumBytes) throw new RangeError(`${label} exceeds its canonical byte bound.`);
655
- };
656
- const detach = (candidate: unknown, path: string, depth: number): unknown => {
657
- if (depth > OH_MEMORY_LIMITS_V1.detachedCanonicalDepth) {
658
- throw new RangeError(`${label} exceeds its canonical nesting depth bound.`);
659
- }
660
- nodes += 1;
661
- if (nodes > OH_MEMORY_LIMITS_V1.detachedCanonicalNodes) {
662
- throw new RangeError(`${label} exceeds its canonical node bound.`);
663
- }
664
- if (candidate === null) {
665
- spendBytes(4);
666
- return candidate;
667
- }
668
- if (typeof candidate === "boolean") {
669
- spendBytes(candidate ? 4 : 5);
670
- return candidate;
671
- }
672
- if (typeof candidate === "string") {
673
- spendBytes(canonicalStringByteLength(candidate, label, path, maximumBytes - bytes));
674
- return candidate;
675
- }
676
- if (typeof candidate === "number") {
677
- if (!Number.isFinite(candidate) || Object.is(candidate, -0)) {
678
- throw new TypeError(`${label} contains a noncanonical number at ${path}.`);
679
- }
680
- spendBytes(utf8ByteLength(canonicalJson(candidate)));
681
- return candidate;
682
- }
683
- if (typeof candidate !== "object") {
684
- throw new TypeError(`${label} contains a non-JSON value at ${path}.`);
685
- }
686
- if (isProxy(candidate)) throw new TypeError(`${label} contains a proxy at ${path}.`);
687
- if (ancestors.has(candidate)) throw new TypeError(`${label} contains a cycle at ${path}.`);
688
- ancestors.add(candidate);
689
- try {
690
- if (Array.isArray(candidate)) {
691
- const lengthDescriptor = Object.getOwnPropertyDescriptor(candidate, "length");
692
- const length = lengthDescriptor?.value;
693
- if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
694
- throw new TypeError(`${label} contains an invalid array at ${path}.`);
695
- }
696
- if (length > OH_MEMORY_LIMITS_V1.detachedCanonicalBreadth) {
697
- throw new RangeError(`${label} exceeds its canonical breadth bound.`);
698
- }
699
- if (length > OH_MEMORY_LIMITS_V1.detachedCanonicalNodes - nodes) {
700
- throw new RangeError(`${label} exceeds its canonical node bound.`);
701
- }
702
- spendBytes(2 + Math.max(0, length - 1));
703
- const keys = Reflect.ownKeys(candidate);
704
- if (keys.length !== length + 1 || !keys.includes("length")
705
- || keys.some((key) => key !== "length" && (typeof key !== "string"
706
- || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
707
- throw new TypeError(`${label} contains a non-data array at ${path}.`);
708
- }
709
- const detached: unknown[] = [];
710
- for (let index = 0; index < length; index += 1) {
711
- const descriptor = Object.getOwnPropertyDescriptor(candidate, String(index));
712
- if (descriptor === undefined || !descriptor.enumerable
713
- || descriptor.get !== undefined || descriptor.set !== undefined) {
714
- throw new TypeError(`${label} contains a non-data array entry at ${path}[${index}].`);
715
- }
716
- detached.push(detach(descriptor.value, `${path}[${index}]`, depth + 1));
717
- }
718
- return Object.freeze(detached);
719
- }
720
- const prototype = Object.getPrototypeOf(candidate);
721
- if (prototype !== Object.prototype && prototype !== null) {
722
- throw new TypeError(`${label} contains a non-plain object at ${path}.`);
723
- }
724
- const keys = Reflect.ownKeys(candidate);
725
- if (keys.some((key) => typeof key !== "string")) {
726
- throw new TypeError(`${label} contains a symbol property at ${path}.`);
727
- }
728
- if (keys.length > OH_MEMORY_LIMITS_V1.detachedCanonicalBreadth) {
729
- throw new RangeError(`${label} exceeds its canonical breadth bound.`);
730
- }
731
- if (keys.length > OH_MEMORY_LIMITS_V1.detachedCanonicalNodes - nodes) {
732
- throw new RangeError(`${label} exceeds its canonical node bound.`);
733
- }
734
- spendBytes(2 + Math.max(0, keys.length - 1));
735
- const detached: Record<string, unknown> = {};
736
- for (const key of keys as string[]) {
737
- spendBytes(canonicalStringByteLength(key, label, `${path}.<key>`,
738
- maximumBytes - bytes - 1) + 1);
739
- const descriptor = Object.getOwnPropertyDescriptor(candidate, key);
740
- if (descriptor === undefined || !descriptor.enumerable
741
- || descriptor.get !== undefined || descriptor.set !== undefined) {
742
- throw new TypeError(`${label} contains a non-data property at ${path}.${key}.`);
743
- }
744
- Object.defineProperty(detached, key, { configurable: false, enumerable: true,
745
- value: detach(descriptor.value, `${path}.${key}`, depth + 1), writable: false });
746
- }
747
- return Object.freeze(detached);
748
- } finally {
749
- ancestors.delete(candidate);
750
- }
751
- };
752
- const detached = detach(value, "$root", 0);
753
- const canonical = canonicalJson(detached);
754
- if (utf8ByteLength(canonical) !== bytes) {
755
- throw new OhIntegrityError(`${label} canonical byte accounting did not reproduce its snapshot.`);
756
- }
757
- return Object.freeze({ canonical, value: detached });
758
- }
759
-
760
- function compareText(left: string, right: string): number {
761
- return left < right ? -1 : left > right ? 1 : 0;
762
- }
763
-
764
- function exactHead(left: OhHeadV1, right: OhHeadV1): boolean {
765
- return canonicalJson(left) === canonicalJson(right);
766
- }
767
-
768
- function authorityId(value: unknown): string {
769
- const parsed = safeCode(value, 128);
770
- if (parsed === null) throw new TypeError("Invalid memory authority ID.");
771
- return parsed;
772
- }
773
-
774
- function bindingFor(store: OhStoreV1, expected: Sha256Hex, lane: OhMemoryLaneV1): OhStoreBindingV1 {
775
- const binding = parseOhStoreBindingV1(store.binding);
776
- if (binding === null || binding.bindingSha256 !== parseSha256Hex(expected)) {
777
- throw new OhIntegrityError(`The ${lane} store is not the host-bound authority.`);
778
- }
779
- if (binding.profile.profileKind !== lane) {
780
- throw new OhProfileError(`The ${lane} memory lane has the wrong store profile.`);
781
- }
782
- return binding;
783
- }
784
-
785
- function laneIdentity(value: LaneSnapshot): OhMemoryLaneIdentityV1 {
786
- return Object.freeze({
787
- authorityId: value.authorityId,
788
- bindingSha256: value.binding.bindingSha256,
789
- datasetSha256: value.dataset.datasetSha256,
790
- head: value.snapshot.head,
791
- lane: value.lane,
792
- snapshotSha256: value.projectionSnapshot.snapshotSha256,
793
- v: 1,
794
- });
795
- }
796
-
797
- function parseDetachedStoreHead(value: unknown, label: string): OhHeadV1 {
798
- const head = parseOhHeadV1(detachCanonicalData(value, `${label} head`, 4 * 1024).value);
799
- if (head === null) throw new OhIntegrityError(`${label} returned an invalid head.`);
800
- return immutableClone(head);
801
- }
802
-
803
- function parseDetachedStoreSnapshot(
804
- value: unknown,
805
- label: string,
806
- expectedHead: OhHeadV1,
807
- spaceId: string,
808
- ): Readonly<{ projectionSnapshot: OhProjectionSnapshotV1; snapshot: OhSnapshotV1 }> {
809
- const detachedSnapshot = detachCanonicalData(value, `${label} snapshot`,
810
- OH_MEMORY_LIMITS_V1.snapshotBytesPerLane);
811
- if (!isPlainRecord(detachedSnapshot.value)
812
- || !hasExactKeys(detachedSnapshot.value, ["head", "records", "v"])
813
- || detachedSnapshot.value.v !== 1 || !Array.isArray(detachedSnapshot.value.records)) {
814
- throw new OhIntegrityError(`${label} returned an invalid snapshot envelope.`);
815
- }
816
- const detached = detachedSnapshot.value as Record<string, unknown>;
817
- const detachedHead = parseOhHeadV1(detached.head);
818
- if (detachedHead === null) throw new OhIntegrityError(`${label} returned an invalid snapshot head.`);
819
- const snapshot: OhSnapshotV1 = immutableClone({ head: detachedHead,
820
- records: detached.records as OhSnapshotV1["records"], v: 1 });
821
- if (!exactHead(snapshot.head, expectedHead)) {
822
- throw new OhIntegrityError(`${label} snapshot differs from its pinned head.`);
823
- }
824
- let projectionSnapshot: OhProjectionSnapshotV1;
825
- try {
826
- projectionSnapshot = createOhProjectionSnapshotV1({
827
- head: snapshot.head,
828
- records: snapshot.records,
829
- spaceId,
830
- });
831
- } catch {
832
- throw new OhIntegrityError(`${label} returned invalid snapshot records.`);
833
- }
834
- return Object.freeze({ projectionSnapshot, snapshot });
835
- }
836
-
837
- function datasetForSnapshot(
838
- binding: OhStoreBindingV1,
839
- snapshot: OhSnapshotV1,
840
- validatedProjectionSnapshot?: OhProjectionSnapshotV1,
841
- ): Readonly<{
842
- dataset: OhProjectionDatasetV1;
843
- projectionSnapshot: OhProjectionSnapshotV1;
844
- }> {
845
- const projectionSnapshot = validatedProjectionSnapshot ?? createOhProjectionSnapshotV1({
846
- head: snapshot.head, records: snapshot.records, spaceId: binding.spaceId });
847
- const dataset = createOhProjectionDatasetV1({
848
- extractorSha256: canonicalSha256({ extractor: "oh.memory.lane-structural", v: 1 }),
849
- factPackId: "oh.memory.lane-structural",
850
- factPackRevision: 1,
851
- facts: createOhProjectionRecordFactsV1(snapshot.records),
852
- snapshot: projectionSnapshot,
853
- });
854
- return { dataset, projectionSnapshot };
855
- }
856
-
857
- async function readLane(
858
- authority: Readonly<{ authorityId: string; binding: OhStoreBindingV1; store: OhStoreV1 }>,
859
- lane: OhMemoryLaneV1,
860
- expectedHead?: OhHeadV1,
861
- ): Promise<LaneSnapshot> {
862
- const label = `The ${lane} store`;
863
- const head = expectedHead === undefined
864
- ? parseDetachedStoreHead(await authority.store.head(), label)
865
- : immutableClone(expectedHead);
866
- const returnedSnapshot = await authority.store.snapshot({
867
- head: { operationSha256: head.operationSha256, sequence: head.sequence },
868
- maximumRecords: OH_MEMORY_LIMITS_V1.maximumRecordsPerLane,
869
- });
870
- const parsed = parseDetachedStoreSnapshot(returnedSnapshot, label, head,
871
- authority.binding.spaceId);
872
- const projected = datasetForSnapshot(authority.binding, parsed.snapshot,
873
- parsed.projectionSnapshot);
874
- return Object.freeze({ authorityId: authority.authorityId, binding: authority.binding,
875
- dataset: projected.dataset, lane, projectionSnapshot: projected.projectionSnapshot,
876
- snapshot: parsed.snapshot });
877
- }
878
-
879
- function syntheticKey(lane: OhMemoryLaneV1, recordSha256: Sha256Hex): string {
880
- return `memory-source:${lane}:${recordSha256}`;
881
- }
882
-
883
- function createSyntheticSources(lanes: readonly LaneSnapshot[]): Readonly<{
884
- records: readonly KnowledgeGraphRecordV1[];
885
- sources: ReadonlyMap<string, SyntheticSource>;
886
- }> {
887
- const sources = new Map<string, SyntheticSource>();
888
- for (const lane of lanes) {
889
- for (const physicalRecord of lane.snapshot.records) {
890
- const key = syntheticKey(lane.lane, physicalRecord.recordSha256);
891
- const record = createKnowledgeGraphRecordV1({ dependencies: [], key, kind: "view", v: 1,
892
- value: { authorityId: lane.authorityId, bindingSha256: lane.binding.bindingSha256,
893
- key: physicalRecord.key, lane: lane.lane, recordSha256: physicalRecord.recordSha256,
894
- snapshotSha256: lane.projectionSnapshot.snapshotSha256, v: 1 } });
895
- const physical = Object.freeze({ authorityId: lane.authorityId,
896
- bindingSha256: lane.binding.bindingSha256,
897
- head: lane.snapshot.head, key: physicalRecord.key, lane: lane.lane,
898
- recordSha256: physicalRecord.recordSha256,
899
- snapshotSha256: lane.projectionSnapshot.snapshotSha256, v: 1 as const });
900
- if (sources.has(key)) throw new OhIntegrityError("A memory lane contains a duplicate source digest.");
901
- sources.set(key, Object.freeze({ physical, record }));
902
- }
903
- }
904
- if (sources.size > OH_MEMORY_LIMITS_V1.maximumSyntheticRecords) {
905
- throw new RangeError("The composite memory snapshot has too many records.");
906
- }
907
- const records = [...sources.values()].map(({ record }) => record)
908
- .sort((left, right) => compareText(left.key, right.key));
909
- return { records, sources };
910
- }
911
-
912
- function sourceFor(
913
- sources: ReadonlyMap<string, SyntheticSource>,
914
- lane: OhMemoryLaneV1,
915
- record: KnowledgeGraphRecordV1,
916
- ) {
917
- const source = sources.get(syntheticKey(lane, record.recordSha256));
918
- if (source === undefined) throw new OhIntegrityError("A composite memory source is missing.");
919
- return [{ key: source.record.key, recordSha256: source.record.recordSha256, v: 1 as const }];
920
- }
921
-
922
- function createCompositeDataset(canonical: LaneSnapshot, working: LaneSnapshot,
923
- extractors: readonly OhMemoryFactExtractorV1[]): Readonly<{
924
- conflicts: readonly OhMemoryConflictV1[];
925
- dataset: OhProjectionDatasetV1;
926
- factPolicies: ReadonlyMap<string, OhMemoryFactPolicyV1>;
927
- snapshot: OhProjectionSnapshotV1;
928
- sources: ReadonlyMap<string, SyntheticSource>;
929
- }> {
930
- const synthetic = createSyntheticSources([canonical, working]);
931
- const extractorInvocations = synthetic.records.length * extractors.length;
932
- if (extractorInvocations > OH_MEMORY_LIMITS_V1.maximumExtractorInvocations) {
933
- throw new RangeError("The composite memory extractor invocation count exceeds its explicit bound.");
934
- }
935
- const facts: OhProjectionFactV1[] = [];
936
- const factDigests = new Set<Sha256Hex>();
937
- const factPolicies = new Map<string, OhMemoryFactPolicyV1>();
938
- const addFact = (fact: OhProjectionFactV1, policy: OhMemoryFactPolicyV1) => {
939
- if (facts.length >= OH_PROJECTION_LIMITS_V1.facts) {
940
- throw new RangeError("The composite memory fact set exceeds its explicit bound.");
941
- }
942
- if (factDigests.has(fact.factSha256)) {
943
- throw new OhIntegrityError("A memory fact extractor emitted the same exact fact twice.");
944
- }
945
- const priorPolicy = factPolicies.get(fact.relation);
946
- if (priorPolicy !== undefined && canonicalJson(priorPolicy) !== canonicalJson(policy)) {
947
- throw new OhIntegrityError("A memory relation has more than one fact policy.");
948
- }
949
- facts.push(fact);
950
- factDigests.add(fact.factSha256);
951
- factPolicies.set(fact.relation, policy);
952
- };
953
- const byLane = new Map<OhMemoryLaneV1, Map<string, KnowledgeGraphRecordV1>>([
954
- ["canonical", new Map(canonical.snapshot.records.map((record) => [record.key, record]))],
955
- ["working", new Map(working.snapshot.records.map((record) => [record.key, record]))],
956
- ]);
957
- for (const lane of [canonical, working] as const) {
958
- for (const record of lane.snapshot.records) {
959
- const extractorRecord = immutableClone(record);
960
- const source = sourceFor(synthetic.sources, lane.lane, record);
961
- addFact(createOhProjectionFactV1({ relation: "memory.record", sources: source,
962
- tuple: [lane.lane, record.key, record.kind, record.recordSha256] }), builtInFactPolicy);
963
- for (const dependency of record.dependencies) {
964
- addFact(createOhProjectionFactV1({ relation: "memory.dependency", sources: source,
965
- tuple: [lane.lane, record.key, dependency] }), builtInFactPolicy);
966
- }
967
- for (const extractor of extractors) {
968
- const declared = extractor.extract(Object.freeze({ lane: lane.lane, record: extractorRecord }));
969
- if (!Array.isArray(declared)
970
- || declared.length > OH_MEMORY_LIMITS_V1.factsPerRecordPerExtractor) {
971
- throw new RangeError("A memory fact extractor exceeded its per-record bound.");
972
- }
973
- for (const fact of declared) {
974
- if (!isPlainRecord(fact) || !hasExactKeys(fact, ["relation", "tuple", "v"])
975
- || fact.v !== 1 || !Array.isArray(fact.tuple) || typeof fact.relation !== "string"
976
- || !extractor.relations.includes(fact.relation)) {
977
- throw new TypeError("A memory fact extractor returned an invalid or reserved fact.");
978
- }
979
- addFact(createOhProjectionFactV1({ relation: fact.relation, sources: source, tuple: fact.tuple }),
980
- Object.freeze({ extractorId: extractor.extractorId,
981
- extractorSha256: extractor.extractorSha256, kind: "domain", v: 1 }));
982
- }
983
- }
984
- }
985
- }
986
- const conflicts: OhMemoryConflictV1[] = [];
987
- const canonicalByKey = byLane.get("canonical")!;
988
- const workingByKey = byLane.get("working")!;
989
- for (const key of [...canonicalByKey.keys()].filter((candidate) => workingByKey.has(candidate)).sort()) {
990
- const canonicalRecord = canonicalByKey.get(key)!;
991
- const workingRecord = workingByKey.get(key)!;
992
- const sources = [
993
- ...sourceFor(synthetic.sources, "canonical", canonicalRecord),
994
- ...sourceFor(synthetic.sources, "working", workingRecord),
995
- ];
996
- if (canonicalRecord.recordSha256 === workingRecord.recordSha256) {
997
- addFact(createOhProjectionFactV1({ relation: "memory.agreement", sources,
998
- tuple: [key, canonicalRecord.recordSha256] }), builtInFactPolicy);
999
- } else {
1000
- addFact(createOhProjectionFactV1({ relation: "memory.conflict", sources,
1001
- tuple: [key, canonicalRecord.recordSha256, workingRecord.recordSha256] }), builtInFactPolicy);
1002
- conflicts.push(Object.freeze({ canonicalRecordSha256: canonicalRecord.recordSha256,
1003
- key, v: 1, workingRecordSha256: workingRecord.recordSha256 }));
1004
- }
1005
- }
1006
- const recordRefs = synthetic.records.map(knowledgeGraphRecordRefV1)
1007
- .sort((left, right) => compareText(left.key, right.key));
1008
- const sourceIdentity = {
1009
- canonical: laneIdentity(canonical),
1010
- conflictPolicy: OH_MEMORY_CONFLICT_POLICY_V1,
1011
- recordRefs,
1012
- v: 1 as const,
1013
- working: laneIdentity(working),
1014
- };
1015
- const head: OhHeadV1 = Object.freeze({ generation: 1,
1016
- graphRevisionSha256: canonicalSha256({ kind: "oh.memory.composite-graph", sourceIdentity }),
1017
- operationSha256: canonicalSha256({ kind: "oh.memory.composite-operation", sourceIdentity }),
1018
- recordsSha256: canonicalSha256(recordRefs), sequence: 1, v: 1 });
1019
- const snapshot = createOhProjectionSnapshotV1({ head, records: synthetic.records,
1020
- spaceId: "oh.memory.composite" });
1021
- const dataset = createOhProjectionDatasetV1({
1022
- extractorSha256: canonicalSha256({
1023
- builtIn: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.extractorSha256,
1024
- extensions: extractors.map(({ extractorId, extractorSha256, relations }) => ({
1025
- extractorId, extractorSha256, relations,
1026
- })),
1027
- v: 1,
1028
- }),
1029
- factPackId: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.factPackId,
1030
- factPackRevision: OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1.factPackRevision,
1031
- facts,
1032
- snapshot,
1033
- });
1034
- return Object.freeze({ conflicts: Object.freeze(conflicts), dataset, factPolicies, snapshot,
1035
- sources: synthetic.sources });
1036
- }
1037
-
1038
- function mapProof(proof: OhProjectionProofV1,
1039
- sources: ReadonlyMap<string, SyntheticSource>,
1040
- factPolicies: ReadonlyMap<string, OhMemoryFactPolicyV1>): OhMemoryProofV1 {
1041
- if (proof.kind === "truncated") return Object.freeze({ ...proof });
1042
- if (proof.kind === "derived") {
1043
- return Object.freeze({ ...proof,
1044
- premises: Object.freeze(proof.premises.map((premise) => mapProof(premise, sources, factPolicies))) });
1045
- }
1046
- const physical = proof.sources.map((source) => {
1047
- const mapped = sources.get(source.key);
1048
- if (mapped === undefined || mapped.record.recordSha256 !== source.recordSha256) {
1049
- throw new OhIntegrityError("A projection proof has no exact physical memory source.");
1050
- }
1051
- return mapped.physical;
1052
- }).sort((left, right) => compareText(canonicalJson(left), canonicalJson(right)));
1053
- const factPolicy = factPolicies.get(proof.relation);
1054
- if (factPolicy === undefined) throw new OhIntegrityError("A projection proof has no memory fact policy.");
1055
- return Object.freeze({ factPolicy, kind: "fact", relation: proof.relation,
1056
- sources: Object.freeze(physical), tuple: proof.tuple, v: 1 });
1057
- }
1058
-
1059
- function collectLanes(proof: OhMemoryProofV1, lanes: Set<OhMemoryLaneV1>): boolean {
1060
- if (proof.kind === "truncated") return true;
1061
- if (proof.kind === "fact") {
1062
- for (const source of proof.sources) lanes.add(source.lane);
1063
- return false;
1064
- }
1065
- let unknown = proof.premisesTruncated;
1066
- for (const premise of proof.premises) unknown = collectLanes(premise, lanes) || unknown;
1067
- return unknown;
1068
- }
1069
-
1070
- function publicRow(row: OhProjectionResultRowV1,
1071
- proofs: readonly OhMemoryProofV1[]): OhMemoryResultRowV1 {
1072
- const lanes = new Set<OhMemoryLaneV1>();
1073
- let unknown = row.proofsTruncated;
1074
- for (const proof of proofs) unknown = collectLanes(proof, lanes) || unknown;
1075
- const premiseLanes = [...lanes].sort() as readonly OhMemoryLaneV1[];
1076
- const premiseAuthority: OhMemoryResultRowV1["premiseAuthority"] = unknown || premiseLanes.length === 0
1077
- ? "unknown" : premiseLanes.includes("working") ? "working" : "canonical";
1078
- const payload = { premiseAuthority, premiseLanes, proofsTruncated: row.proofsTruncated,
1079
- supportCount: row.supportCount, v: 1 as const, values: row.values };
1080
- return Object.freeze({ ...payload, resultRowSha256: canonicalSha256(payload) });
1081
- }
1082
-
1083
- function resolvePrograms(programs: readonly OhMemoryNamedProgramV1[]): ReadonlyMap<string, OhMemoryNamedProgramV1> {
1084
- if (programs.length < 1 || programs.length > OH_MEMORY_LIMITS_V1.maximumPrograms) {
1085
- throw new RangeError("Memory requires a bounded nonempty named program registry.");
1086
- }
1087
- const resolved = new Map<string, OhMemoryNamedProgramV1>();
1088
- for (const program of programs) {
1089
- const programId = safeCode(program.programId, 128);
1090
- const purpose = safeCode(program.purpose, 256);
1091
- const query = parseOhProjectionQueryV1(program.query);
1092
- const rulePack = parseOhProjectionRulePackV1(program.rulePack);
1093
- if (programId === null || purpose === null || query === null || rulePack === null
1094
- || resolved.has(programId)) {
1095
- throw new TypeError("Invalid or duplicate named memory program.");
1096
- }
1097
- resolved.set(programId, immutableClone({ ...(program.evaluation === undefined ? {}
1098
- : { evaluation: { ...program.evaluation } }), programId, purpose, query, rulePack }));
1099
- }
1100
- return resolved;
1101
- }
1102
-
1103
- function resolveExtractors(extractors: readonly OhMemoryFactExtractorV1[]): readonly OhMemoryFactExtractorV1[] {
1104
- if (extractors.length > OH_MEMORY_LIMITS_V1.maximumExtractors) {
1105
- throw new RangeError("The memory domain extractor registry is too large.");
1106
- }
1107
- const claimedRelations = new Set<string>();
1108
- const resolved = extractors.map((extractor) => {
1109
- const extractorId = safeCode(extractor.extractorId, 128);
1110
- const extractorSha256 = parseSha256Hex(extractor.extractorSha256);
1111
- if (extractorId === null || extractorSha256 === null || typeof extractor.extract !== "function"
1112
- || !Array.isArray(extractor.relations) || extractor.relations.length < 1
1113
- || extractor.relations.length > OH_MEMORY_LIMITS_V1.relationsPerExtractor) {
1114
- throw new TypeError("Invalid memory domain fact extractor.");
1115
- }
1116
- const relations = extractor.relations.map((relation) => safeCode(relation, 128)).sort();
1117
- if (relations.some((relation) => relation === null
1118
- || relation.startsWith("memory.") || relation.startsWith("oh."))
1119
- || new Set(relations).size !== relations.length) {
1120
- throw new TypeError("A memory domain fact extractor has invalid or reserved relations.");
1121
- }
1122
- for (const relation of relations as string[]) {
1123
- if (claimedRelations.has(relation)) {
1124
- throw new TypeError("Memory domain fact extractor relations must have one owner.");
1125
- }
1126
- claimedRelations.add(relation);
1127
- }
1128
- return Object.freeze({ extract: extractor.extract, extractorId, extractorSha256,
1129
- relations: Object.freeze(relations as string[]) });
1130
- }).sort((left, right) => compareText(left.extractorId, right.extractorId));
1131
- if (new Set(resolved.map(({ extractorId }) => extractorId)).size !== resolved.length) {
1132
- throw new TypeError("Duplicate memory domain fact extractor ID.");
1133
- }
1134
- return Object.freeze(resolved);
1135
- }
1136
-
1137
- function resolveNominationRoutes(routes: readonly OhMemoryNominationRouteV1[]):
1138
- ReadonlyMap<string, OhMemoryNominationRouteV1> {
1139
- if (routes.length > OH_MEMORY_LIMITS_V1.maximumNominationRoutes) {
1140
- throw new RangeError("The memory nomination route registry is too large.");
1141
- }
1142
- const resolved = new Map<string, OhMemoryNominationRouteV1>();
1143
- for (const route of routes) {
1144
- const nominationId = safeCode(route.nominationId, 128);
1145
- const destinationPurpose = safeCode(route.destinationPurpose, 256);
1146
- if (nominationId === null || destinationPurpose === null || resolved.has(nominationId)) {
1147
- throw new TypeError("Invalid or duplicate memory nomination route.");
1148
- }
1149
- resolved.set(nominationId, Object.freeze({ destinationPurpose, nominationId }));
1150
- }
1151
- return resolved;
1152
- }
1153
-
1154
- function parseQueryRequest(value: unknown): Readonly<{ programId: string }> {
1155
- const detached = detachCanonicalData(value, "The named memory query",
1156
- OH_MEMORY_QUERY_LIMITS_V2.requestBytes).value;
1157
- if (!isPlainRecord(detached) || !hasExactKeys(detached, ["programId", "v"])
1158
- || detached.v !== 1) throw new TypeError("Invalid named memory query.");
1159
- const programId = safeCode(detached.programId, 128);
1160
- if (programId === null) throw new TypeError("Invalid named memory query identity.");
1161
- return { programId };
1162
- }
1163
-
1164
- function parseExplainRequest(value: unknown): Readonly<{
1165
- row: number;
1166
- resultSha256: Sha256Hex;
1167
- token: string;
1168
- }> {
1169
- const detached = detachCanonicalData(value, "The memory explanation request",
1170
- OH_MEMORY_QUERY_LIMITS_V2.requestBytes).value;
1171
- if (!isPlainRecord(detached)
1172
- || !hasExactKeys(detached, ["resultSha256", "row", "token", "v"])
1173
- || detached.v !== 1 || typeof detached.token !== "string" || detached.token.length !== 43
1174
- || !Number.isSafeInteger(detached.row) || (detached.row as number) < 0) {
1175
- throw new TypeError("Invalid memory explanation request.");
1176
- }
1177
- const resultSha256 = parseSha256Hex(detached.resultSha256);
1178
- if (resultSha256 === null) throw new TypeError("Invalid memory explanation result identity.");
1179
- return { resultSha256, row: detached.row as number, token: detached.token };
1180
- }
1181
-
1182
- function parseNominationRequest(value: unknown): Readonly<{
1183
- nominationId: string;
1184
- roots: readonly string[];
1185
- }> {
1186
- const detached = detachCanonicalData(value, "The memory nomination request",
1187
- OH_MEMORY_QUERY_LIMITS_V2.requestBytes * 8).value;
1188
- if (!isPlainRecord(detached) || !hasExactKeys(detached, ["nominationId", "roots", "v"])
1189
- || detached.v !== 1 || !Array.isArray(detached.roots) || detached.roots.length < 1
1190
- || detached.roots.length > OH_DEPENDENCY_CLOSURE_LIMITS_V1.roots) {
1191
- throw new TypeError("Invalid memory nomination request.");
1192
- }
1193
- const nominationId = safeCode(detached.nominationId, 128);
1194
- const roots = detached.roots.map((root) => safeCode(root, 512)).sort();
1195
- if (nominationId === null || roots.some((root) => root === null)
1196
- || new Set(roots).size !== roots.length) throw new TypeError("Invalid memory nomination identity.");
1197
- return { nominationId, roots: roots as readonly string[] };
1198
- }
1199
-
1200
- function parseDetachedMemoryNominationV1(value: unknown): OhMemoryNominationV1 | null {
1201
- try {
1202
- const keys = ownDataKeysV2(value, 7, "The memory nomination");
1203
- if (keys.length !== 7 || !["closure", "destinationPurpose", "nominationId",
1204
- "nominationSha256", "source", "status", "v"].every((key) => keys.includes(key))) return null;
1205
- const record = value as Record<string, unknown>;
1206
- if (record.status !== "prepared" || record.v !== 1) return null;
1207
- const closure = parseOhDependencyClosureV1(record.closure);
1208
- const destinationPurpose = safeCode(record.destinationPurpose, 256);
1209
- const nominationId = safeCode(record.nominationId, 128);
1210
- const nominationSha256 = parseSha256Hex(record.nominationSha256);
1211
- if (closure === null || destinationPurpose === null || nominationId === null
1212
- || nominationSha256 === null || closure.binding.profile.profileKind !== "working"
1213
- || [...ownDataKeysV2(record.source, 5, "The memory nomination source")].sort().join("\0")
1214
- !== ["authorityId", "bindingSha256", "head", "lane", "v"].sort().join("\0")) return null;
1215
- const sourceRecord = record.source as Record<string, unknown>;
1216
- if (sourceRecord.lane !== "working" || sourceRecord.v !== 1) return null;
1217
- const authorityIdValue = safeCode(sourceRecord.authorityId, 128);
1218
- const bindingSha256 = parseSha256Hex(sourceRecord.bindingSha256);
1219
- const head = parseOhHeadV1(sourceRecord.head);
1220
- if (authorityIdValue === null || bindingSha256 === null || head === null
1221
- || bindingSha256 !== closure.binding.bindingSha256
1222
- || !exactHead(head, closure.head)) return null;
1223
- const source = { authorityId: authorityIdValue, bindingSha256, head,
1224
- lane: "working" as const, v: 1 as const };
1225
- const payload = { closure, destinationPurpose, nominationId, source,
1226
- status: "prepared" as const, v: 1 as const };
1227
- return canonicalSha256(payload) === nominationSha256
1228
- ? immutableClone({ ...payload, nominationSha256 }) : null;
1229
- } catch {
1230
- return null;
1231
- }
1232
- }
1233
-
1234
- export function parseOhMemoryNominationV1(value: unknown): OhMemoryNominationV1 | null {
1235
- try {
1236
- const detached = detachCanonicalData(value, "The memory nomination",
1237
- OH_MEMORY_AUTHORITY_LIMITS_V1.adoptionRequestBytes);
1238
- return parseDetachedMemoryNominationV1(detached.value);
1239
- } catch {
1240
- return null;
1241
- }
1242
- }
1243
-
1244
- function isoInstant(date: Date): string {
1245
- const value = date.toISOString();
1246
- if (parseCanonicalInstantV1(value) === null) throw new TypeError("The memory clock returned an invalid instant.");
1247
- return value;
1248
- }
1249
-
1250
- function clockMilliseconds(now: () => Date): number {
1251
- const milliseconds = now().getTime();
1252
- if (!Number.isFinite(milliseconds)) throw new TypeError("The memory clock returned an invalid date.");
1253
- return milliseconds;
1254
- }
1255
-
1256
- function monotonicMilliseconds(now: () => number): number {
1257
- const milliseconds = now();
1258
- if (!Number.isFinite(milliseconds) || milliseconds < 0) {
1259
- throw new TypeError("The memory monotonic clock returned an invalid value.");
1260
- }
1261
- return milliseconds;
1262
- }
1263
-
1264
- /**
1265
- * Creates a model-facing memory surface over two host-bound physical Oh
1266
- * authorities. The returned object has no store, locator, rule, sync, canonical
1267
- * write, or purge handle.
1268
- */
1269
- export async function createOhMemoryAgentV1(options: OhMemoryFacadeOptionsV1): Promise<OhMemoryAgentV1> {
1270
- const memoryActorId = safeCode(options.actorId, 128);
1271
- if (memoryActorId === null) throw new TypeError("Invalid host-bound memory actor ID.");
1272
- const canonicalStore = options.canonical.store;
1273
- const workingStore = options.working.store;
1274
- const workingCodecs = options.working.codecs;
1275
- const canonicalAuthorityId = authorityId(options.canonical.authorityId);
1276
- const workingAuthorityId = authorityId(options.working.authorityId);
1277
- if (canonicalAuthorityId === workingAuthorityId) {
1278
- throw new OhProfileError("Working and canonical memory must be distinct physical authorities.");
1279
- }
1280
- const canonicalBinding = bindingFor(canonicalStore,
1281
- options.canonical.expectedBindingSha256, "canonical");
1282
- const workingBinding = bindingFor(workingStore,
1283
- options.working.expectedBindingSha256, "working");
1284
- const expectedCanonicalHead = parseMemoryAuthorityHead(options.canonical.expectedHead,
1285
- "pinned canonical");
1286
- const programs = resolvePrograms(options.programs);
1287
- const extractors = resolveExtractors(options.extractors ?? []);
1288
- const nominationRoutes = resolveNominationRoutes(options.nominationRoutes ?? []);
1289
- const ingress = new OhSemanticBundleIngressV1(
1290
- capacityGuardedWorkingStore(workingStore, workingBinding),
1291
- workingCodecs,
1292
- );
1293
- const now = options.now ?? (() => new Date());
1294
- const monotonicNow = options.monotonicNow ?? (() => performance.now());
1295
- const capabilityLifetime = options.explainCapabilityLifetimeMs
1296
- ?? OH_MEMORY_LIMITS_V1.explainCapabilityLifetimeMs;
1297
- if (!Number.isSafeInteger(capabilityLifetime) || capabilityLifetime < 1_000
1298
- || capabilityLifetime > 60 * 60 * 1_000) {
1299
- throw new RangeError("Invalid memory explanation capability lifetime.");
1300
- }
1301
- const canonical = await readLane({ authorityId: canonicalAuthorityId,
1302
- binding: canonicalBinding, store: canonicalStore }, "canonical", expectedCanonicalHead);
1303
- const explanations = new Map<string, StoredExplanation>();
1304
- let explanationBytes = 0;
1305
- let lastMonotonicMs = -1;
1306
- let lastWallClockMs = Number.NEGATIVE_INFINITY;
1307
- const wallClock = () => {
1308
- const milliseconds = clockMilliseconds(now);
1309
- if (milliseconds < lastWallClockMs) throw new OhProfileError("The memory wall clock regressed.");
1310
- lastWallClockMs = milliseconds;
1311
- return milliseconds;
1312
- };
1313
- const monotonicClock = () => {
1314
- const milliseconds = monotonicMilliseconds(monotonicNow);
1315
- if (milliseconds < lastMonotonicMs) throw new OhProfileError("The memory monotonic clock regressed.");
1316
- lastMonotonicMs = milliseconds;
1317
- return milliseconds;
1318
- };
1319
- const deleteExplanation = (token: string) => {
1320
- const stored = explanations.get(token);
1321
- if (stored !== undefined && explanations.delete(token)) explanationBytes -= stored.bytes;
1322
- };
1323
-
1324
- const remember = async (value: unknown): Promise<OhMemoryRememberReceiptV1> => {
1325
- const detached = detachCanonicalData(value, "The memory semantic bundle",
1326
- OH_MEMORY_LIMITS_V1.rememberBytes).value;
1327
- if (!isPlainRecord(detached) || !hasExactKeys(detached,
1328
- ["expectedHead", "puts", "requestId", "tombstones", "v"]) || detached.v !== 1) {
1329
- throw new TypeError("Invalid memory remember request.");
1330
- }
1331
- const requestId = safeCode(detached.requestId, 128);
1332
- if (requestId === null) throw new TypeError("Invalid memory remember request identity.");
1333
- const operationId = `memory_${canonicalSha256({ actorId: memoryActorId,
1334
- bindingSha256: workingBinding.bindingSha256, requestId, v: 1 }).slice(0, 48)}`;
1335
- const returnedOperation = await ingress.commit({ actorId: memoryActorId,
1336
- expectedHead: detached.expectedHead, instant: isoInstant(new Date(wallClock())), operationId,
1337
- puts: detached.puts, tombstones: detached.tombstones, v: 1 });
1338
- const operation = parseOhOperationV1(detachCanonicalData(returnedOperation,
1339
- "The returned working memory operation", OH_MEMORY_LIMITS_V1.rememberBytes * 2).value);
1340
- if (operation === null || operation.actorId !== memoryActorId
1341
- || operation.operationId !== operationId || operation.spaceId !== workingBinding.spaceId) {
1342
- throw new OhIntegrityError("The working authority returned a different memory operation.");
1343
- }
1344
- const head: OhHeadV1 = { generation: operation.sequence,
1345
- graphRevisionSha256: operation.graphRevisionSha256,
1346
- operationSha256: operation.operationSha256, recordsSha256: operation.recordsSha256,
1347
- sequence: operation.sequence, v: 1 };
1348
- const payload = { actorId: operation.actorId, authorityId: workingAuthorityId,
1349
- bindingSha256: workingBinding.bindingSha256, head, instant: operation.instant,
1350
- lane: "working" as const, operationSha256: operation.operationSha256, requestId,
1351
- status: "committed" as const, v: 1 as const };
1352
- return immutableClone({ ...payload, receiptSha256: canonicalSha256(payload) });
1353
- };
1354
-
1355
- const query = async (value: unknown): Promise<OhMemoryQueryResultV1> => {
1356
- const request = parseQueryRequest(value);
1357
- const program = programs.get(request.programId);
1358
- if (program === undefined) throw new TypeError("Unknown named memory program.");
1359
- const working = await readLane({ authorityId: workingAuthorityId,
1360
- binding: workingBinding, store: workingStore }, "working");
1361
- const composite = createCompositeDataset(canonical, working, extractors);
1362
- const projection = evaluateOhProjectionV1({ dataset: composite.dataset,
1363
- ...(program.evaluation === undefined ? {} : { options: program.evaluation }),
1364
- query: program.query, rulePack: program.rulePack, snapshot: composite.snapshot });
1365
- const identityPayload = {
1366
- canonical: laneIdentity(canonical),
1367
- compositeDatasetSha256: composite.dataset.datasetSha256,
1368
- conflictPolicy: OH_MEMORY_CONFLICT_POLICY_V1,
1369
- evaluationSha256: projection.identity.evaluationSha256,
1370
- programId: program.programId,
1371
- projectionSha256: projection.identity.projectionSha256,
1372
- purpose: program.purpose,
1373
- querySha256: program.query.querySha256,
1374
- rulePackSha256: program.rulePack.rulePackSha256,
1375
- v: 1 as const,
1376
- working: laneIdentity(working),
1377
- };
1378
- const identity: OhMemoryIdentityV1 = immutableClone({ ...identityPayload,
1379
- memorySha256: canonicalSha256(identityPayload) });
1380
- const proofs = immutableClone(projection.rows.map((row) =>
1381
- row.proofs.map((proof) => mapProof(proof, composite.sources, composite.factPolicies))));
1382
- const rows = immutableClone(projection.rows.map((row, index) => publicRow(row, proofs[index]!)));
1383
- const resultPayload = immutableClone({ authority: "derived" as const,
1384
- conflicts: composite.conflicts, identity, projectionResultSha256: projection.resultSha256,
1385
- rows, v: 1 as const });
1386
- const resultSha256 = canonicalSha256(resultPayload);
1387
- const issuedAt = wallClock();
1388
- const issuedAtMonotonic = monotonicClock();
1389
- const expiresAtMs = issuedAt + capabilityLifetime;
1390
- const expiresAtMonotonicMs = issuedAtMonotonic + capabilityLifetime;
1391
- const expiresAt = isoInstant(new Date(expiresAtMs));
1392
- for (const [existingToken, stored] of explanations) {
1393
- if (issuedAtMonotonic >= stored.expiresAtMonotonicMs) deleteExplanation(existingToken);
1394
- }
1395
- const storedPayload = immutableClone({ expiresAtMonotonicMs, identity, proofs, resultSha256, rows });
1396
- const storedBytes = utf8ByteLength(canonicalJson(storedPayload)) + 128;
1397
- if (storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityEntryBytes
1398
- || storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
1399
- throw new RangeError("The memory explanation exceeds its retained capability bound.");
1400
- }
1401
- while (explanations.size >= OH_MEMORY_LIMITS_V1.explainCapabilities
1402
- || explanationBytes + storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
1403
- const oldest = explanations.keys().next().value as string | undefined;
1404
- if (oldest === undefined) break;
1405
- deleteExplanation(oldest);
1406
- }
1407
- let token = randomBytes(32).toString("base64url");
1408
- while (explanations.has(token)) token = randomBytes(32).toString("base64url");
1409
- explanations.set(token, immutableClone({ ...storedPayload, bytes: storedBytes }));
1410
- explanationBytes += storedBytes;
1411
- const result = immutableClone({ ...resultPayload,
1412
- explainCapability: { expiresAt, token, v: 1 as const }, resultSha256 });
1413
- if (utf8ByteLength(canonicalJson(result)) > OH_MEMORY_LIMITS_V1.resultBytes) {
1414
- deleteExplanation(token);
1415
- throw new RangeError("The composite memory result exceeds its canonical byte bound.");
1416
- }
1417
- return result;
1418
- };
1419
-
1420
- const explain = async (value: unknown): Promise<OhMemoryExplanationV1> => {
1421
- const request = parseExplainRequest(value);
1422
- const stored = explanations.get(request.token);
1423
- const currentTime = monotonicClock();
1424
- if (stored === undefined || stored.resultSha256 !== request.resultSha256
1425
- || currentTime >= stored.expiresAtMonotonicMs) {
1426
- deleteExplanation(request.token);
1427
- throw new OhProfileError("The memory explanation capability is absent, expired, or misbound.");
1428
- }
1429
- const row = stored.rows[request.row];
1430
- const proofs = stored.proofs[request.row];
1431
- if (row === undefined || proofs === undefined) throw new RangeError("The explanation row is out of bounds.");
1432
- const payload = { authority: "derived" as const, identity: stored.identity,
1433
- premiseAuthority: row.premiseAuthority, premiseLanes: row.premiseLanes, proofs,
1434
- proofsTruncated: row.proofsTruncated, resultRowSha256: row.resultRowSha256,
1435
- resultSha256: stored.resultSha256, supportCount: row.supportCount, v: 1 as const,
1436
- values: row.values };
1437
- return immutableClone({ ...payload, explanationSha256: canonicalSha256(payload) });
1438
- };
1439
-
1440
- const nominate = async (value: unknown): Promise<OhMemoryNominationV1> => {
1441
- const request = parseNominationRequest(value);
1442
- const route = nominationRoutes.get(request.nominationId);
1443
- if (route === undefined) throw new TypeError("Unknown named memory nomination route.");
1444
- const head = parseOhHeadV1(detachCanonicalData(await workingStore.head(),
1445
- "The working nomination store head", 4 * 1024).value);
1446
- if (head === null) throw new OhIntegrityError("The working nomination store returned an invalid head.");
1447
- const returnedClosure = await workingStore.exportDependencyClosure({ head: {
1448
- operationSha256: head.operationSha256, sequence: head.sequence }, roots: request.roots });
1449
- const closure = detachCanonicalData(returnedClosure, "The working nomination closure",
1450
- OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes).value;
1451
- const verified = verifyOhDependencyClosureAgainstV1(closure, { binding: workingBinding, head });
1452
- if (!verified.ok) throw new OhIntegrityError("The working nomination closure failed exact verification.");
1453
- if (canonicalJson(verified.closure.roots) !== canonicalJson(request.roots)) {
1454
- throw new OhIntegrityError("The working nomination closure substituted different roots.");
1455
- }
1456
- const source = Object.freeze({ authorityId: workingAuthorityId,
1457
- bindingSha256: workingBinding.bindingSha256, head, lane: "working" as const, v: 1 as const });
1458
- const payload = { closure: verified.closure, destinationPurpose: route.destinationPurpose,
1459
- nominationId: route.nominationId, source, status: "prepared" as const, v: 1 as const };
1460
- return immutableClone({ ...payload, nominationSha256: canonicalSha256(payload) });
1461
- };
1462
-
1463
- return Object.freeze({ explain, nominate, query, remember });
1464
- }
1465
-
1466
- type ResolvedMemoryProgramV2 = OhMemoryNamedProgramV2 & Readonly<{
1467
- programSha256: Sha256Hex;
1468
- }>;
1469
-
1470
- type StoredExplanationV2 = Readonly<{
1471
- bytes: number;
1472
- expiresAtMonotonicMs: number;
1473
- identity: OhMemoryIdentityV2;
1474
- page: OhMemoryPageV2;
1475
- proofs: readonly (readonly OhMemoryProofV1[])[];
1476
- resultSha256: Sha256Hex;
1477
- rows: readonly OhMemoryResultRowV2[];
1478
- }>;
1479
-
1480
- type OhMemoryRuntimeV2 = {
1481
- readonly capabilityLifetime: number;
1482
- explanationBytes: number;
1483
- readonly explanations: Map<string, StoredExplanationV2>;
1484
- lastMonotonicMs: number;
1485
- lastWallClockMs: number;
1486
- readonly monotonicNow: () => number;
1487
- readonly now: () => Date;
4
+ OH_MEMORY_FORMAT_VERSION_V1,
5
+ OH_MEMORY_CONFLICT_POLICY_V1,
6
+ OH_MEMORY_LIMITS_V1,
7
+ OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1,
8
+ OH_MEMORY_QUERY_LIMITS_V2,
9
+ OhMemoryContinuationError,
10
+ OH_MEMORY_AUTHORITY_LIMITS_V1,
11
+ OhMemoryAdoptionConflictError,
12
+ parseOhMemoryNominationV1,
13
+ createOhMemoryAgentV1,
14
+ createOhMemoryAgentV2
15
+ } from "./memory-core";
16
+ import { createOhMemoryAuthorityV1 as makeAuthority } from "./memory-authority-runtime";
17
+ import type { OhMemoryAuthorityOptionsV1, OhMemoryAuthorityV1 } from "./memory-core";
18
+ export {
19
+ OH_MEMORY_FORMAT_VERSION_V1,
20
+ OH_MEMORY_CONFLICT_POLICY_V1,
21
+ OH_MEMORY_LIMITS_V1,
22
+ OH_MEMORY_COMPOSITE_FACT_EXTRACTOR_V1,
23
+ OH_MEMORY_QUERY_LIMITS_V2,
24
+ OhMemoryContinuationError,
25
+ OH_MEMORY_AUTHORITY_LIMITS_V1,
26
+ OhMemoryAdoptionConflictError,
27
+ parseOhMemoryNominationV1,
28
+ createOhMemoryAgentV1,
29
+ createOhMemoryAgentV2
1488
30
  };
1489
-
1490
- function createOhMemoryRuntimeV2(options: OhMemoryFacadeOptionsV2): OhMemoryRuntimeV2 {
1491
- const capabilityLifetime = options.explainCapabilityLifetimeMs
1492
- ?? OH_MEMORY_LIMITS_V1.explainCapabilityLifetimeMs;
1493
- if (!Number.isSafeInteger(capabilityLifetime) || capabilityLifetime < 1_000
1494
- || capabilityLifetime > 60 * 60 * 1_000) {
1495
- throw new RangeError("Invalid memory explanation capability lifetime.");
1496
- }
1497
- return {
1498
- capabilityLifetime,
1499
- explanationBytes: 0,
1500
- explanations: new Map<string, StoredExplanationV2>(),
1501
- lastMonotonicMs: -1,
1502
- lastWallClockMs: Number.NEGATIVE_INFINITY,
1503
- monotonicNow: options.monotonicNow ?? (() => performance.now()),
1504
- now: options.now ?? (() => new Date()),
1505
- };
1506
- }
1507
-
1508
- type OhMemoryContinuationIdentityV2 = Readonly<{
1509
- bindingsSha256: Sha256Hex;
1510
- memorySha256: Sha256Hex;
1511
- nextOffset: number;
1512
- pageSize: number;
1513
- programSha256: Sha256Hex;
1514
- projectionResultSha256: Sha256Hex;
1515
- totalRows: number;
1516
- v: 2;
1517
- }>;
1518
-
1519
- type OhMemoryContinuationV2 = OhMemoryContinuationIdentityV2 & Readonly<{
1520
- continuationSha256: Sha256Hex;
1521
- }>;
1522
-
1523
- type OhMemoryContinuationEnvelopeV2 = OhMemoryContinuationV2 & Readonly<{
1524
- continuationHmacSha256: Sha256Hex;
1525
- }>;
1526
-
1527
- function ownDataKeysV2(value: unknown, maximum: number, label: string): readonly string[] {
1528
- if (!isPlainRecord(value)) throw new TypeError(`${label} must be a plain data object.`);
1529
- const ownKeys = Reflect.ownKeys(value);
1530
- if (ownKeys.length > maximum) throw new RangeError(`${label} has too many entries.`);
1531
- if (ownKeys.some((key) => typeof key !== "string")) {
1532
- throw new TypeError(`${label} must have only string data properties.`);
1533
- }
1534
- const keys = ownKeys as string[];
1535
- for (const key of keys) {
1536
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
1537
- if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor)) {
1538
- throw new TypeError(`${label} must have only enumerable data properties.`);
1539
- }
1540
- }
1541
- return keys;
1542
- }
1543
-
1544
- function continuationKeyV2(value: Uint8Array | undefined): Uint8Array {
1545
- if (value === undefined) return Uint8Array.from(randomBytes(32));
1546
- if (!(value instanceof Uint8Array)
1547
- || value.byteLength < OH_MEMORY_QUERY_LIMITS_V2.continuationKeyMinimumBytes
1548
- || value.byteLength > OH_MEMORY_QUERY_LIMITS_V2.continuationKeyMaximumBytes) {
1549
- throw new RangeError("The V2 memory continuation key must be 32 through 64 raw bytes.");
1550
- }
1551
- return Uint8Array.from(value);
1552
- }
1553
-
1554
- function positiveBounded(value: unknown, maximum: number, label: string, minimum = 1): number {
1555
- if (!Number.isSafeInteger(value) || (value as number) < minimum || (value as number) > maximum) {
1556
- throw new RangeError(`${label} must be an integer from ${minimum} through ${maximum}.`);
1557
- }
1558
- return value as number;
1559
- }
1560
-
1561
- function resolveEvaluationV2(value: unknown): OhMemoryEvaluationLimitsV2 {
1562
- if (!isPlainRecord(value) || !hasExactKeys(value, ["maximumDerivedTuples", "maximumProofDepth",
1563
- "maximumProofNodes", "maximumResultBytes", "maximumRounds", "maximumTotalProofNodes",
1564
- "maximumWorkUnits"])) {
1565
- throw new TypeError("A V2 memory program must declare every projection evaluation limit.");
1566
- }
1567
- return Object.freeze({
1568
- maximumDerivedTuples: positiveBounded(value.maximumDerivedTuples,
1569
- OH_PROJECTION_LIMITS_V1.derivedTuples, "maximumDerivedTuples"),
1570
- maximumProofDepth: positiveBounded(value.maximumProofDepth,
1571
- OH_PROJECTION_LIMITS_V1.proofDepth, "maximumProofDepth"),
1572
- maximumProofNodes: positiveBounded(value.maximumProofNodes,
1573
- OH_PROJECTION_LIMITS_V1.proofNodes, "maximumProofNodes"),
1574
- maximumResultBytes: positiveBounded(value.maximumResultBytes,
1575
- OH_PROJECTION_LIMITS_V1.resultBytes, "maximumResultBytes", 64 * 1024),
1576
- maximumRounds: positiveBounded(value.maximumRounds,
1577
- OH_PROJECTION_LIMITS_V1.rounds, "maximumRounds"),
1578
- maximumTotalProofNodes: positiveBounded(value.maximumTotalProofNodes,
1579
- OH_PROJECTION_LIMITS_V1.totalProofNodes, "maximumTotalProofNodes"),
1580
- maximumWorkUnits: positiveBounded(value.maximumWorkUnits,
1581
- OH_PROJECTION_LIMITS_V1.workUnits, "maximumWorkUnits"),
1582
- });
1583
- }
1584
-
1585
- function resolveProgramsV2(programs: readonly OhMemoryNamedProgramV2[]):
1586
- ReadonlyMap<string, ResolvedMemoryProgramV2> {
1587
- if (!Array.isArray(programs) || programs.length < 1
1588
- || programs.length > OH_MEMORY_LIMITS_V1.maximumPrograms) {
1589
- throw new RangeError("Memory requires a bounded nonempty V2 named program registry.");
1590
- }
1591
- const resolved = new Map<string, ResolvedMemoryProgramV2>();
1592
- for (const candidate of programs) {
1593
- if (!isPlainRecord(candidate) || !hasExactKeys(candidate, ["evaluation", "maximumPageBytes",
1594
- "maximumRows", "pageSize", "parameters", "programId", "purpose", "query", "rulePack", "v"])
1595
- || candidate.v !== 2 || !Array.isArray(candidate.parameters)) {
1596
- throw new TypeError("Invalid V2 named memory program.");
1597
- }
1598
- const programId = safeCode(candidate.programId, 128);
1599
- const purpose = safeCode(candidate.purpose, 256);
1600
- const query = parseOhProjectionQueryV1(candidate.query);
1601
- const rulePack = parseOhProjectionRulePackV1(candidate.rulePack);
1602
- const evaluation = resolveEvaluationV2(candidate.evaluation);
1603
- const maximumRows = positiveBounded(candidate.maximumRows,
1604
- OH_MEMORY_QUERY_LIMITS_V2.maximumProgramRows, "maximumRows");
1605
- const pageSize = positiveBounded(candidate.pageSize,
1606
- Math.min(maximumRows, OH_MEMORY_QUERY_LIMITS_V2.maximumPageRows), "pageSize");
1607
- const maximumPageBytes = positiveBounded(candidate.maximumPageBytes,
1608
- OH_MEMORY_QUERY_LIMITS_V2.maximumPageBytes, "maximumPageBytes",
1609
- OH_MEMORY_QUERY_LIMITS_V2.minimumPageBytes);
1610
- if (programId === null || purpose === null || query === null || rulePack === null
1611
- || resolved.has(programId) || query.limit !== maximumRows
1612
- || candidate.parameters.length > OH_MEMORY_QUERY_LIMITS_V2.bindings) {
1613
- throw new TypeError("Invalid or duplicate V2 named memory program.");
1614
- }
1615
- const parameters = candidate.parameters.map((parameter) => safeCode(parameter, 128)).sort();
1616
- if (parameters.some((parameter) => parameter === null)
1617
- || new Set(parameters).size !== parameters.length) {
1618
- throw new TypeError("A V2 memory program has invalid or duplicate parameters.");
1619
- }
1620
- const queryVariables = new Set(query.where.flatMap((literal) => literal.terms.flatMap((term) =>
1621
- term.kind === "variable" ? [term.name] : [])));
1622
- if ((parameters as readonly string[]).some((parameter) => !queryVariables.has(parameter)
1623
- || query.find.includes(parameter))) {
1624
- throw new TypeError("V2 parameters must be query-body variables that are not projected outputs.");
1625
- }
1626
- const detachedParameters = Object.freeze(parameters as string[]);
1627
- const programPayload = {
1628
- evaluation,
1629
- maximumPageBytes,
1630
- maximumRows,
1631
- pageSize,
1632
- parameters: detachedParameters,
1633
- programId,
1634
- purpose,
1635
- querySha256: query.querySha256,
1636
- rulePackSha256: rulePack.rulePackSha256,
1637
- v: 2 as const,
1638
- };
1639
- const program = immutableClone({ evaluation, maximumPageBytes, maximumRows, pageSize,
1640
- parameters: detachedParameters, programId, programSha256: canonicalSha256(programPayload),
1641
- purpose, query, rulePack, v: 2 as const });
1642
- resolved.set(programId, program);
1643
- }
1644
- return resolved;
1645
- }
1646
-
1647
- function parsePrimitiveBindingV2(value: unknown): JsonPrimitive {
1648
- if (value !== null && typeof value !== "boolean" && typeof value !== "number"
1649
- && typeof value !== "string") throw new TypeError("Memory bindings must be JSON primitives.");
1650
- if (typeof value === "string" && value.length > OH_PROJECTION_LIMITS_V1.atomBytes) {
1651
- throw new RangeError("A memory binding exceeds the projection atom byte bound.");
1652
- }
1653
- if (typeof value === "number" && (!Number.isFinite(value) || Object.is(value, -0))) {
1654
- throw new TypeError("Memory bindings must be canonical finite JSON numbers.");
1655
- }
1656
- const serialized = canonicalJson(value);
1657
- if (utf8ByteLength(serialized) > OH_PROJECTION_LIMITS_V1.atomBytes) {
1658
- throw new RangeError("A memory binding exceeds the projection atom byte bound.");
1659
- }
1660
- return value as JsonPrimitive;
1661
- }
1662
-
1663
- function parseQueryRequestV2(value: unknown): Readonly<{
1664
- bindingsValue: Readonly<Record<string, JsonPrimitive>>;
1665
- continuation: string | null;
1666
- programId: string;
1667
- }> {
1668
- let detached: unknown;
1669
- let detachedCanonical = "";
1670
- let keys: readonly string[];
1671
- try {
1672
- const detachedData = detachCanonicalData(value, "The parameterized memory query",
1673
- OH_MEMORY_QUERY_LIMITS_V2.requestBytes);
1674
- detached = detachedData.value;
1675
- detachedCanonical = detachedData.canonical;
1676
- } catch (error) {
1677
- if (error instanceof RangeError) throw error;
1678
- if (error instanceof Error && error.message.includes("$root.bindings")) {
1679
- throw new TypeError("Memory bindings must be JSON primitives.");
1680
- }
1681
- throw new TypeError("Invalid parameterized memory query.");
1682
- }
1683
- try {
1684
- keys = ownDataKeysV2(detached, 4, "The parameterized memory query");
1685
- } catch (error) {
1686
- if (error instanceof Error && error.message.includes("$root.bindings")) {
1687
- throw new TypeError("Memory bindings must be JSON primitives.");
1688
- }
1689
- throw new TypeError("Invalid parameterized memory query.");
1690
- }
1691
- if (keys.length !== 4 || !["bindings", "continuation", "programId", "v"]
1692
- .every((key) => keys.includes(key))) {
1693
- throw new TypeError("Invalid parameterized memory query.");
1694
- }
1695
- if (utf8ByteLength(detachedCanonical) > OH_MEMORY_QUERY_LIMITS_V2.requestBytes) {
1696
- throw new RangeError("The parameterized memory query exceeds its canonical byte bound.");
1697
- }
1698
- const record = detached as Record<string, unknown>;
1699
- if (record.v !== 2) {
1700
- throw new TypeError("Invalid parameterized memory query.");
1701
- }
1702
- if (record.continuation !== null && typeof record.continuation !== "string") {
1703
- throw new OhMemoryContinuationError("encoding", "Invalid memory continuation encoding.");
1704
- }
1705
- const programId = safeCode(record.programId, 128);
1706
- if (programId === null) throw new TypeError("Invalid parameterized memory query identity.");
1707
- const continuation = record.continuation as string | null;
1708
- if (typeof continuation === "string" && (continuation.length < 1
1709
- || continuation.length > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes
1710
- || utf8ByteLength(continuation) > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes)) {
1711
- throw new OhMemoryContinuationError("encoding",
1712
- "The memory continuation exceeds its byte bound.");
1713
- }
1714
- const bindingKeys = ownDataKeysV2(record.bindings, OH_MEMORY_QUERY_LIMITS_V2.bindings,
1715
- "The parameterized memory query bindings");
1716
- const bindingRecord = record.bindings as Record<string, unknown>;
1717
- const bindings: Record<string, JsonPrimitive> = {};
1718
- for (const key of bindingKeys) {
1719
- if (safeCode(key, 128) === null) throw new TypeError("Invalid memory binding name.");
1720
- bindings[key] = parsePrimitiveBindingV2(bindingRecord[key]);
1721
- }
1722
- const boundedRequest = { bindings, continuation, programId, v: 2 as const };
1723
- if (utf8ByteLength(canonicalJson(boundedRequest)) > OH_MEMORY_QUERY_LIMITS_V2.requestBytes) {
1724
- throw new RangeError("The parameterized memory query exceeds its canonical byte bound.");
1725
- }
1726
- return { bindingsValue: immutableClone(bindings), continuation, programId };
1727
- }
1728
-
1729
- function parseBindingsV2(value: Readonly<Record<string, JsonPrimitive>>,
1730
- parameters: readonly string[]): Readonly<{
1731
- bindings: Readonly<Record<string, JsonPrimitive>>;
1732
- bindingsSha256: Sha256Hex;
1733
- }> {
1734
- if (!isPlainRecord(value) || !hasExactKeys(value, parameters)) {
1735
- throw new TypeError("Memory query bindings must exactly match the host-declared parameters.");
1736
- }
1737
- const bindings: Record<string, JsonPrimitive> = {};
1738
- for (const parameter of parameters) bindings[parameter] = value[parameter]!;
1739
- if (utf8ByteLength(canonicalJson(bindings)) > OH_MEMORY_QUERY_LIMITS_V2.bindingBytes) {
1740
- throw new RangeError("Memory query bindings exceed their canonical byte bound.");
1741
- }
1742
- const detached = immutableClone(bindings);
1743
- return Object.freeze({ bindings: detached,
1744
- bindingsSha256: canonicalSha256({ bindings: detached, parameters, v: 2 }) });
1745
- }
1746
-
1747
- function bindQueryV2(query: OhProjectionQueryV1,
1748
- bindings: Readonly<Record<string, JsonPrimitive>>): OhProjectionQueryV1 {
1749
- const where = query.where.map((literal) => createOhProjectionLiteralV1({
1750
- relation: literal.relation,
1751
- terms: literal.terms.map((term) => term.kind === "variable" && Object.hasOwn(bindings, term.name)
1752
- ? ohProjectionConstantV1(bindings[term.name]!) : term),
1753
- }));
1754
- return createOhProjectionQueryV1({ find: query.find, limit: query.limit,
1755
- queryId: query.queryId, where });
1756
- }
1757
-
1758
- function publicRowV2(row: OhProjectionResultRowV1,
1759
- proofs: readonly OhMemoryProofV1[]): OhMemoryResultRowV2 {
1760
- const lanes = new Set<OhMemoryLaneV1>();
1761
- let unknown = row.proofsTruncated;
1762
- for (const proof of proofs) unknown = collectLanes(proof, lanes) || unknown;
1763
- const premiseLanes = [...lanes].sort() as readonly OhMemoryLaneV1[];
1764
- const premiseAuthority: OhMemoryResultRowV2["premiseAuthority"] = unknown || premiseLanes.length === 0
1765
- ? "unknown" : premiseLanes.includes("working") ? "working" : "canonical";
1766
- const payload = { premiseAuthority, premiseLanes, proofsTruncated: row.proofsTruncated,
1767
- supportCount: row.supportCount, v: 2 as const, values: row.values };
1768
- return Object.freeze({ ...payload, resultRowSha256: canonicalSha256(payload) });
1769
- }
1770
-
1771
- function continuationHmacV2(key: Uint8Array, value: OhMemoryContinuationV2): Buffer {
1772
- return createHmac("sha256", key).update("oh.memory.continuation.v2\0", "utf8")
1773
- .update(canonicalJson(value), "utf8").digest();
1774
- }
1775
-
1776
- function encodeContinuationV2(value: OhMemoryContinuationIdentityV2,
1777
- key: Uint8Array): Readonly<{ continuation: string; continuationSha256: Sha256Hex }> {
1778
- const identity = immutableClone(value);
1779
- const continuationSha256 = canonicalSha256(identity);
1780
- const signed = immutableClone({ ...identity, continuationSha256 });
1781
- const envelope: OhMemoryContinuationEnvelopeV2 = immutableClone({ ...signed,
1782
- continuationHmacSha256: continuationHmacV2(key, signed).toString("hex") as Sha256Hex });
1783
- const continuation = Buffer.from(canonicalJson(envelope), "utf8").toString("base64url");
1784
- if (utf8ByteLength(continuation) > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes) {
1785
- throw new RangeError("The issued memory continuation exceeds its byte bound.");
1786
- }
1787
- return Object.freeze({ continuation, continuationSha256 });
1788
- }
1789
-
1790
- function parseContinuationV2(value: string, key: Uint8Array): OhMemoryContinuationV2 {
1791
- if (value.length < 1 || utf8ByteLength(value) > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes
1792
- || !/^[A-Za-z0-9_-]+$/u.test(value)) {
1793
- throw new OhMemoryContinuationError("encoding", "Invalid memory continuation encoding.");
1794
- }
1795
- const bytes = Buffer.from(value, "base64url");
1796
- if (bytes.toString("base64url") !== value
1797
- || bytes.byteLength > OH_MEMORY_QUERY_LIMITS_V2.continuationBytes) {
1798
- throw new OhMemoryContinuationError("encoding", "Invalid memory continuation encoding.");
1799
- }
1800
- const text = bytes.toString("utf8");
1801
- let decoded: unknown;
1802
- try { decoded = JSON.parse(text); } catch {
1803
- throw new OhMemoryContinuationError("encoding", "Invalid memory continuation JSON.");
1804
- }
1805
- if (!isPlainRecord(decoded)
1806
- || !hasExactKeys(decoded, ["bindingsSha256", "continuationHmacSha256", "continuationSha256",
1807
- "memorySha256", "nextOffset", "pageSize", "programSha256", "projectionResultSha256",
1808
- "totalRows", "v"])
1809
- || decoded.v !== 2) {
1810
- throw new OhMemoryContinuationError("encoding", "Invalid memory continuation payload.");
1811
- }
1812
- const bindingsSha256 = parseSha256Hex(decoded.bindingsSha256);
1813
- const continuationHmacSha256 = parseSha256Hex(decoded.continuationHmacSha256);
1814
- const continuationSha256 = parseSha256Hex(decoded.continuationSha256);
1815
- const memorySha256 = parseSha256Hex(decoded.memorySha256);
1816
- const programSha256 = parseSha256Hex(decoded.programSha256);
1817
- const projectionResultSha256 = parseSha256Hex(decoded.projectionResultSha256);
1818
- if (bindingsSha256 === null || continuationHmacSha256 === null
1819
- || continuationSha256 === null || memorySha256 === null || programSha256 === null
1820
- || projectionResultSha256 === null
1821
- || !Number.isSafeInteger(decoded.nextOffset) || (decoded.nextOffset as number) < 1
1822
- || (decoded.nextOffset as number) > OH_MEMORY_QUERY_LIMITS_V2.maximumProgramRows
1823
- || !Number.isSafeInteger(decoded.pageSize) || (decoded.pageSize as number) < 1
1824
- || (decoded.pageSize as number) > OH_MEMORY_QUERY_LIMITS_V2.maximumPageRows
1825
- || !Number.isSafeInteger(decoded.totalRows) || (decoded.totalRows as number) < 1
1826
- || (decoded.totalRows as number) > OH_MEMORY_QUERY_LIMITS_V2.maximumProgramRows
1827
- || (decoded.nextOffset as number) >= (decoded.totalRows as number)
1828
- || (decoded.nextOffset as number) % (decoded.pageSize as number) !== 0) {
1829
- throw new OhMemoryContinuationError("identity", "Invalid memory continuation identity.");
1830
- }
1831
- const identity: OhMemoryContinuationIdentityV2 = { bindingsSha256, memorySha256,
1832
- nextOffset: decoded.nextOffset as number,
1833
- pageSize: decoded.pageSize as number, programSha256, projectionResultSha256,
1834
- totalRows: decoded.totalRows as number, v: 2 as const };
1835
- const signed: OhMemoryContinuationV2 = { ...identity, continuationSha256 };
1836
- const envelope: OhMemoryContinuationEnvelopeV2 = { ...signed, continuationHmacSha256 };
1837
- if (canonicalJson(envelope) !== text) {
1838
- throw new OhMemoryContinuationError("encoding", "Invalid memory continuation payload.");
1839
- }
1840
- const expectedHmac = continuationHmacV2(key, signed);
1841
- const receivedHmac = Buffer.from(continuationHmacSha256, "hex");
1842
- if (!timingSafeEqual(expectedHmac, receivedHmac)) {
1843
- throw new OhMemoryContinuationError("authentication",
1844
- "The memory continuation is not an issued capability.");
1845
- }
1846
- if (canonicalSha256(identity) !== continuationSha256) {
1847
- throw new OhMemoryContinuationError("identity", "The memory continuation digest is invalid.");
1848
- }
1849
- return Object.freeze(signed);
1850
- }
1851
-
1852
- function parseExplainRequestV2(value: unknown): Readonly<{
1853
- pageRow: number;
1854
- resultSha256: Sha256Hex;
1855
- token: string;
1856
- }> {
1857
- const detached = detachCanonicalData(value, "The V2 memory explanation request",
1858
- OH_MEMORY_QUERY_LIMITS_V2.requestBytes).value;
1859
- if (!isPlainRecord(detached)
1860
- || !hasExactKeys(detached, ["pageRow", "resultSha256", "token", "v"])
1861
- || detached.v !== 2 || typeof detached.token !== "string" || detached.token.length !== 43
1862
- || !Number.isSafeInteger(detached.pageRow) || (detached.pageRow as number) < 0) {
1863
- throw new TypeError("Invalid V2 memory explanation request.");
1864
- }
1865
- const resultSha256 = parseSha256Hex(detached.resultSha256);
1866
- if (resultSha256 === null) throw new TypeError("Invalid V2 memory explanation result identity.");
1867
- return { pageRow: detached.pageRow as number, resultSha256, token: detached.token };
1868
- }
1869
-
1870
- /**
1871
- * Creates the additive V2 memory facade. V2 adds only host-declared primitive
1872
- * bindings and fail-closed stable pagination; V1 request and digest contracts
1873
- * remain untouched.
1874
- */
1875
- async function createOhMemoryAgentV2WithRuntime(
1876
- options: OhMemoryFacadeOptionsV2,
1877
- sharedRuntime?: OhMemoryRuntimeV2,
1878
- ): Promise<OhMemoryAgentV2> {
1879
- const memoryActorId = safeCode(options.actorId, 128);
1880
- if (memoryActorId === null) throw new TypeError("Invalid host-bound memory actor ID.");
1881
- const continuationKey = continuationKeyV2(options.continuationKey);
1882
- const canonicalStore = options.canonical.store;
1883
- const workingStore = options.working.store;
1884
- const workingCodecs = options.working.codecs;
1885
- const canonicalAuthorityId = authorityId(options.canonical.authorityId);
1886
- const workingAuthorityId = authorityId(options.working.authorityId);
1887
- if (canonicalAuthorityId === workingAuthorityId) {
1888
- throw new OhProfileError("Working and canonical memory must be distinct physical authorities.");
1889
- }
1890
- const canonicalBinding = bindingFor(canonicalStore,
1891
- options.canonical.expectedBindingSha256, "canonical");
1892
- const workingBinding = bindingFor(workingStore,
1893
- options.working.expectedBindingSha256, "working");
1894
- const expectedCanonicalHead = parseMemoryAuthorityHead(options.canonical.expectedHead,
1895
- "pinned canonical");
1896
- const programs = resolveProgramsV2(options.programs);
1897
- const extractors = resolveExtractors(options.extractors ?? []);
1898
- const nominationRoutes = resolveNominationRoutes(options.nominationRoutes ?? []);
1899
- const ingress = new OhSemanticBundleIngressV1(
1900
- capacityGuardedWorkingStore(workingStore, workingBinding),
1901
- workingCodecs,
1902
- );
1903
- const runtime = sharedRuntime ?? createOhMemoryRuntimeV2(options);
1904
- const canonical = await readLane({ authorityId: canonicalAuthorityId,
1905
- binding: canonicalBinding, store: canonicalStore }, "canonical", expectedCanonicalHead);
1906
- const wallClock = () => {
1907
- const milliseconds = clockMilliseconds(runtime.now);
1908
- if (milliseconds < runtime.lastWallClockMs) {
1909
- throw new OhProfileError("The memory wall clock regressed.");
1910
- }
1911
- runtime.lastWallClockMs = milliseconds;
1912
- return milliseconds;
1913
- };
1914
- const monotonicClock = () => {
1915
- const milliseconds = monotonicMilliseconds(runtime.monotonicNow);
1916
- if (milliseconds < runtime.lastMonotonicMs) {
1917
- throw new OhProfileError("The memory monotonic clock regressed.");
1918
- }
1919
- runtime.lastMonotonicMs = milliseconds;
1920
- return milliseconds;
1921
- };
1922
- const deleteExplanation = (token: string) => {
1923
- const stored = runtime.explanations.get(token);
1924
- if (stored !== undefined && runtime.explanations.delete(token)) {
1925
- runtime.explanationBytes -= stored.bytes;
1926
- }
1927
- };
1928
-
1929
- const remember = async (value: unknown): Promise<OhMemoryRememberReceiptV1> => {
1930
- const detached = detachCanonicalData(value, "The memory semantic bundle",
1931
- OH_MEMORY_LIMITS_V1.rememberBytes).value;
1932
- if (!isPlainRecord(detached) || !hasExactKeys(detached,
1933
- ["expectedHead", "puts", "requestId", "tombstones", "v"]) || detached.v !== 1) {
1934
- throw new TypeError("Invalid memory remember request.");
1935
- }
1936
- const requestId = safeCode(detached.requestId, 128);
1937
- if (requestId === null) throw new TypeError("Invalid memory remember request identity.");
1938
- const operationId = `memory_${canonicalSha256({ actorId: memoryActorId,
1939
- bindingSha256: workingBinding.bindingSha256, requestId, v: 1 }).slice(0, 48)}`;
1940
- const returnedOperation = await ingress.commit({ actorId: memoryActorId,
1941
- expectedHead: detached.expectedHead, instant: isoInstant(new Date(wallClock())), operationId,
1942
- puts: detached.puts, tombstones: detached.tombstones, v: 1 });
1943
- const operation = parseOhOperationV1(detachCanonicalData(returnedOperation,
1944
- "The returned working memory operation", OH_MEMORY_LIMITS_V1.rememberBytes * 2).value);
1945
- if (operation === null || operation.actorId !== memoryActorId
1946
- || operation.operationId !== operationId || operation.spaceId !== workingBinding.spaceId) {
1947
- throw new OhIntegrityError("The working authority returned a different memory operation.");
1948
- }
1949
- const head: OhHeadV1 = { generation: operation.sequence,
1950
- graphRevisionSha256: operation.graphRevisionSha256,
1951
- operationSha256: operation.operationSha256, recordsSha256: operation.recordsSha256,
1952
- sequence: operation.sequence, v: 1 };
1953
- const payload = { actorId: operation.actorId, authorityId: workingAuthorityId,
1954
- bindingSha256: workingBinding.bindingSha256, head, instant: operation.instant,
1955
- lane: "working" as const, operationSha256: operation.operationSha256, requestId,
1956
- status: "committed" as const, v: 1 as const };
1957
- return immutableClone({ ...payload, receiptSha256: canonicalSha256(payload) });
1958
- };
1959
-
1960
- const query = async (value: unknown): Promise<OhMemoryQueryResultV2> => {
1961
- const request = parseQueryRequestV2(value);
1962
- const program = programs.get(request.programId);
1963
- if (program === undefined) throw new TypeError("Unknown named V2 memory program.");
1964
- const bound = parseBindingsV2(request.bindingsValue, program.parameters);
1965
- const requestedContinuation = request.continuation === null
1966
- ? null : parseContinuationV2(request.continuation, continuationKey);
1967
- if (requestedContinuation !== null
1968
- && (requestedContinuation.bindingsSha256 !== bound.bindingsSha256
1969
- || requestedContinuation.pageSize !== program.pageSize
1970
- || requestedContinuation.programSha256 !== program.programSha256
1971
- || requestedContinuation.totalRows > program.maximumRows
1972
- || requestedContinuation.nextOffset >= requestedContinuation.totalRows
1973
- || requestedContinuation.nextOffset % program.pageSize !== 0)) {
1974
- throw new OhMemoryContinuationError("identity",
1975
- "The memory continuation does not match this exact program, binding, and page identity.");
1976
- }
1977
- const boundQuery = bindQueryV2(program.query, bound.bindings);
1978
- const working = await readLane({ authorityId: workingAuthorityId,
1979
- binding: workingBinding, store: workingStore }, "working");
1980
- const composite = createCompositeDataset(canonical, working, extractors);
1981
- const projection = evaluateOhProjectionV1({ dataset: composite.dataset,
1982
- options: program.evaluation, query: boundQuery, rulePack: program.rulePack,
1983
- snapshot: composite.snapshot });
1984
- if (projection.stats.truncated) {
1985
- const reasons = projection.stats.truncationReasons.join(", ");
1986
- throw new RangeError(`The V2 memory projection was truncated (${reasons}); no page was returned.`);
1987
- }
1988
- if (projection.rows.length > program.maximumRows) {
1989
- throw new RangeError("The V2 memory projection exceeds its host-declared row bound.");
1990
- }
1991
- const identityPayload = {
1992
- bindings: bound.bindings,
1993
- bindingsSha256: bound.bindingsSha256,
1994
- boundQuerySha256: boundQuery.querySha256,
1995
- canonical: laneIdentity(canonical),
1996
- compositeDatasetSha256: composite.dataset.datasetSha256,
1997
- conflictPolicy: OH_MEMORY_CONFLICT_POLICY_V1,
1998
- evaluationSha256: projection.identity.evaluationSha256,
1999
- programId: program.programId,
2000
- programSha256: program.programSha256,
2001
- projectionSha256: projection.identity.projectionSha256,
2002
- purpose: program.purpose,
2003
- rulePackSha256: program.rulePack.rulePackSha256,
2004
- templateQuerySha256: program.query.querySha256,
2005
- v: 2 as const,
2006
- working: laneIdentity(working),
2007
- };
2008
- const identity: OhMemoryIdentityV2 = immutableClone({ ...identityPayload,
2009
- memorySha256: canonicalSha256(identityPayload) });
2010
- if (requestedContinuation !== null
2011
- && (requestedContinuation.memorySha256 !== identity.memorySha256
2012
- || requestedContinuation.projectionResultSha256 !== projection.resultSha256)) {
2013
- throw new OhMemoryContinuationError("identity",
2014
- "The memory continuation does not match this exact source and projection identity.");
2015
- }
2016
- if (requestedContinuation !== null
2017
- && (requestedContinuation.totalRows !== projection.rows.length
2018
- || requestedContinuation.nextOffset >= projection.rows.length
2019
- || requestedContinuation.nextOffset % program.pageSize !== 0)) {
2020
- throw new OhMemoryContinuationError("identity",
2021
- "The memory continuation does not match this exact row identity.");
2022
- }
2023
- const start = requestedContinuation?.nextOffset ?? 0;
2024
- const endExclusive = Math.min(start + program.pageSize, projection.rows.length);
2025
- const projectionRows = projection.rows.slice(start, endExclusive);
2026
- const proofs = immutableClone(projectionRows.map((row) => row.proofs.map((proof) =>
2027
- mapProof(proof, composite.sources, composite.factPolicies))));
2028
- const rows = immutableClone(projectionRows.map((row, index) => publicRowV2(row, proofs[index]!)));
2029
- const hasMore = endExclusive < projection.rows.length;
2030
- const page: OhMemoryPageV2 = immutableClone({ completeness: hasMore ? "partial" : "complete",
2031
- endExclusive, hasMore, maximumPageBytes: program.maximumPageBytes, pageSize: program.pageSize,
2032
- returnedRows: rows.length, start, totalRows: projection.rows.length,
2033
- truncation: { reasons: [], truncated: false, v: 2 as const }, v: 2 as const });
2034
- const issuedContinuation = hasMore ? encodeContinuationV2({ bindingsSha256: bound.bindingsSha256,
2035
- memorySha256: identity.memorySha256, nextOffset: endExclusive, pageSize: program.pageSize,
2036
- programSha256: program.programSha256, projectionResultSha256: projection.resultSha256,
2037
- totalRows: projection.rows.length, v: 2 }, continuationKey) : null;
2038
- const continuation = issuedContinuation?.continuation ?? null;
2039
- const continuationSha256 = issuedContinuation?.continuationSha256 ?? null;
2040
- const conflicts = immutableClone({ count: composite.conflicts.length,
2041
- conflictsSha256: canonicalSha256(composite.conflicts), v: 2 as const });
2042
- const resultIdentityPayload = immutableClone({ authority: "derived" as const, conflicts,
2043
- continuationSha256,
2044
- identity, page, projectionResultSha256: projection.resultSha256, rows, v: 2 as const });
2045
- const resultSha256 = canonicalSha256(resultIdentityPayload);
2046
- const resultPayload = immutableClone({ ...resultIdentityPayload, continuation });
2047
- const issuedAt = wallClock();
2048
- const issuedAtMonotonic = monotonicClock();
2049
- const expiresAtMs = issuedAt + runtime.capabilityLifetime;
2050
- const expiresAtMonotonicMs = issuedAtMonotonic + runtime.capabilityLifetime;
2051
- const expiresAt = isoInstant(new Date(expiresAtMs));
2052
- const pageBytePreflight = { ...resultPayload,
2053
- explainCapability: { expiresAt, token: "A".repeat(43), v: 2 as const }, resultSha256 };
2054
- if (utf8ByteLength(canonicalJson(pageBytePreflight)) > program.maximumPageBytes) {
2055
- throw new RangeError("The V2 memory page exceeds its host-declared canonical byte bound.");
2056
- }
2057
- for (const [existingToken, stored] of runtime.explanations) {
2058
- if (issuedAtMonotonic >= stored.expiresAtMonotonicMs) deleteExplanation(existingToken);
2059
- }
2060
- const storedPayload = immutableClone({ expiresAtMonotonicMs, identity, page, proofs,
2061
- resultSha256, rows });
2062
- const storedBytes = utf8ByteLength(canonicalJson(storedPayload)) + 128;
2063
- if (storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityEntryBytes
2064
- || storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
2065
- throw new RangeError("The V2 memory explanation exceeds its retained capability bound.");
2066
- }
2067
- while (runtime.explanations.size >= OH_MEMORY_LIMITS_V1.explainCapabilities
2068
- || runtime.explanationBytes + storedBytes > OH_MEMORY_LIMITS_V1.explainCapabilityTotalBytes) {
2069
- const oldest = runtime.explanations.keys().next().value as string | undefined;
2070
- if (oldest === undefined) break;
2071
- deleteExplanation(oldest);
2072
- }
2073
- let token = randomBytes(32).toString("base64url");
2074
- while (runtime.explanations.has(token)) token = randomBytes(32).toString("base64url");
2075
- runtime.explanations.set(token, immutableClone({ ...storedPayload, bytes: storedBytes }));
2076
- runtime.explanationBytes += storedBytes;
2077
- const result = immutableClone({ ...resultPayload,
2078
- explainCapability: { expiresAt, token, v: 2 as const }, resultSha256 });
2079
- if (utf8ByteLength(canonicalJson(result)) > program.maximumPageBytes) {
2080
- deleteExplanation(token);
2081
- throw new RangeError("The V2 memory page exceeds its host-declared canonical byte bound.");
2082
- }
2083
- return result;
2084
- };
2085
-
2086
- const explain = async (value: unknown): Promise<OhMemoryExplanationV2> => {
2087
- const request = parseExplainRequestV2(value);
2088
- const stored = runtime.explanations.get(request.token);
2089
- const currentTime = monotonicClock();
2090
- if (stored === undefined || stored.resultSha256 !== request.resultSha256
2091
- || currentTime >= stored.expiresAtMonotonicMs) {
2092
- deleteExplanation(request.token);
2093
- throw new OhProfileError("The V2 memory explanation capability is absent, expired, or misbound.");
2094
- }
2095
- const row = stored.rows[request.pageRow];
2096
- const proofs = stored.proofs[request.pageRow];
2097
- if (row === undefined || proofs === undefined) throw new RangeError("The explanation page row is out of bounds.");
2098
- const payload = { authority: "derived" as const, identity: stored.identity, page: stored.page,
2099
- pageRow: request.pageRow, premiseAuthority: row.premiseAuthority,
2100
- premiseLanes: row.premiseLanes, proofs, proofsTruncated: row.proofsTruncated,
2101
- resultRowSha256: row.resultRowSha256, resultSha256: stored.resultSha256,
2102
- supportCount: row.supportCount, v: 2 as const, values: row.values };
2103
- return immutableClone({ ...payload, explanationSha256: canonicalSha256(payload) });
2104
- };
2105
-
2106
- const nominate = async (value: unknown): Promise<OhMemoryNominationV1> => {
2107
- const request = parseNominationRequest(value);
2108
- const route = nominationRoutes.get(request.nominationId);
2109
- if (route === undefined) throw new TypeError("Unknown named memory nomination route.");
2110
- const head = parseOhHeadV1(detachCanonicalData(await workingStore.head(),
2111
- "The working nomination store head", 4 * 1024).value);
2112
- if (head === null) throw new OhIntegrityError("The working nomination store returned an invalid head.");
2113
- const returnedClosure = await workingStore.exportDependencyClosure({ head: {
2114
- operationSha256: head.operationSha256, sequence: head.sequence }, roots: request.roots });
2115
- const closure = detachCanonicalData(returnedClosure, "The working nomination closure",
2116
- OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes).value;
2117
- const verified = verifyOhDependencyClosureAgainstV1(closure, { binding: workingBinding, head });
2118
- if (!verified.ok) throw new OhIntegrityError("The working nomination closure failed exact verification.");
2119
- if (canonicalJson(verified.closure.roots) !== canonicalJson(request.roots)) {
2120
- throw new OhIntegrityError("The working nomination closure substituted different roots.");
2121
- }
2122
- const source = Object.freeze({ authorityId: workingAuthorityId,
2123
- bindingSha256: workingBinding.bindingSha256, head, lane: "working" as const, v: 1 as const });
2124
- const payload = { closure: verified.closure, destinationPurpose: route.destinationPurpose,
2125
- nominationId: route.nominationId, source, status: "prepared" as const, v: 1 as const };
2126
- return immutableClone({ ...payload, nominationSha256: canonicalSha256(payload) });
2127
- };
2128
-
2129
- return Object.freeze({ explain, nominate, query, remember });
2130
- }
2131
-
2132
- export async function createOhMemoryAgentV2(
2133
- options: OhMemoryFacadeOptionsV2,
2134
- ): Promise<OhMemoryAgentV2> {
2135
- return await createOhMemoryAgentV2WithRuntime(options);
2136
- }
2137
-
2138
- function parseDetachedMemoryAuthorityHead(value: unknown, label: string): OhHeadV1 {
2139
- const head = parseOhHeadV1(value);
2140
- if (head === null) throw new TypeError(`Invalid ${label} memory head.`);
2141
- return immutableClone(head);
2142
- }
2143
-
2144
- function parseMemoryAuthorityHead(value: unknown, label: string): OhHeadV1 {
2145
- return parseDetachedMemoryAuthorityHead(detachCanonicalData(value,
2146
- `The ${label} memory head`, 4 * 1024).value, label);
2147
- }
2148
-
2149
- function parseCanonicalAdvanceRequest(value: unknown): Readonly<{
2150
- expectedHead: OhHeadV1;
2151
- nextHead: OhHeadV1;
2152
- }> {
2153
- const detached = detachCanonicalData(value, "The canonical memory advance request",
2154
- OH_MEMORY_QUERY_LIMITS_V2.requestBytes).value;
2155
- if (!isPlainRecord(detached) || !hasExactKeys(detached, ["expectedHead", "nextHead", "v"])
2156
- || detached.v !== 1) throw new TypeError("Invalid canonical memory advance request.");
2157
- return Object.freeze({
2158
- expectedHead: parseDetachedMemoryAuthorityHead(detached.expectedHead, "expected canonical"),
2159
- nextHead: parseDetachedMemoryAuthorityHead(detached.nextHead, "next canonical"),
2160
- });
2161
- }
2162
-
2163
- function parseAdoptionRequest(value: unknown): Readonly<{
2164
- expectedCanonicalHead: OhHeadV1;
2165
- nomination: OhMemoryNominationV1;
2166
- replacements: readonly OhMemoryAdoptionReplacementV1[];
2167
- }> {
2168
- const detached = detachCanonicalData(value, "The memory adoption request",
2169
- OH_MEMORY_AUTHORITY_LIMITS_V1.adoptionRequestBytes).value;
2170
- if (!isPlainRecord(detached)
2171
- || (!hasExactKeys(detached, ["expectedCanonicalHead", "nomination", "v"])
2172
- && !hasExactKeys(detached, ["expectedCanonicalHead", "nomination", "replacements", "v"]))
2173
- || detached.v !== 1) throw new TypeError("Invalid memory adoption request.");
2174
- const nomination = parseDetachedMemoryNominationV1(detached.nomination);
2175
- if (nomination === null) throw new TypeError("Invalid memory adoption nomination.");
2176
- const replacementValues = "replacements" in detached ? detached.replacements : [];
2177
- if (!Array.isArray(replacementValues)
2178
- || replacementValues.length > OH_MEMORY_AUTHORITY_LIMITS_V1.adoptionReplacements) {
2179
- throw new TypeError("Invalid memory adoption replacements.");
2180
- }
2181
- const nominatedByKey = new Map(nomination.closure.records.map((record) => [record.key, record]));
2182
- const replacements = replacementValues.map((replacement) => {
2183
- if (!isPlainRecord(replacement)
2184
- || !hasExactKeys(replacement, ["expectedPriorRecordSha256", "key", "v"])
2185
- || replacement.v !== 1) throw new TypeError("Invalid memory adoption replacement.");
2186
- const key = safeCode(replacement.key, 512);
2187
- const expectedPriorRecordSha256 = parseSha256Hex(replacement.expectedPriorRecordSha256);
2188
- const nominated = key === null ? undefined : nominatedByKey.get(key);
2189
- if (key === null || expectedPriorRecordSha256 === null || nominated === undefined) {
2190
- throw new TypeError("Invalid memory adoption replacement.");
2191
- }
2192
- return { expectedPriorRecordSha256, key, v: 1 as const };
2193
- }).sort((left, right) => compareText(left.key, right.key));
2194
- if (!orderedUnique(replacements, (replacement) => replacement.key)) {
2195
- throw new TypeError("Memory adoption replacement keys must be unique.");
2196
- }
2197
- return Object.freeze({
2198
- expectedCanonicalHead: parseDetachedMemoryAuthorityHead(detached.expectedCanonicalHead,
2199
- "expected canonical adoption"),
2200
- nomination,
2201
- replacements: immutableClone(replacements),
2202
- });
2203
- }
2204
-
2205
- function headRef(head: OhHeadV1): OhHeadRefV1 {
2206
- return Object.freeze({ operationSha256: head.operationSha256, sequence: head.sequence });
2207
- }
2208
-
2209
- async function proveCanonicalDescendant(
2210
- authority: Readonly<{ authorityId: string; binding: OhStoreBindingV1; store: OhStoreV1 }>,
2211
- priorHead: OhHeadV1,
2212
- nextHead: OhHeadV1,
2213
- requiredFirstHead?: OhHeadV1,
2214
- ): Promise<LaneSnapshot> {
2215
- if (nextHead.sequence <= priorHead.sequence) {
2216
- throw new OhConflictError("The next canonical memory head is not a descendant of the current pin.");
2217
- }
2218
- const distance = nextHead.sequence - priorHead.sequence;
2219
- if (distance > OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalAdvanceOperations
2220
- || Math.ceil(distance / OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPage)
2221
- > OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalAdvancePages) {
2222
- throw new RangeError("The canonical memory advance exceeds its total proof bound; advance in host-reviewed chunks.");
2223
- }
2224
- const through = headRef(nextHead);
2225
- let cursor = headRef(priorHead);
2226
- let pageCount = 0;
2227
- let reachedHead: OhHeadV1 | null = null;
2228
- let firstHead: OhHeadV1 | null = null;
2229
- while (cursor.sequence < through.sequence) {
2230
- if (pageCount >= OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalAdvancePages) {
2231
- throw new RangeError("The canonical memory advance exceeded its page proof bound; advance in host-reviewed chunks.");
2232
- }
2233
- pageCount += 1;
2234
- const remaining = through.sequence - cursor.sequence;
2235
- const limit = Math.min(remaining, OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPage);
2236
- const returnedData = detachCanonicalData(
2237
- await authority.store.changesSince(cursor, { limit, through }),
2238
- "The canonical change-feed page",
2239
- OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPageBytes,
2240
- ).value;
2241
- const returned = returnedData as Record<string, unknown>;
2242
- if (!isPlainRecord(returned) || !hasExactKeys(returned,
2243
- ["from", "hasMore", "operations", "through", "to", "v"])
2244
- || returned.v !== 1 || typeof returned.hasMore !== "boolean"
2245
- || !Array.isArray(returned.operations) || returned.operations.length > limit) {
2246
- throw new OhIntegrityError("The canonical change feed returned an invalid page envelope.");
2247
- }
2248
- const from = parseOhHeadRefV1(returned.from);
2249
- const returnedThrough = parseOhHeadV1(returned.through);
2250
- const returnedTo = parseOhHeadRefV1(returned.to);
2251
- if (from === null || returnedThrough === null || returnedTo === null
2252
- || canonicalJson(from) !== canonicalJson(cursor)
2253
- || !exactHead(returnedThrough, nextHead)) {
2254
- throw new OhIntegrityError("The canonical change feed changed its pinned bounds.");
2255
- }
2256
- let reached = cursor;
2257
- for (const value of returned.operations) {
2258
- const operation = parseOhOperationV1(value);
2259
- if (operation === null || operation.spaceId !== authority.binding.spaceId
2260
- || operation.sequence !== reached.sequence + 1
2261
- || operation.parentOperationSha256 !== reached.operationSha256) {
2262
- throw new OhIntegrityError("The canonical change feed contains a gap or different authority.");
2263
- }
2264
- reached = Object.freeze({ operationSha256: operation.operationSha256,
2265
- sequence: operation.sequence });
2266
- reachedHead = immutableClone({ generation: operation.sequence,
2267
- graphRevisionSha256: operation.graphRevisionSha256,
2268
- operationSha256: operation.operationSha256, recordsSha256: operation.recordsSha256,
2269
- sequence: operation.sequence, v: 1 });
2270
- firstHead ??= reachedHead;
2271
- }
2272
- if (canonicalJson(reached) !== canonicalJson(returnedTo)
2273
- || (returned.hasMore && returned.operations.length === 0)
2274
- || (returned.hasMore && reached.sequence >= through.sequence)
2275
- || reached.sequence > through.sequence
2276
- || (!returned.hasMore && canonicalJson(reached) !== canonicalJson(through))) {
2277
- throw new OhIntegrityError("The canonical change feed did not prove the requested descendant.");
2278
- }
2279
- cursor = reached;
2280
- }
2281
- if (reachedHead === null || !exactHead(reachedHead, nextHead)) {
2282
- throw new OhIntegrityError("The canonical change feed did not prove the requested full head.");
2283
- }
2284
- if (requiredFirstHead !== undefined
2285
- && (firstHead === null || !exactHead(firstHead, requiredFirstHead))) {
2286
- throw new OhIntegrityError("The returned adoption operation is not on the current canonical path.");
2287
- }
2288
- return await readLane(authority, "canonical", nextHead);
2289
- }
2290
-
2291
- function canonicalAdvanceReceipt(
2292
- authorityIdValue: string,
2293
- bindingSha256: Sha256Hex,
2294
- priorHead: OhHeadV1,
2295
- head: OhHeadV1,
2296
- status: OhMemoryCanonicalAdvanceReceiptV1["status"],
2297
- ): OhMemoryCanonicalAdvanceReceiptV1 {
2298
- const payload = { authorityId: authorityIdValue, bindingSha256, head, priorHead, status,
2299
- v: 1 as const };
2300
- return immutableClone({ ...payload, receiptSha256: canonicalSha256(payload) });
2301
- }
2302
-
2303
- function adoptionReceipt(
2304
- actorId: string,
2305
- authorityIdValue: string,
2306
- bindingSha256: Sha256Hex,
2307
- nominationSha256: Sha256Hex,
2308
- operationSha256: Sha256Hex | null,
2309
- priorHead: OhHeadV1,
2310
- head: OhHeadV1,
2311
- status: OhMemoryAdoptionReceiptV1["status"],
2312
- ): OhMemoryAdoptionReceiptV1 {
2313
- const payload = { actorId, authorityId: authorityIdValue, bindingSha256, head,
2314
- nominationSha256, operationSha256, priorHead, status, v: 1 as const };
2315
- return immutableClone({ ...payload, receiptSha256: canonicalSha256(payload) });
2316
- }
2317
-
2318
- function adoptionDifferences(
2319
- snapshot: OhSnapshotV1,
2320
- records: readonly KnowledgeGraphRecordV1[],
2321
- ): readonly OhMemoryAdoptionConflictEntryV1[] {
2322
- const canonicalByKey = new Map(snapshot.records.map((record) => [record.key, record]));
2323
- return immutableClone(records.flatMap((record) => {
2324
- const canonicalRecord = canonicalByKey.get(record.key);
2325
- return canonicalRecord?.recordSha256 === record.recordSha256 ? [] : [{
2326
- canonicalRecordSha256: canonicalRecord?.recordSha256 ?? null,
2327
- key: record.key,
2328
- nominatedRecordSha256: record.recordSha256,
2329
- v: 1 as const,
2330
- }];
2331
- }).sort((left, right) => compareText(left.key, right.key)));
2332
- }
2333
-
2334
- function unauthorizedAdoptionDifferences(
2335
- reviewedSnapshot: OhSnapshotV1,
2336
- currentSnapshot: OhSnapshotV1,
2337
- records: readonly KnowledgeGraphRecordV1[],
2338
- replacements: readonly OhMemoryAdoptionReplacementV1[],
2339
- ): readonly OhMemoryAdoptionConflictEntryV1[] {
2340
- const reviewedByKey = new Map(reviewedSnapshot.records.map((record) => [record.key, record]));
2341
- const currentByKey = new Map(currentSnapshot.records.map((record) => [record.key, record]));
2342
- const replacementByKey = new Map(replacements.map((replacement) => [replacement.key,
2343
- replacement.expectedPriorRecordSha256]));
2344
- const conflicts = records.flatMap((nominated) => {
2345
- const reviewed = reviewedByKey.get(nominated.key);
2346
- const expectedPriorRecordSha256 = replacementByKey.get(nominated.key);
2347
- const alreadyEqual = reviewed?.recordSha256 === nominated.recordSha256;
2348
- const authorized = reviewed === undefined
2349
- ? expectedPriorRecordSha256 === undefined
2350
- : alreadyEqual
2351
- ? expectedPriorRecordSha256 === undefined
2352
- || expectedPriorRecordSha256 === reviewed.recordSha256
2353
- : expectedPriorRecordSha256 === reviewed.recordSha256;
2354
- if (authorized) return [];
2355
- return [{
2356
- canonicalRecordSha256: currentByKey.get(nominated.key)?.recordSha256 ?? null,
2357
- key: nominated.key,
2358
- nominatedRecordSha256: nominated.recordSha256,
2359
- v: 1 as const,
2360
- }];
2361
- });
2362
- return immutableClone(conflicts
2363
- .sort((left, right) => compareText(left.key, right.key)));
2364
- }
2365
-
2366
- async function assertWorkingCommitCapacity(
2367
- store: OhStoreV1,
2368
- binding: OhStoreBindingV1,
2369
- changes: readonly KnowledgeGraphChangeV1[],
2370
- head: OhHeadV1,
2371
- ): Promise<void> {
2372
- const returnedSnapshot = await store.snapshot({
2373
- head: { operationSha256: head.operationSha256, sequence: head.sequence },
2374
- maximumRecords: OH_MEMORY_LIMITS_V1.maximumRecordsPerLane,
2375
- });
2376
- const { snapshot } = parseDetachedStoreSnapshot(returnedSnapshot,
2377
- "The working capacity store", head, binding.spaceId);
2378
- const recordsByKey = new Map(snapshot.records.map((record) => [record.key, record]));
2379
- for (const change of canonicalKnowledgeGraphChangesV1(changes)) {
2380
- if (change.kind === "put") recordsByKey.set(change.record.key, change.record);
2381
- else recordsByKey.delete(change.key);
2382
- }
2383
- if (recordsByKey.size > OH_MEMORY_LIMITS_V1.maximumRecordsPerLane) {
2384
- throw new RangeError("The remembered working memory would exceed its record snapshot bound.");
2385
- }
2386
- const nextSequence = snapshot.head.sequence + 1;
2387
- if (!Number.isSafeInteger(nextSequence)) {
2388
- throw new RangeError("The remembered working memory would exceed its head sequence bound.");
2389
- }
2390
- const records = [...recordsByKey.values()]
2391
- .sort((left, right) => compareText(left.key, right.key));
2392
- const placeholderDigest = canonicalSha256({ kind: "oh.memory.remember-capacity", v: 1 });
2393
- const prospective: OhSnapshotV1 = {
2394
- head: {
2395
- generation: nextSequence,
2396
- graphRevisionSha256: placeholderDigest,
2397
- operationSha256: placeholderDigest,
2398
- recordsSha256: canonicalSha256(records.map(knowledgeGraphRecordRefV1)),
2399
- sequence: nextSequence,
2400
- v: 1,
2401
- },
2402
- records,
2403
- v: 1,
2404
- };
2405
- if (utf8ByteLength(canonicalJson(prospective)) > OH_MEMORY_LIMITS_V1.snapshotBytesPerLane) {
2406
- throw new RangeError("The remembered working memory would exceed its snapshot byte bound.");
2407
- }
2408
- }
2409
-
2410
- function capacityGuardedWorkingStore(store: OhStoreV1, binding: OhStoreBindingV1): OhStoreV1 {
2411
- const guarded: OhStoreV1 = {
2412
- binding,
2413
- changesSince: async (from, options) => await store.changesSince(from, options),
2414
- close: async () => await store.close(),
2415
- commit: async (input) => {
2416
- const current = parseDetachedStoreHead(await store.head(),
2417
- "The working capacity store");
2418
- // The store owns exact operation-id replay. A stale expected head may
2419
- // therefore be either a harmless replay or a conflict. Delegate it
2420
- // unchanged so a hypothetical reapplication cannot reject a replay.
2421
- if (
2422
- current.generation === input.expectedHead.generation
2423
- && current.operationSha256 === input.expectedHead.operationSha256
2424
- ) await assertWorkingCommitCapacity(store, binding, input.changes, current);
2425
- return await store.commit(input);
2426
- },
2427
- exportDependencyClosure: async (input) => await store.exportDependencyClosure(input),
2428
- head: async () => await store.head(),
2429
- snapshot: async (options) => await store.snapshot(options),
2430
- verify: async () => await store.verify(),
2431
- };
2432
- return Object.freeze(guarded);
2433
- }
2434
-
2435
- function assertAdoptionSnapshotCapacity(
2436
- snapshot: OhSnapshotV1,
2437
- changedRecords: readonly KnowledgeGraphRecordV1[],
2438
- ): void {
2439
- const recordsByKey = new Map(snapshot.records.map((record) => [record.key, record]));
2440
- for (const record of changedRecords) recordsByKey.set(record.key, record);
2441
- if (recordsByKey.size > OH_MEMORY_LIMITS_V1.maximumRecordsPerLane) {
2442
- throw new RangeError("The adopted canonical memory would exceed its record snapshot bound.");
2443
- }
2444
- const nextSequence = snapshot.head.sequence + 1;
2445
- if (!Number.isSafeInteger(nextSequence)) {
2446
- throw new RangeError("The adopted canonical memory would exceed its head sequence bound.");
2447
- }
2448
- const records = [...recordsByKey.values()]
2449
- .sort((left, right) => compareText(left.key, right.key));
2450
- const placeholderDigest = canonicalSha256({ kind: "oh.memory.adoption-capacity", v: 1 });
2451
- const prospective: OhSnapshotV1 = {
2452
- head: {
2453
- generation: nextSequence,
2454
- graphRevisionSha256: placeholderDigest,
2455
- operationSha256: placeholderDigest,
2456
- recordsSha256: canonicalSha256(records.map(knowledgeGraphRecordRefV1)),
2457
- sequence: nextSequence,
2458
- v: 1,
2459
- },
2460
- records,
2461
- v: 1,
2462
- };
2463
- if (utf8ByteLength(canonicalJson(prospective)) > OH_MEMORY_LIMITS_V1.snapshotBytesPerLane) {
2464
- throw new RangeError("The adopted canonical memory would exceed its canonical snapshot byte bound.");
2465
- }
2466
- }
2467
-
2468
- function adoptionConflict(
2469
- expectedHead: OhHeadV1,
2470
- actualHead: OhHeadV1,
2471
- completeConflicts: readonly OhMemoryAdoptionConflictEntryV1[],
2472
- ): OhMemoryAdoptionConflictError {
2473
- const sorted = immutableClone([...completeConflicts]
2474
- .sort((left, right) => compareText(left.key, right.key)));
2475
- const conflicts = immutableClone(sorted.slice(0,
2476
- OH_MEMORY_AUTHORITY_LIMITS_V1.reportedAdoptionConflicts));
2477
- const conflict = immutableClone({ actualHead, conflicts,
2478
- conflictsSha256: canonicalSha256({ conflicts: sorted, v: 1 }), expectedHead,
2479
- reportedConflicts: conflicts.length, totalConflicts: sorted.length,
2480
- truncated: conflicts.length !== sorted.length, v: 1 as const });
2481
- return new OhMemoryAdoptionConflictError(conflict);
2482
- }
2483
-
2484
- /**
2485
- * Creates the stable two-lane memory boundary. The agent object carries no
2486
- * canonical mutation handle; trusted host code retains serialized rollover and
2487
- * reviewed adoption controls separately.
2488
- */
2489
- export async function createOhMemoryAuthorityV1(
2490
- options: OhMemoryAuthorityOptionsV1,
2491
- ): Promise<OhMemoryAuthorityV1> {
2492
- const maximumCanonicalOperationBytes = options.maximumCanonicalOperationBytes
2493
- ?? OH_OPERATION_MAX_BYTES_V1;
2494
- if (!Number.isSafeInteger(maximumCanonicalOperationBytes)
2495
- || maximumCanonicalOperationBytes < 1
2496
- || maximumCanonicalOperationBytes > OH_OPERATION_MAX_BYTES_V1) {
2497
- throw new TypeError("Invalid canonical memory operation byte bound.");
2498
- }
2499
- const memoryActorId = safeCode(options.actorId, 128);
2500
- const adoptionActorId = safeCode(options.adoptionActorId, 128);
2501
- if (memoryActorId === null || adoptionActorId === null) {
2502
- throw new TypeError("Invalid host-bound memory authority actor ID.");
2503
- }
2504
- const canonicalStore = options.canonical.store;
2505
- const workingStore = options.working.store;
2506
- const canonicalAuthorityId = authorityId(options.canonical.authorityId);
2507
- const workingAuthorityId = authorityId(options.working.authorityId);
2508
- if (canonicalAuthorityId === workingAuthorityId) {
2509
- throw new OhProfileError("Working and canonical memory must be distinct physical authorities.");
2510
- }
2511
- const canonicalBinding = bindingFor(canonicalStore,
2512
- options.canonical.expectedBindingSha256, "canonical");
2513
- const workingBinding = bindingFor(workingStore,
2514
- options.working.expectedBindingSha256, "working");
2515
- const initialCanonicalHead = parseMemoryAuthorityHead(options.canonical.expectedHead,
2516
- "initial canonical");
2517
- const workingCodecs = options.working.codecs;
2518
- const explainCapabilityLifetimeMs = options.explainCapabilityLifetimeMs;
2519
- const monotonicNow = options.monotonicNow;
2520
- const now = options.now;
2521
- const continuationKey = continuationKeyV2(options.continuationKey);
2522
- const programs = Object.freeze([...resolveProgramsV2(options.programs).values()].map((program) =>
2523
- immutableClone({ evaluation: program.evaluation, maximumPageBytes: program.maximumPageBytes,
2524
- maximumRows: program.maximumRows, pageSize: program.pageSize, parameters: program.parameters,
2525
- programId: program.programId, purpose: program.purpose, query: program.query,
2526
- rulePack: program.rulePack, v: 2 as const })));
2527
- const extractors = resolveExtractors(options.extractors ?? []);
2528
- const nominationRoutes = Object.freeze([...resolveNominationRoutes(options.nominationRoutes ?? []).values()]);
2529
- const routesById = new Map(nominationRoutes.map((route) => [route.nominationId, route]));
2530
- const runtime = createOhMemoryRuntimeV2(options);
2531
-
2532
- const createAgentAt = async (expectedHead: OhHeadV1) => await createOhMemoryAgentV2WithRuntime({
2533
- actorId: memoryActorId,
2534
- canonical: { authorityId: canonicalAuthorityId,
2535
- expectedBindingSha256: canonicalBinding.bindingSha256, expectedHead, store: canonicalStore },
2536
- continuationKey,
2537
- ...(explainCapabilityLifetimeMs === undefined ? {} : {
2538
- explainCapabilityLifetimeMs,
2539
- }),
2540
- extractors,
2541
- ...(monotonicNow === undefined ? {} : { monotonicNow }),
2542
- nominationRoutes,
2543
- ...(now === undefined ? {} : { now }),
2544
- programs,
2545
- working: { authorityId: workingAuthorityId, codecs: workingCodecs,
2546
- expectedBindingSha256: workingBinding.bindingSha256, store: workingStore },
2547
- }, runtime);
2548
-
2549
- let activeCanonicalHead = initialCanonicalHead;
2550
- let activeAgent = await createAgentAt(activeCanonicalHead);
2551
- const agent: OhMemoryAgentV2 = Object.freeze({
2552
- async explain(value: unknown) {
2553
- const selected = activeAgent;
2554
- return await selected.explain(value);
2555
- },
2556
- nominate(value: unknown) {
2557
- const selected = activeAgent;
2558
- return selected.nominate(value);
2559
- },
2560
- async query(value: unknown) {
2561
- const selected = activeAgent;
2562
- return await selected.query(value);
2563
- },
2564
- remember(value: unknown) {
2565
- const selected = activeAgent;
2566
- return selected.remember(value);
2567
- },
2568
- });
2569
-
2570
- let hostTail: Promise<void> = Promise.resolve();
2571
- const serialized = <T>(operation: () => Promise<T>): Promise<T> => {
2572
- const result = hostTail.then(operation);
2573
- hostTail = result.then(() => undefined, () => undefined);
2574
- return result;
2575
- };
2576
-
2577
- const installCanonicalHead = async (head: OhHeadV1): Promise<void> => {
2578
- const nextAgent = await createAgentAt(head);
2579
- activeAgent = nextAgent;
2580
- activeCanonicalHead = immutableClone(head);
2581
- };
2582
-
2583
- const canonicalAuthority = Object.freeze({ authorityId: canonicalAuthorityId,
2584
- binding: canonicalBinding, store: canonicalStore });
2585
- const readPhysicalCanonicalHead = async (): Promise<OhHeadV1> =>
2586
- parseMemoryAuthorityHead(await canonicalStore.head(), "physical canonical");
2587
-
2588
- const advanceCanonical = (value: unknown): Promise<OhMemoryCanonicalAdvanceReceiptV1> => {
2589
- let request: ReturnType<typeof parseCanonicalAdvanceRequest>;
2590
- try { request = parseCanonicalAdvanceRequest(value); } catch (error) { return Promise.reject(error); }
2591
- return serialized(async () => {
2592
- const priorHead = activeCanonicalHead;
2593
- if (!exactHead(request.expectedHead, priorHead)) {
2594
- throw new OhConflictError("The expected canonical memory head does not match the current pin.");
2595
- }
2596
- if (exactHead(request.nextHead, priorHead)) {
2597
- return canonicalAdvanceReceipt(canonicalAuthorityId, canonicalBinding.bindingSha256,
2598
- priorHead, priorHead, "unchanged");
2599
- }
2600
- await proveCanonicalDescendant(canonicalAuthority, priorHead, request.nextHead);
2601
- await installCanonicalHead(request.nextHead);
2602
- return canonicalAdvanceReceipt(canonicalAuthorityId, canonicalBinding.bindingSha256,
2603
- priorHead, request.nextHead, "advanced");
2604
- });
2605
- };
2606
-
2607
- const adoptNomination = (value: unknown): Promise<OhMemoryAdoptionReceiptV1> => {
2608
- let request: ReturnType<typeof parseAdoptionRequest>;
2609
- try { request = parseAdoptionRequest(value); } catch (error) { return Promise.reject(error); }
2610
- return serialized(async () => {
2611
- const route = routesById.get(request.nomination.nominationId);
2612
- if (route === undefined || route.destinationPurpose !== request.nomination.destinationPurpose) {
2613
- throw new OhProfileError("The memory nomination is not bound to this adoption route.");
2614
- }
2615
- if (request.nomination.source.authorityId !== workingAuthorityId
2616
- || request.nomination.source.bindingSha256 !== workingBinding.bindingSha256
2617
- || request.nomination.closure.binding.bindingSha256 !== workingBinding.bindingSha256) {
2618
- throw new OhProfileError("The memory nomination is not from the bound working authority.");
2619
- }
2620
- const returnedReexport = await workingStore.exportDependencyClosure({
2621
- head: headRef(request.nomination.source.head),
2622
- maximumRecords: OH_DEPENDENCY_CLOSURE_LIMITS_V1.records,
2623
- roots: request.nomination.closure.roots,
2624
- });
2625
- const reexported = detachCanonicalData(returnedReexport, "The working re-exported nomination",
2626
- OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes);
2627
- if (reexported.canonical !== canonicalJson(request.nomination.closure)) {
2628
- throw new OhIntegrityError("The working authority did not re-export the nominated closure exactly.");
2629
- }
2630
-
2631
- const priorHead = activeCanonicalHead;
2632
- const physicalHead = await readPhysicalCanonicalHead();
2633
- const replacementConflictsAt = async (currentLane: LaneSnapshot) => {
2634
- if (request.replacements.length === 0) return [];
2635
- const reviewedLane = exactHead(request.expectedCanonicalHead, currentLane.snapshot.head)
2636
- ? currentLane
2637
- : await readLane(canonicalAuthority, "canonical", request.expectedCanonicalHead);
2638
- return unauthorizedAdoptionDifferences(reviewedLane.snapshot, currentLane.snapshot,
2639
- request.nomination.closure.records, request.replacements);
2640
- };
2641
- if (!exactHead(physicalHead, priorHead)) {
2642
- const physicalLane = await proveCanonicalDescendant(canonicalAuthority, priorHead, physicalHead);
2643
- const physicalDifferences = adoptionDifferences(physicalLane.snapshot,
2644
- request.nomination.closure.records);
2645
- if (physicalDifferences.length === 0) {
2646
- const replacementConflicts = await replacementConflictsAt(physicalLane);
2647
- if (replacementConflicts.length > 0) {
2648
- throw adoptionConflict(request.expectedCanonicalHead, physicalHead, replacementConflicts);
2649
- }
2650
- await installCanonicalHead(physicalHead);
2651
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2652
- canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
2653
- priorHead, physicalHead, "already-present");
2654
- }
2655
- throw adoptionConflict(request.expectedCanonicalHead, physicalHead, physicalDifferences);
2656
- }
2657
-
2658
- const lane = await readLane(canonicalAuthority, "canonical", priorHead);
2659
- const differences = adoptionDifferences(lane.snapshot, request.nomination.closure.records);
2660
- if (!exactHead(request.expectedCanonicalHead, priorHead)) {
2661
- if (differences.length === 0) {
2662
- const replacementConflicts = await replacementConflictsAt(lane);
2663
- if (replacementConflicts.length > 0) {
2664
- throw adoptionConflict(request.expectedCanonicalHead, priorHead, replacementConflicts);
2665
- }
2666
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2667
- canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
2668
- priorHead, priorHead, "already-present");
2669
- }
2670
- throw adoptionConflict(request.expectedCanonicalHead, priorHead, differences);
2671
- }
2672
- if (differences.length === 0) {
2673
- const replacementConflicts = await replacementConflictsAt(lane);
2674
- if (replacementConflicts.length > 0) {
2675
- throw adoptionConflict(request.expectedCanonicalHead, priorHead, replacementConflicts);
2676
- }
2677
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2678
- canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
2679
- priorHead, priorHead, "already-present");
2680
- }
2681
- const unauthorized = unauthorizedAdoptionDifferences(lane.snapshot, lane.snapshot,
2682
- request.nomination.closure.records, request.replacements);
2683
- if (unauthorized.length > 0) {
2684
- throw adoptionConflict(request.expectedCanonicalHead, priorHead, unauthorized);
2685
- }
2686
- const changedKeys = new Set(differences.map(({ key }) => key));
2687
- const changedRecords = request.nomination.closure.records
2688
- .filter(({ key }) => changedKeys.has(key));
2689
- if (changedRecords.length === 0) {
2690
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2691
- canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
2692
- priorHead, priorHead, "already-present");
2693
- }
2694
- assertAdoptionSnapshotCapacity(lane.snapshot, changedRecords);
2695
- const changes = canonicalKnowledgeGraphChangesV1(changedRecords
2696
- .map((record): KnowledgeGraphChangeV1 => ({ kind: "put", record, v: 1 })));
2697
- const operationId = `memory_adopt_${canonicalSha256({ actorId: adoptionActorId,
2698
- bindingSha256: canonicalBinding.bindingSha256,
2699
- nominationSha256: request.nomination.nominationSha256,
2700
- priorHead, v: 1 }).slice(0, 48)}`;
2701
- let returnedOperation: unknown;
2702
- try {
2703
- returnedOperation = await canonicalStore.commit({ actorId: adoptionActorId, changes,
2704
- expectedHead: { generation: priorHead.generation,
2705
- operationSha256: priorHead.operationSha256 },
2706
- maximumOperationBytes: maximumCanonicalOperationBytes,
2707
- operationId });
2708
- } catch (error) {
2709
- if (!(error instanceof OhConflictError)) throw error;
2710
- const actualHead = await readPhysicalCanonicalHead();
2711
- const actualLane = exactHead(actualHead, priorHead)
2712
- ? await readLane(canonicalAuthority, "canonical", actualHead)
2713
- : await proveCanonicalDescendant(canonicalAuthority, priorHead, actualHead);
2714
- const actualDifferences = adoptionDifferences(actualLane.snapshot,
2715
- request.nomination.closure.records);
2716
- if (actualDifferences.length === 0) {
2717
- if (!exactHead(actualHead, priorHead)) {
2718
- await installCanonicalHead(actualHead);
2719
- }
2720
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2721
- canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
2722
- priorHead, actualHead, "already-present");
2723
- }
2724
- throw adoptionConflict(request.expectedCanonicalHead, actualHead, actualDifferences);
2725
- }
2726
- const operation = parseOhOperationV1(detachCanonicalData(returnedOperation,
2727
- "The returned canonical adoption operation",
2728
- OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPageBytes).value);
2729
- if (operation === null || operation.actorId !== adoptionActorId
2730
- || operation.operationId !== operationId || operation.spaceId !== canonicalBinding.spaceId
2731
- || operation.parentOperationSha256 !== priorHead.operationSha256
2732
- || operation.sequence !== priorHead.sequence + 1
2733
- || canonicalJson(operation.changes) !== canonicalJson(changes)) {
2734
- throw new OhIntegrityError("The canonical authority returned a different adoption operation.");
2735
- }
2736
- const head: OhHeadV1 = immutableClone({ generation: operation.sequence,
2737
- graphRevisionSha256: operation.graphRevisionSha256,
2738
- operationSha256: operation.operationSha256, recordsSha256: operation.recordsSha256,
2739
- sequence: operation.sequence, v: 1 });
2740
- const actualHead = await readPhysicalCanonicalHead();
2741
- if (!exactHead(actualHead, head)) {
2742
- const actualLane = exactHead(actualHead, priorHead)
2743
- ? await readLane(canonicalAuthority, "canonical", actualHead)
2744
- : await proveCanonicalDescendant(canonicalAuthority, priorHead, actualHead, head);
2745
- const actualDifferences = adoptionDifferences(actualLane.snapshot,
2746
- request.nomination.closure.records);
2747
- if (actualDifferences.length !== 0) {
2748
- throw adoptionConflict(request.expectedCanonicalHead, actualHead, actualDifferences);
2749
- }
2750
- await installCanonicalHead(actualHead);
2751
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2752
- canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
2753
- priorHead, actualHead, "already-present");
2754
- }
2755
- await installCanonicalHead(actualHead);
2756
- return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
2757
- canonicalBinding.bindingSha256, request.nomination.nominationSha256,
2758
- operation.operationSha256, priorHead, actualHead, "adopted");
2759
- });
2760
- };
2761
-
2762
- return Object.freeze({ agent, host: Object.freeze({ adoptNomination, advanceCanonical }) });
31
+ export type {
32
+ OhMemoryLaneV1,
33
+ OhMemoryAuthoritySourceV1,
34
+ OhMemoryProofV1,
35
+ OhMemoryLaneIdentityV1,
36
+ OhMemoryIdentityV1,
37
+ OhMemoryConflictV1,
38
+ OhMemoryResultRowV1,
39
+ OhMemoryQueryResultV1,
40
+ OhMemoryRememberReceiptV1,
41
+ OhMemoryExplanationV1,
42
+ OhMemoryNominationV1,
43
+ OhMemoryNamedProgramV1,
44
+ OhMemoryNominationRouteV1,
45
+ OhMemoryFactPolicyV1,
46
+ OhMemoryFactDeclarationV1,
47
+ OhMemoryFactExtractorV1,
48
+ OhMemoryFacadeOptionsV1,
49
+ OhMemoryAgentV1,
50
+ OhMemoryEvaluationLimitsV2,
51
+ OhMemoryNamedProgramV2,
52
+ OhMemoryFacadeOptionsV2,
53
+ OhMemoryIdentityV2,
54
+ OhMemoryResultRowV2,
55
+ OhMemoryPageV2,
56
+ OhMemoryQueryResultV2,
57
+ OhMemoryContinuationErrorReasonV2,
58
+ OhMemoryExplanationV2,
59
+ OhMemoryAgentV2,
60
+ OhMemoryCanonicalAdvanceReceiptV1,
61
+ OhMemoryAdoptionConflictEntryV1,
62
+ OhMemoryAdoptionReplacementV1,
63
+ OhMemoryAdoptionConflictV1,
64
+ OhMemoryAdoptionReceiptV1,
65
+ OhMemoryHostControlV1,
66
+ OhMemoryAuthorityV1,
67
+ OhMemoryAuthorityOptionsV1
68
+ } from "./memory-core";
69
+
70
+ export async function createOhMemoryAuthorityV1(options: OhMemoryAuthorityOptionsV1): Promise<OhMemoryAuthorityV1> {
71
+ return await makeAuthority(options);
2763
72
  }