@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
@@ -0,0 +1,133 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { safeCode } from "./canonical";
3
+ import { OH_OPERATION_MAX_BYTES_V1 } from "./operation";
4
+ import { OhConflictError, OhIntegrityError, OhProfileError,
5
+ type OhHeadV1, type OhStoreV1 } from "./store";
6
+ import {
7
+ authorityId, bindingFor, continuationKeyV2, createOhMemoryAgentV2WithRuntime,
8
+ createOhMemoryRuntimeV2, immutableClone, parseMemoryAuthorityHead,
9
+ resolveExtractors, resolveNominationRoutes, resolveProgramsV2,
10
+ type OhMemoryAuthorityOptionsV1,
11
+ } from "./memory-core";
12
+
13
+ export type MemoryFailure = Readonly<{
14
+ _tag: "MemoryConflict" | "MemoryIntegrity" | "MemoryProfile" | "MemoryValidation" | "MemoryCapacity" | "MemoryForeign";
15
+ cause: unknown;
16
+ }>;
17
+
18
+ export function memoryFailure(cause: unknown): MemoryFailure {
19
+ return { _tag: cause instanceof OhConflictError ? "MemoryConflict"
20
+ : cause instanceof OhIntegrityError ? "MemoryIntegrity"
21
+ : cause instanceof OhProfileError ? "MemoryProfile"
22
+ : cause instanceof TypeError ? "MemoryValidation"
23
+ : cause instanceof RangeError ? "MemoryCapacity" : "MemoryForeign", cause };
24
+ }
25
+
26
+ /** Validation may reject hostile values; unrelated generator defects are not caught. */
27
+ export function memoryValue<A>(evaluate: () => A): Effect.Effect<A, MemoryFailure> {
28
+ return Effect.try({ try: evaluate, catch: memoryFailure });
29
+ }
30
+
31
+ function memoryCall<A>(operation: () => Promise<A>): Effect.Effect<A, MemoryFailure> {
32
+ return Effect.tryPromise({ try: operation, catch: memoryFailure });
33
+ }
34
+
35
+ function readStore(store: OhStoreV1) {
36
+ return {
37
+ binding: store.binding,
38
+ head: memoryCall(() => store.head()),
39
+ snapshot: (options: Parameters<OhStoreV1["snapshot"]>[0]) => memoryCall(() => store.snapshot(options)),
40
+ changesSince: (from: Parameters<OhStoreV1["changesSince"]>[0], options: Parameters<OhStoreV1["changesSince"]>[1]) =>
41
+ memoryCall(() => store.changesSince(from, options)),
42
+ };
43
+ }
44
+
45
+ /** Canonical commit is provided only to the trusted host authority composition. */
46
+ export class CanonicalMemoryStore extends Context.Tag("@hraness/oh/CanonicalMemoryStore")<
47
+ CanonicalMemoryStore, ReturnType<typeof canonicalStorePort>
48
+ >() {}
49
+
50
+ function canonicalStorePort(store: OhStoreV1) {
51
+ return { ...readStore(store),
52
+ commit: (input: Parameters<OhStoreV1["commit"]>[0]) => memoryCall(() => store.commit(input)),
53
+ };
54
+ }
55
+
56
+ export type CanonicalMemoryStoreService = ReturnType<typeof canonicalStorePort>;
57
+
58
+ /** The host adoption program borrows only the working closure read capability. */
59
+ export class WorkingMemorySource extends Context.Tag("@hraness/oh/WorkingMemorySource")<
60
+ WorkingMemorySource, {
61
+ readonly exportDependencyClosure: (input: Parameters<OhStoreV1["exportDependencyClosure"]>[0]) =>
62
+ Effect.Effect<Awaited<ReturnType<OhStoreV1["exportDependencyClosure"]>>, MemoryFailure>;
63
+ }
64
+ >() {}
65
+
66
+ function prepareAuthority(options: OhMemoryAuthorityOptionsV1) {
67
+ const maximumCanonicalOperationBytes = options.maximumCanonicalOperationBytes ?? OH_OPERATION_MAX_BYTES_V1;
68
+ if (!Number.isSafeInteger(maximumCanonicalOperationBytes) || maximumCanonicalOperationBytes < 1
69
+ || maximumCanonicalOperationBytes > OH_OPERATION_MAX_BYTES_V1) {
70
+ throw new TypeError("Invalid canonical memory operation byte bound.");
71
+ }
72
+ const memoryActorId = safeCode(options.actorId, 128);
73
+ const adoptionActorId = safeCode(options.adoptionActorId, 128);
74
+ if (memoryActorId === null || adoptionActorId === null) throw new TypeError("Invalid host-bound memory authority actor ID.");
75
+ const canonicalStore = options.canonical.store;
76
+ const workingStore = options.working.store;
77
+ const canonicalAuthorityId = authorityId(options.canonical.authorityId);
78
+ const workingAuthorityId = authorityId(options.working.authorityId);
79
+ if (canonicalAuthorityId === workingAuthorityId) throw new OhProfileError("Working and canonical memory must be distinct physical authorities.");
80
+ const canonicalBinding = bindingFor(canonicalStore, options.canonical.expectedBindingSha256, "canonical");
81
+ const workingBinding = bindingFor(workingStore, options.working.expectedBindingSha256, "working");
82
+ const initialCanonicalHead = parseMemoryAuthorityHead(options.canonical.expectedHead, "initial canonical");
83
+ const workingCodecs = options.working.codecs;
84
+ const explainCapabilityLifetimeMs = options.explainCapabilityLifetimeMs;
85
+ const monotonicNow = options.monotonicNow;
86
+ const now = options.now;
87
+ const continuationKey = continuationKeyV2(options.continuationKey);
88
+ const programs = Object.freeze([...resolveProgramsV2(options.programs).values()].map((program) =>
89
+ immutableClone({ evaluation: program.evaluation, maximumPageBytes: program.maximumPageBytes,
90
+ maximumRows: program.maximumRows, pageSize: program.pageSize, parameters: program.parameters,
91
+ programId: program.programId, purpose: program.purpose, query: program.query,
92
+ rulePack: program.rulePack, v: 2 as const })));
93
+ const extractors = resolveExtractors(options.extractors ?? []);
94
+ const nominationRoutes = Object.freeze([...resolveNominationRoutes(options.nominationRoutes ?? []).values()]);
95
+ const runtime = createOhMemoryRuntimeV2(options);
96
+ const createAgentAt = (expectedHead: OhHeadV1) => memoryCall(() => createOhMemoryAgentV2WithRuntime({
97
+ actorId: memoryActorId,
98
+ canonical: { authorityId: canonicalAuthorityId, expectedBindingSha256: canonicalBinding.bindingSha256,
99
+ expectedHead, store: canonicalStore },
100
+ continuationKey,
101
+ ...(explainCapabilityLifetimeMs === undefined ? {} : { explainCapabilityLifetimeMs }),
102
+ extractors,
103
+ ...(monotonicNow === undefined ? {} : { monotonicNow }),
104
+ nominationRoutes,
105
+ ...(now === undefined ? {} : { now }),
106
+ programs,
107
+ working: { authorityId: workingAuthorityId, codecs: workingCodecs,
108
+ expectedBindingSha256: workingBinding.bindingSha256, store: workingStore },
109
+ }, runtime));
110
+ return { adoptionActorId, canonicalAuthorityId, workingAuthorityId, canonicalBinding, workingBinding,
111
+ initialCanonicalHead, maximumCanonicalOperationBytes, createAgentAt,
112
+ routesById: new Map(nominationRoutes.map((route) => [route.nominationId, route])),
113
+ canonical: canonicalStorePort(canonicalStore),
114
+ working: { exportDependencyClosure: (input: Parameters<OhStoreV1["exportDependencyClosure"]>[0]) =>
115
+ memoryCall(() => workingStore.exportDependencyClosure(input)) },
116
+ };
117
+ }
118
+
119
+ export class MemoryAuthorityConfig extends Context.Tag("@hraness/oh/MemoryAuthorityConfig")<
120
+ MemoryAuthorityConfig, Omit<ReturnType<typeof prepareAuthority>, "canonical" | "working">
121
+ >() {}
122
+
123
+ /** Preparation captures caller-owned options before the first foreign wait. Stores
124
+ * are borrowed; these Layers cannot close or purge either physical authority. */
125
+ export function memoryAuthorityLive(options: OhMemoryAuthorityOptionsV1):
126
+ Layer.Layer<MemoryAuthorityConfig | CanonicalMemoryStore | WorkingMemorySource, MemoryFailure> {
127
+ return Layer.unwrapEffect(memoryValue(() => prepareAuthority(options)).pipe(Effect.map(prepared => {
128
+ const { canonical, working, ...config } = prepared;
129
+ return Layer.mergeAll(Layer.succeed(MemoryAuthorityConfig, config),
130
+ Layer.succeed(CanonicalMemoryStore, canonical),
131
+ Layer.succeed(WorkingMemorySource, working));
132
+ })));
133
+ }
@@ -0,0 +1,309 @@
1
+ import { Cause, Effect, Exit, Option } from "effect";
2
+ import { canonicalJson, canonicalSha256, isPlainRecord, hasExactKeys } from "./canonical";
3
+ import { canonicalKnowledgeGraphChangesV1, type KnowledgeGraphChangeV1 } from "./graph";
4
+ import { parseOhOperationV1 } from "./operation";
5
+ import { OH_DEPENDENCY_CLOSURE_LIMITS_V1, OhConflictError, OhIntegrityError, OhProfileError,
6
+ parseOhHeadRefV1, parseOhHeadV1, type OhHeadV1, type OhStoreBindingV1 } from "./store";
7
+ import { OH_MEMORY_LIMITS_V1, OH_MEMORY_AUTHORITY_LIMITS_V1, immutableClone, exactHead, headRef,
8
+ parseDetachedStoreSnapshot, datasetForSnapshot, parseMemoryAuthorityHead, detachCanonicalData,
9
+ parseCanonicalAdvanceRequest, parseAdoptionRequest, canonicalAdvanceReceipt, adoptionReceipt,
10
+ adoptionDifferences, unauthorizedAdoptionDifferences, assertAdoptionSnapshotCapacity, adoptionConflict,
11
+ type LaneSnapshot, type OhMemoryCanonicalAdvanceReceiptV1, type OhMemoryAdoptionReceiptV1,
12
+ type OhMemoryAdoptionConflictEntryV1 } from "./memory-core";
13
+ import { CanonicalMemoryStore, WorkingMemorySource, MemoryAuthorityConfig, memoryValue, memoryFailure, type CanonicalMemoryStoreService,
14
+ type MemoryFailure } from "./memory-authority-platform";
15
+
16
+ type CanonicalAuthority = Readonly<{ authorityId: string; binding: OhStoreBindingV1;
17
+ store: CanonicalMemoryStoreService }>;
18
+
19
+ function readLane(authority: CanonicalAuthority, lane: "canonical", expectedHead: OhHeadV1):
20
+ Effect.Effect<LaneSnapshot, MemoryFailure> {
21
+ return Effect.gen(function* () {
22
+ const head = immutableClone(expectedHead);
23
+ const returnedSnapshot = yield* authority.store.snapshot({
24
+ head: { operationSha256: head.operationSha256, sequence: head.sequence },
25
+ maximumRecords: OH_MEMORY_LIMITS_V1.maximumRecordsPerLane,
26
+ });
27
+ return yield* memoryValue(() => {
28
+ const parsed = parseDetachedStoreSnapshot(returnedSnapshot, `The ${lane} store`, head, authority.binding.spaceId);
29
+ const projected = datasetForSnapshot(authority.binding, parsed.snapshot, parsed.projectionSnapshot);
30
+ return Object.freeze({ authorityId: authority.authorityId, binding: authority.binding,
31
+ dataset: projected.dataset, lane, projectionSnapshot: projected.projectionSnapshot, snapshot: parsed.snapshot });
32
+ });
33
+ });
34
+ }
35
+
36
+ function proveCanonicalDescendant(
37
+ authority: CanonicalAuthority,
38
+ priorHead: OhHeadV1,
39
+ nextHead: OhHeadV1,
40
+ requiredFirstHead?: OhHeadV1,
41
+ ): Effect.Effect<LaneSnapshot, MemoryFailure> {
42
+ return Effect.gen(function* () {
43
+ if (nextHead.sequence <= priorHead.sequence) {
44
+ return yield* Effect.fail(memoryFailure(new OhConflictError("The next canonical memory head is not a descendant of the current pin.")));
45
+ }
46
+ const distance = nextHead.sequence - priorHead.sequence;
47
+ if (distance > OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalAdvanceOperations
48
+ || Math.ceil(distance / OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPage)
49
+ > OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalAdvancePages) {
50
+ return yield* Effect.fail(memoryFailure(new RangeError("The canonical memory advance exceeds its total proof bound; advance in host-reviewed chunks.")));
51
+ }
52
+ const through = headRef(nextHead);
53
+ let cursor = headRef(priorHead);
54
+ let pageCount = 0;
55
+ let reachedHead: OhHeadV1 | null = null;
56
+ let firstHead: OhHeadV1 | null = null;
57
+ while (cursor.sequence < through.sequence) {
58
+ if (pageCount >= OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalAdvancePages) {
59
+ return yield* Effect.fail(memoryFailure(new RangeError("The canonical memory advance exceeded its page proof bound; advance in host-reviewed chunks.")));
60
+ }
61
+ pageCount += 1;
62
+ const remaining = through.sequence - cursor.sequence;
63
+ const limit = Math.min(remaining, OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPage);
64
+ const page = yield* authority.store.changesSince(cursor, { limit, through });
65
+ const returnedData = (yield* memoryValue(() => detachCanonicalData(
66
+ page,
67
+ "The canonical change-feed page",
68
+ OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPageBytes,
69
+ ))).value;
70
+ const returned = returnedData as Record<string, unknown>;
71
+ if (!isPlainRecord(returned) || !hasExactKeys(returned,
72
+ ["from", "hasMore", "operations", "through", "to", "v"])
73
+ || returned.v !== 1 || typeof returned.hasMore !== "boolean"
74
+ || !Array.isArray(returned.operations) || returned.operations.length > limit) {
75
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The canonical change feed returned an invalid page envelope.")));
76
+ }
77
+ const from = parseOhHeadRefV1(returned.from);
78
+ const returnedThrough = parseOhHeadV1(returned.through);
79
+ const returnedTo = parseOhHeadRefV1(returned.to);
80
+ if (from === null || returnedThrough === null || returnedTo === null
81
+ || canonicalJson(from) !== canonicalJson(cursor)
82
+ || !exactHead(returnedThrough, nextHead)) {
83
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The canonical change feed changed its pinned bounds.")));
84
+ }
85
+ let reached = cursor;
86
+ for (const value of returned.operations) {
87
+ const operation = parseOhOperationV1(value);
88
+ if (operation === null || operation.spaceId !== authority.binding.spaceId
89
+ || operation.sequence !== reached.sequence + 1
90
+ || operation.parentOperationSha256 !== reached.operationSha256) {
91
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The canonical change feed contains a gap or different authority.")));
92
+ }
93
+ reached = Object.freeze({ operationSha256: operation.operationSha256,
94
+ sequence: operation.sequence });
95
+ reachedHead = immutableClone({ generation: operation.sequence,
96
+ graphRevisionSha256: operation.graphRevisionSha256,
97
+ operationSha256: operation.operationSha256, recordsSha256: operation.recordsSha256,
98
+ sequence: operation.sequence, v: 1 });
99
+ firstHead ??= reachedHead;
100
+ }
101
+ if (canonicalJson(reached) !== canonicalJson(returnedTo)
102
+ || (returned.hasMore && returned.operations.length === 0)
103
+ || (returned.hasMore && reached.sequence >= through.sequence)
104
+ || reached.sequence > through.sequence
105
+ || (!returned.hasMore && canonicalJson(reached) !== canonicalJson(through))) {
106
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The canonical change feed did not prove the requested descendant.")));
107
+ }
108
+ cursor = reached;
109
+ }
110
+ if (reachedHead === null || !exactHead(reachedHead, nextHead)) {
111
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The canonical change feed did not prove the requested full head.")));
112
+ }
113
+ if (requiredFirstHead !== undefined
114
+ && (firstHead === null || !exactHead(firstHead, requiredFirstHead))) {
115
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The returned adoption operation is not on the current canonical path.")));
116
+ }
117
+ return yield* readLane(authority, "canonical", nextHead);
118
+ });
119
+ }
120
+
121
+ /** One FIFO for trusted host changes; agent calls retain their captured immutable pin.
122
+ * After admission the operation retains its permit through actual foreign settlement
123
+ * and pin publication. Native commit/CAS remains authoritative; there is no retry. */
124
+ export const makeMemoryAuthority = Effect.gen(function* () {
125
+ const { initialCanonicalHead, createAgentAt, canonicalAuthorityId, workingAuthorityId,
126
+ canonicalBinding, workingBinding, routesById, adoptionActorId, maximumCanonicalOperationBytes } = yield* MemoryAuthorityConfig;
127
+ const canonicalStore = yield* CanonicalMemoryStore;
128
+ const workingStore = yield* WorkingMemorySource;
129
+ const hostOrder = yield* Effect.makeSemaphore(1);
130
+ let activeCanonicalHead = initialCanonicalHead;
131
+ let activeAgent = yield* createAgentAt(activeCanonicalHead);
132
+ const canonicalAuthority = Object.freeze({ authorityId: canonicalAuthorityId, binding: canonicalBinding, store: canonicalStore });
133
+ const installCanonicalHead = (head: OhHeadV1) => Effect.gen(function* () {
134
+ const nextAgent = yield* createAgentAt(head);
135
+ // Publish both references together only after the next snapshot is verified.
136
+ activeAgent = nextAgent;
137
+ activeCanonicalHead = immutableClone(head);
138
+ });
139
+ const readPhysicalCanonicalHead = () => canonicalStore.head.pipe(
140
+ Effect.flatMap(head => memoryValue(() => parseMemoryAuthorityHead(head, "physical canonical"))));
141
+ const advanceCanonical = (request: ReturnType<typeof parseCanonicalAdvanceRequest>): Effect.Effect<OhMemoryCanonicalAdvanceReceiptV1, MemoryFailure> =>
142
+ hostOrder.withPermits(1)(Effect.uninterruptible(Effect.gen(function* () {
143
+ const priorHead = activeCanonicalHead;
144
+ if (!exactHead(request.expectedHead, priorHead)) {
145
+ return yield* Effect.fail(memoryFailure(new OhConflictError("The expected canonical memory head does not match the current pin.")));
146
+ }
147
+ if (exactHead(request.nextHead, priorHead)) {
148
+ return canonicalAdvanceReceipt(canonicalAuthorityId, canonicalBinding.bindingSha256,
149
+ priorHead, priorHead, "unchanged");
150
+ }
151
+ yield* proveCanonicalDescendant(canonicalAuthority, priorHead, request.nextHead);
152
+ yield* installCanonicalHead(request.nextHead);
153
+ return canonicalAdvanceReceipt(canonicalAuthorityId, canonicalBinding.bindingSha256,
154
+ priorHead, request.nextHead, "advanced");
155
+ })));
156
+
157
+ const adoptNomination = (request: ReturnType<typeof parseAdoptionRequest>): Effect.Effect<OhMemoryAdoptionReceiptV1, MemoryFailure> =>
158
+ hostOrder.withPermits(1)(Effect.uninterruptible(Effect.gen(function* () {
159
+ const route = routesById.get(request.nomination.nominationId);
160
+ if (route === undefined || route.destinationPurpose !== request.nomination.destinationPurpose) {
161
+ return yield* Effect.fail(memoryFailure(new OhProfileError("The memory nomination is not bound to this adoption route.")));
162
+ }
163
+ if (request.nomination.source.authorityId !== workingAuthorityId
164
+ || request.nomination.source.bindingSha256 !== workingBinding.bindingSha256
165
+ || request.nomination.closure.binding.bindingSha256 !== workingBinding.bindingSha256) {
166
+ return yield* Effect.fail(memoryFailure(new OhProfileError("The memory nomination is not from the bound working authority.")));
167
+ }
168
+ const returnedReexport = yield* workingStore.exportDependencyClosure({
169
+ head: headRef(request.nomination.source.head),
170
+ maximumRecords: OH_DEPENDENCY_CLOSURE_LIMITS_V1.records,
171
+ roots: request.nomination.closure.roots,
172
+ });
173
+ const reexported = yield* memoryValue(() => detachCanonicalData(returnedReexport, "The working re-exported nomination",
174
+ OH_DEPENDENCY_CLOSURE_LIMITS_V1.bytes));
175
+ if (reexported.canonical !== canonicalJson(request.nomination.closure)) {
176
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The working authority did not re-export the nominated closure exactly.")));
177
+ }
178
+
179
+ const priorHead = activeCanonicalHead;
180
+ const physicalHead = yield* readPhysicalCanonicalHead();
181
+ const replacementConflictsAt = (currentLane: LaneSnapshot): Effect.Effect<readonly OhMemoryAdoptionConflictEntryV1[], MemoryFailure> => Effect.gen(function* () {
182
+ if (request.replacements.length === 0) return [];
183
+ const reviewedLane = exactHead(request.expectedCanonicalHead, currentLane.snapshot.head)
184
+ ? currentLane
185
+ : yield* readLane(canonicalAuthority, "canonical", request.expectedCanonicalHead);
186
+ return unauthorizedAdoptionDifferences(reviewedLane.snapshot, currentLane.snapshot,
187
+ request.nomination.closure.records, request.replacements);
188
+ });
189
+ if (!exactHead(physicalHead, priorHead)) {
190
+ const physicalLane = yield* proveCanonicalDescendant(canonicalAuthority, priorHead, physicalHead);
191
+ const physicalDifferences = adoptionDifferences(physicalLane.snapshot,
192
+ request.nomination.closure.records);
193
+ if (physicalDifferences.length === 0) {
194
+ const replacementConflicts = yield* replacementConflictsAt(physicalLane);
195
+ if (replacementConflicts.length > 0) {
196
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, physicalHead, replacementConflicts)));
197
+ }
198
+ yield* installCanonicalHead(physicalHead);
199
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
200
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
201
+ priorHead, physicalHead, "already-present");
202
+ }
203
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, physicalHead, physicalDifferences)));
204
+ }
205
+
206
+ const lane = yield* readLane(canonicalAuthority, "canonical", priorHead);
207
+ const differences = adoptionDifferences(lane.snapshot, request.nomination.closure.records);
208
+ if (!exactHead(request.expectedCanonicalHead, priorHead)) {
209
+ if (differences.length === 0) {
210
+ const replacementConflicts = yield* replacementConflictsAt(lane);
211
+ if (replacementConflicts.length > 0) {
212
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, priorHead, replacementConflicts)));
213
+ }
214
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
215
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
216
+ priorHead, priorHead, "already-present");
217
+ }
218
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, priorHead, differences)));
219
+ }
220
+ if (differences.length === 0) {
221
+ const replacementConflicts = yield* replacementConflictsAt(lane);
222
+ if (replacementConflicts.length > 0) {
223
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, priorHead, replacementConflicts)));
224
+ }
225
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
226
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
227
+ priorHead, priorHead, "already-present");
228
+ }
229
+ const unauthorized = unauthorizedAdoptionDifferences(lane.snapshot, lane.snapshot,
230
+ request.nomination.closure.records, request.replacements);
231
+ if (unauthorized.length > 0) {
232
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, priorHead, unauthorized)));
233
+ }
234
+ const changedKeys = new Set(differences.map(({ key }) => key));
235
+ const changedRecords = request.nomination.closure.records
236
+ .filter(({ key }) => changedKeys.has(key));
237
+ if (changedRecords.length === 0) {
238
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
239
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
240
+ priorHead, priorHead, "already-present");
241
+ }
242
+ yield* memoryValue(() => assertAdoptionSnapshotCapacity(lane.snapshot, changedRecords));
243
+ const changes = canonicalKnowledgeGraphChangesV1(changedRecords
244
+ .map((record): KnowledgeGraphChangeV1 => ({ kind: "put", record, v: 1 })));
245
+ const operationId = `memory_adopt_${canonicalSha256({ actorId: adoptionActorId,
246
+ bindingSha256: canonicalBinding.bindingSha256,
247
+ nominationSha256: request.nomination.nominationSha256,
248
+ priorHead, v: 1 }).slice(0, 48)}`;
249
+ const committed = yield* Effect.exit(canonicalStore.commit({ actorId: adoptionActorId, changes,
250
+ expectedHead: { generation: priorHead.generation,
251
+ operationSha256: priorHead.operationSha256 },
252
+ maximumOperationBytes: maximumCanonicalOperationBytes,
253
+ operationId }));
254
+ if (Exit.isFailure(committed)) {
255
+ const error = Cause.failureOption(committed.cause);
256
+ if (Option.isNone(error) || error.value._tag !== "MemoryConflict") return yield* Effect.failCause(committed.cause);
257
+ const actualHead = yield* readPhysicalCanonicalHead();
258
+ const actualLane = exactHead(actualHead, priorHead)
259
+ ? yield* readLane(canonicalAuthority, "canonical", actualHead)
260
+ : yield* proveCanonicalDescendant(canonicalAuthority, priorHead, actualHead);
261
+ const actualDifferences = adoptionDifferences(actualLane.snapshot,
262
+ request.nomination.closure.records);
263
+ if (actualDifferences.length === 0) {
264
+ if (!exactHead(actualHead, priorHead)) {
265
+ yield* installCanonicalHead(actualHead);
266
+ }
267
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
268
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
269
+ priorHead, actualHead, "already-present");
270
+ }
271
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, actualHead, actualDifferences)));
272
+ }
273
+ const operation = yield* memoryValue(() => parseOhOperationV1(detachCanonicalData(committed.value,
274
+ "The returned canonical adoption operation",
275
+ OH_MEMORY_AUTHORITY_LIMITS_V1.canonicalChangeFeedPageBytes).value));
276
+ if (operation === null || operation.actorId !== adoptionActorId
277
+ || operation.operationId !== operationId || operation.spaceId !== canonicalBinding.spaceId
278
+ || operation.parentOperationSha256 !== priorHead.operationSha256
279
+ || operation.sequence !== priorHead.sequence + 1
280
+ || canonicalJson(operation.changes) !== canonicalJson(changes)) {
281
+ return yield* Effect.fail(memoryFailure(new OhIntegrityError("The canonical authority returned a different adoption operation.")));
282
+ }
283
+ const head: OhHeadV1 = immutableClone({ generation: operation.sequence,
284
+ graphRevisionSha256: operation.graphRevisionSha256,
285
+ operationSha256: operation.operationSha256, recordsSha256: operation.recordsSha256,
286
+ sequence: operation.sequence, v: 1 });
287
+ const actualHead = yield* readPhysicalCanonicalHead();
288
+ if (!exactHead(actualHead, head)) {
289
+ const actualLane = exactHead(actualHead, priorHead)
290
+ ? yield* readLane(canonicalAuthority, "canonical", actualHead)
291
+ : yield* proveCanonicalDescendant(canonicalAuthority, priorHead, actualHead, head);
292
+ const actualDifferences = adoptionDifferences(actualLane.snapshot,
293
+ request.nomination.closure.records);
294
+ if (actualDifferences.length !== 0) {
295
+ return yield* Effect.fail(memoryFailure(adoptionConflict(request.expectedCanonicalHead, actualHead, actualDifferences)));
296
+ }
297
+ yield* installCanonicalHead(actualHead);
298
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
299
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256, null,
300
+ priorHead, actualHead, "already-present");
301
+ }
302
+ yield* installCanonicalHead(actualHead);
303
+ return adoptionReceipt(adoptionActorId, canonicalAuthorityId,
304
+ canonicalBinding.bindingSha256, request.nomination.nominationSha256,
305
+ operation.operationSha256, priorHead, actualHead, "adopted");
306
+ })));
307
+
308
+ return { get agent() { return activeAgent; }, advanceCanonical, adoptNomination };
309
+ });
@@ -0,0 +1,40 @@
1
+ import { Cause, Effect, Exit, Option } from "effect";
2
+ import { parseAdoptionRequest, parseCanonicalAdvanceRequest,
3
+ type OhMemoryAuthorityOptionsV1, type OhMemoryAuthorityV1 } from "./memory-core";
4
+ import { memoryAuthorityLive, type MemoryFailure } from "./memory-authority-platform";
5
+ import { makeMemoryAuthority } from "./memory-authority-program";
6
+
7
+ function unwrap<A>(exit: Exit.Exit<A, MemoryFailure>): A {
8
+ if (Exit.isSuccess(exit)) return exit.value;
9
+ const expected = Cause.failureOption(exit.cause);
10
+ if (Option.isSome(expected)) throw expected.value.cause;
11
+ throw Cause.squash(exit.cause);
12
+ }
13
+
14
+ function run<A>(program: Effect.Effect<A, MemoryFailure, never>): Promise<A> {
15
+ return Effect.runPromiseExit(program).then(unwrap);
16
+ }
17
+
18
+ /** Existing Promise surface; stores and agent capability lifetimes remain borrowed. */
19
+ export async function createOhMemoryAuthorityV1(options: OhMemoryAuthorityOptionsV1): Promise<OhMemoryAuthorityV1> {
20
+ const authority = await run(makeMemoryAuthority.pipe(Effect.provide(memoryAuthorityLive(options))));
21
+ return Object.freeze({
22
+ agent: Object.freeze({
23
+ explain: (value: unknown) => authority.agent.explain(value),
24
+ nominate: (value: unknown) => authority.agent.nominate(value),
25
+ query: (value: unknown) => authority.agent.query(value),
26
+ remember: (value: unknown) => authority.agent.remember(value),
27
+ }),
28
+ host: Object.freeze({
29
+ advanceCanonical: (value: unknown) => {
30
+ // Detach before enqueueing; caller mutation cannot change a waiting request.
31
+ try { return run(authority.advanceCanonical(parseCanonicalAdvanceRequest(value))); }
32
+ catch (cause) { return Promise.reject(cause); }
33
+ },
34
+ adoptNomination: (value: unknown) => {
35
+ try { return run(authority.adoptNomination(parseAdoptionRequest(value))); }
36
+ catch (cause) { return Promise.reject(cause); }
37
+ },
38
+ }),
39
+ });
40
+ }