@hraness/oh 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +598 -0
  3. package/dist/canonical.d.ts +32 -0
  4. package/dist/canonical.d.ts.map +1 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +3419 -0
  8. package/dist/contract.d.ts +33 -0
  9. package/dist/contract.d.ts.map +1 -0
  10. package/dist/graph.d.ts +67 -0
  11. package/dist/graph.d.ts.map +1 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +1988 -0
  15. package/dist/libsql.d.ts +57 -0
  16. package/dist/libsql.d.ts.map +1 -0
  17. package/dist/libsql.js +2662 -0
  18. package/dist/memory.d.ts +366 -0
  19. package/dist/memory.d.ts.map +1 -0
  20. package/dist/memory.js +3650 -0
  21. package/dist/ontology.d.ts +242 -0
  22. package/dist/ontology.d.ts.map +1 -0
  23. package/dist/operation.d.ts +24 -0
  24. package/dist/operation.d.ts.map +1 -0
  25. package/dist/projection-public.d.ts +59 -0
  26. package/dist/projection-public.d.ts.map +1 -0
  27. package/dist/projection-public.js +1682 -0
  28. package/dist/projection-suss.d.ts +17 -0
  29. package/dist/projection-suss.d.ts.map +1 -0
  30. package/dist/projection-suss.js +1721 -0
  31. package/dist/projection.d.ts +315 -0
  32. package/dist/projection.d.ts.map +1 -0
  33. package/dist/schema.d.ts +45 -0
  34. package/dist/schema.d.ts.map +1 -0
  35. package/dist/sdk.d.ts +51 -0
  36. package/dist/sdk.d.ts.map +1 -0
  37. package/dist/sdk.js +3072 -0
  38. package/dist/search.d.ts +34 -0
  39. package/dist/search.d.ts.map +1 -0
  40. package/dist/semantic.d.ts +83 -0
  41. package/dist/semantic.d.ts.map +1 -0
  42. package/dist/semantic.js +706 -0
  43. package/dist/sqlite/driver.d.ts +6 -0
  44. package/dist/sqlite/driver.d.ts.map +1 -0
  45. package/dist/sqlite/index.d.ts +5 -0
  46. package/dist/sqlite/index.d.ts.map +1 -0
  47. package/dist/sqlite/index.js +2840 -0
  48. package/dist/sqlite/migrations.d.ts +10 -0
  49. package/dist/sqlite/migrations.d.ts.map +1 -0
  50. package/dist/sqlite/port.d.ts +40 -0
  51. package/dist/sqlite/port.d.ts.map +1 -0
  52. package/dist/sqlite/runtime.d.ts +23 -0
  53. package/dist/sqlite/runtime.d.ts.map +1 -0
  54. package/dist/sqlite/store.d.ts +85 -0
  55. package/dist/sqlite/store.d.ts.map +1 -0
  56. package/dist/store.d.ts +236 -0
  57. package/dist/store.d.ts.map +1 -0
  58. package/dist/store.js +996 -0
  59. package/dist/sync.d.ts +57 -0
  60. package/dist/sync.d.ts.map +1 -0
  61. package/dist/sync.js +1328 -0
  62. package/package.json +121 -0
  63. package/skills/oh/SKILL.md +206 -0
  64. package/skills/oh/agents/openai.yaml +4 -0
  65. package/spec/README.md +74 -0
  66. package/spec/manifest.json +41 -0
  67. package/spec/v1/canonical-json.md +59 -0
  68. package/spec/v1/contract.json +28 -0
  69. package/spec/v1/contract.schema.json +58 -0
  70. package/spec/v1/embedding-profile.json +11 -0
  71. package/spec/v1/embedding.md +56 -0
  72. package/spec/v1/graph.md +87 -0
  73. package/spec/v1/memory.md +193 -0
  74. package/spec/v1/migration.md +92 -0
  75. package/spec/v1/ontology.json +55 -0
  76. package/spec/v1/ontology.md +80 -0
  77. package/spec/v1/operation.schema.json +138 -0
  78. package/spec/v1/projection-identity.schema.json +58 -0
  79. package/spec/v1/projection-query.schema.json +60 -0
  80. package/spec/v1/projection-result.schema.json +452 -0
  81. package/spec/v1/projection-rule-pack.schema.json +182 -0
  82. package/spec/v1/projection.md +165 -0
  83. package/spec/v1/record.schema.json +95 -0
  84. package/spec/v1/schema-evolution.md +51 -0
  85. package/spec/v1/schema-revision.schema.json +178 -0
  86. package/spec/v1/storage.md +88 -0
  87. package/spec/v1/store.md +131 -0
  88. package/spec/v1/sync-bundle.schema.json +51 -0
  89. package/spec/v1/sync.md +67 -0
  90. package/src/canonical.test.ts +46 -0
  91. package/src/canonical.ts +203 -0
  92. package/src/cli.test.ts +103 -0
  93. package/src/cli.ts +308 -0
  94. package/src/contract.ts +87 -0
  95. package/src/contracts.test.ts +147 -0
  96. package/src/graph.ts +248 -0
  97. package/src/index.ts +8 -0
  98. package/src/libsql.test.ts +657 -0
  99. package/src/libsql.ts +1687 -0
  100. package/src/memory.test.ts +783 -0
  101. package/src/memory.ts +1684 -0
  102. package/src/ontology.ts +573 -0
  103. package/src/operation.ts +80 -0
  104. package/src/projection-public.ts +53 -0
  105. package/src/projection-suss.ts +129 -0
  106. package/src/projection.test.ts +418 -0
  107. package/src/projection.ts +1457 -0
  108. package/src/schema.ts +156 -0
  109. package/src/sdk.ts +96 -0
  110. package/src/search.ts +66 -0
  111. package/src/semantic.test.ts +480 -0
  112. package/src/semantic.ts +333 -0
  113. package/src/sqlite/driver.ts +47 -0
  114. package/src/sqlite/index.ts +4 -0
  115. package/src/sqlite/migrations.test.ts +44 -0
  116. package/src/sqlite/migrations.ts +178 -0
  117. package/src/sqlite/port.test.ts +127 -0
  118. package/src/sqlite/port.ts +120 -0
  119. package/src/sqlite/runtime.test.ts +68 -0
  120. package/src/sqlite/runtime.ts +53 -0
  121. package/src/sqlite/store.test.ts +295 -0
  122. package/src/sqlite/store.ts +988 -0
  123. package/src/store.test.ts +121 -0
  124. package/src/store.ts +701 -0
  125. package/src/sync.test.ts +117 -0
  126. package/src/sync.ts +227 -0
@@ -0,0 +1,127 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+
6
+ import { createKnowledgeGraphRecordV1 } from "../graph";
7
+ import {
8
+ createOhStoreBindingV1,
9
+ OH_CANONICAL_STORE_PROFILE_V1,
10
+ OH_WORKING_STORE_PROFILE_V1,
11
+ OhProfileError,
12
+ OhPurgedSpaceError,
13
+ } from "../store";
14
+ import { createOhSqliteStoreAuthorityV1 } from "./port";
15
+ import { OhSqliteStore } from "./store";
16
+
17
+ const roots: string[] = [];
18
+ afterEach(async () => {
19
+ await Promise.all(roots.splice(0).map((root) => rm(root, { force: true, recursive: true })));
20
+ });
21
+
22
+ async function databasePath(): Promise<string> {
23
+ const root = await mkdtemp(join(tmpdir(), "oh-port-test-"));
24
+ roots.push(root);
25
+ return join(root, "oh.sqlite");
26
+ }
27
+
28
+ const entity = (key: string, name: string, dependencies: readonly string[] = []) =>
29
+ createKnowledgeGraphRecordV1({ dependencies, key, kind: "entity", v: 1, value: { name } });
30
+
31
+ describe("promise-based SQLite store port", () => {
32
+ test("reads exact historical heads and paginates a feed through a pinned head", async () => {
33
+ const authority = createOhSqliteStoreAuthorityV1({ path: ":memory:",
34
+ profile: OH_WORKING_STORE_PROFILE_V1, realmId: "realm:history", spaceId: "history" });
35
+ const first = await authority.store.commit({ actorId: "agent.test", changes: [{ kind: "put",
36
+ record: entity("entity:a", "A"), v: 1 }], expectedHead: await authority.store.head(),
37
+ instant: "2026-08-29T12:00:00.000Z", operationId: "op_a" });
38
+ await authority.store.commit({ actorId: "agent.test", changes: [{ kind: "put",
39
+ record: entity("entity:b", "B"), v: 1 }], expectedHead: await authority.store.head(),
40
+ instant: "2026-08-29T12:01:00.000Z", operationId: "op_b" });
41
+ const third = await authority.store.commit({ actorId: "agent.test", changes: [{ kind: "put",
42
+ record: entity("entity:c", "C"), v: 1 }], expectedHead: await authority.store.head(),
43
+ instant: "2026-08-29T12:02:00.000Z", operationId: "op_c" });
44
+
45
+ const firstSnapshot = await authority.store.snapshot({ head: {
46
+ operationSha256: first.operationSha256, sequence: first.sequence } });
47
+ expect(firstSnapshot.records.map(({ key }) => key)).toEqual(["entity:a"]);
48
+ const page = await authority.store.changesSince({ operationSha256: null, sequence: 0 }, {
49
+ limit: 2, through: { operationSha256: third.operationSha256, sequence: third.sequence },
50
+ });
51
+ expect(page.operations.map(({ operationId }) => operationId)).toEqual(["op_a", "op_b"]);
52
+ expect(page.hasMore).toBe(true);
53
+ const final = await authority.store.changesSince(page.to, { limit: 2,
54
+ through: { operationSha256: third.operationSha256, sequence: third.sequence } });
55
+ expect(final.operations.map(({ operationId }) => operationId)).toEqual(["op_c"]);
56
+ expect(final.hasMore).toBe(false);
57
+ await authority.store.close();
58
+ });
59
+
60
+ test("exports a verified closure from the exact requested head", async () => {
61
+ const authority = createOhSqliteStoreAuthorityV1({ path: ":memory:",
62
+ profile: OH_WORKING_STORE_PROFILE_V1, realmId: "realm:closure", spaceId: "closure" });
63
+ const parent = entity("entity:parent", "Parent");
64
+ const child = entity("entity:child", "Child", [parent.key]);
65
+ await authority.store.commit({ actorId: "agent.test", changes: [
66
+ { kind: "put", record: child, v: 1 }, { kind: "put", record: parent, v: 1 },
67
+ ], expectedHead: await authority.store.head(), operationId: "op_closure" });
68
+ const closure = await authority.store.exportDependencyClosure({ roots: [child.key] });
69
+ expect(closure.records.map(({ key }) => key)).toEqual([child.key, parent.key].sort());
70
+ expect(closure.binding.bindingSha256).toBe(authority.store.binding.bindingSha256);
71
+ await authority.store.close();
72
+ });
73
+
74
+ test("persists an exact realm binding and rejects a different profile on reopen", async () => {
75
+ const path = await databasePath();
76
+ const canonical = createOhSqliteStoreAuthorityV1({ path, profile: OH_CANONICAL_STORE_PROFILE_V1,
77
+ realmId: "realm:canonical", spaceId: "same" });
78
+ await canonical.store.close();
79
+ expect(() => createOhSqliteStoreAuthorityV1({ path, profile: OH_WORKING_STORE_PROFILE_V1,
80
+ realmId: "realm:working", spaceId: "same" })).toThrow(OhProfileError);
81
+ });
82
+
83
+ test("keeps purge on host control, deletes all working payload rows, and leaves a receipt", async () => {
84
+ const path = await databasePath();
85
+ const authority = createOhSqliteStoreAuthorityV1({ path, profile: OH_WORKING_STORE_PROFILE_V1,
86
+ realmId: "realm:purge", spaceId: "purge" });
87
+ expect("purgeWorkingSpace" in authority.store).toBe(false);
88
+ await authority.store.commit({ actorId: "agent.test", changes: [{ kind: "put",
89
+ record: entity("entity:private", "Private"), v: 1 }], expectedHead: await authority.store.head(),
90
+ operationId: "op_private" });
91
+ const receipt = await authority.host.purgeWorkingSpace({ purgedAt: "2026-08-29T13:00:00.000Z" });
92
+ expect(receipt.priorHead.sequence).toBe(1);
93
+ expect(await authority.host.purgeWorkingSpace({ purgedAt: "2026-08-29T13:00:00.000Z" })).toEqual(receipt);
94
+
95
+ const database = new OhSqliteStore({ path, spaceId: "other" });
96
+ for (const table of ["oh_operations", "oh_operation_records", "oh_records", "oh_dependencies",
97
+ "oh_search_documents", "oh_sync_outbox", "oh_sync_state", "oh_space_bindings", "oh_spaces"]) {
98
+ const where = table === "oh_operation_records"
99
+ ? "operation_sha256 IN (SELECT operation_sha256 FROM oh_operations WHERE space_id = 'purge')"
100
+ : "space_id = 'purge'";
101
+ const count = database.database.query<{ count: number }, []>(`SELECT count(*) AS count FROM ${table} WHERE ${where}`).get()?.count;
102
+ expect(count, table).toBe(0);
103
+ }
104
+ expect(database.database.query<{ count: number }, []>(
105
+ "SELECT count(*) AS count FROM oh_space_purges WHERE space_id = 'purge'",
106
+ ).get()?.count).toBe(1);
107
+ database.close();
108
+ expect(() => new OhSqliteStore({ path, spaceId: "purge" })).toThrow(OhPurgedSpaceError);
109
+ });
110
+
111
+ test("never gives canonical profiles a destructive host capability", async () => {
112
+ const authority = createOhSqliteStoreAuthorityV1({ path: ":memory:",
113
+ profile: OH_CANONICAL_STORE_PROFILE_V1, realmId: "realm:canonical", spaceId: "canonical" });
114
+ await expect(authority.host.purgeWorkingSpace({})).rejects.toThrow(OhProfileError);
115
+ await authority.store.close();
116
+ });
117
+
118
+ test("refuses operation replication for a bound working profile", () => {
119
+ const store = new OhSqliteStore({ path: ":memory:", spaceId: "local-only" });
120
+ store.bind(createOhStoreBindingV1({ profile: OH_WORKING_STORE_PROFILE_V1,
121
+ realmId: "realm:local-only", spaceId: "local-only", v: 1 }));
122
+ expect(() => store.exportOperations()).toThrow(OhProfileError);
123
+ expect(() => store.importOperation({})).toThrow(OhProfileError);
124
+ expect(store.verifyReplay()).toMatchObject({ operations: 0, records: 0 });
125
+ store.close();
126
+ });
127
+ });
@@ -0,0 +1,120 @@
1
+ import { canonicalJson } from "../canonical";
2
+ import {
3
+ createOhStoreBindingV1,
4
+ OH_CANONICAL_STORE_PROFILE_V1,
5
+ OhProfileError,
6
+ parseOhStoreProfileV1,
7
+ type OhChangesPageV1,
8
+ type OhCommitInputV1,
9
+ type OhDependencyClosureV1,
10
+ type OhHeadRefV1,
11
+ type OhHeadV1,
12
+ type OhSnapshotV1,
13
+ type OhSpacePurgeReceiptV1,
14
+ type OhStoreAuthorityV1,
15
+ type OhStoreBindingV1,
16
+ type OhStoreHostControlV1,
17
+ type OhStoreProfileV1,
18
+ type OhStoreV1,
19
+ type OhStoreVerificationV1,
20
+ } from "../store";
21
+ import type { OhOperationV1 } from "../operation";
22
+ import type { OhSqliteDatabase } from "./driver";
23
+ import { OhSqliteStore } from "./store";
24
+
25
+ export type OhSqliteStoreAuthorityOptionsV1 = Readonly<{
26
+ database?: OhSqliteDatabase;
27
+ path?: string;
28
+ profile?: OhStoreProfileV1;
29
+ realmId?: string;
30
+ spaceId?: string;
31
+ }>;
32
+
33
+ export class OhSqliteStorePortV1 implements OhStoreV1 {
34
+ readonly binding: OhStoreBindingV1;
35
+ readonly #authority: OhSqliteStore;
36
+
37
+ constructor(authority: OhSqliteStore, binding: OhStoreBindingV1) {
38
+ const persisted = authority.bind(binding);
39
+ if (canonicalJson(persisted) !== canonicalJson(binding)) {
40
+ throw new OhProfileError("The SQLite authority returned a different store binding.");
41
+ }
42
+ this.#authority = authority;
43
+ this.binding = persisted;
44
+ }
45
+
46
+ async head(): Promise<OhHeadV1> {
47
+ return this.#authority.head();
48
+ }
49
+
50
+ async snapshot(options: Readonly<{
51
+ head?: OhHeadRefV1;
52
+ maximumRecords?: number;
53
+ }> = {}): Promise<OhSnapshotV1> {
54
+ return this.#authority.snapshotAtHead(options);
55
+ }
56
+
57
+ async changesSince(
58
+ from: OhHeadRefV1,
59
+ options: Readonly<{ limit?: number; through?: OhHeadRefV1 }> = {},
60
+ ): Promise<OhChangesPageV1> {
61
+ return this.#authority.changesSince(from, options);
62
+ }
63
+
64
+ async commit(input: OhCommitInputV1): Promise<OhOperationV1> {
65
+ return this.#authority.commit(input);
66
+ }
67
+
68
+ async exportDependencyClosure(input: Readonly<{
69
+ head?: OhHeadRefV1;
70
+ maximumRecords?: number;
71
+ roots: readonly string[];
72
+ }>): Promise<OhDependencyClosureV1> {
73
+ return this.#authority.exportDependencyClosure({ binding: this.binding, ...input });
74
+ }
75
+
76
+ async verify(): Promise<OhStoreVerificationV1> {
77
+ const verified = this.#authority.verifyReplay();
78
+ return { head: verified.head, integrity: "verified", operations: verified.operations,
79
+ records: verified.records, v: 1 };
80
+ }
81
+
82
+ async close(): Promise<void> {
83
+ this.#authority.close();
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Binds a Bun SQLite authority to the promise-based store port. Retain the
89
+ * returned `host` object in trusted control-plane code; pass only `store` to
90
+ * ordinary consumers.
91
+ */
92
+ export function createOhSqliteStoreAuthorityV1(
93
+ options: OhSqliteStoreAuthorityOptionsV1 = {},
94
+ ): OhStoreAuthorityV1 {
95
+ const profile = parseOhStoreProfileV1(options.profile ?? OH_CANONICAL_STORE_PROFILE_V1);
96
+ if (profile === null) throw new TypeError("Invalid SQLite store profile.");
97
+ const spaceId = options.spaceId ?? "default";
98
+ const binding = createOhStoreBindingV1({ profile,
99
+ realmId: options.realmId ?? `realm:${spaceId}`, spaceId, v: 1 });
100
+ const authority = new OhSqliteStore({
101
+ ...(options.database === undefined ? {} : { database: options.database }),
102
+ ...(options.path === undefined ? {} : { path: options.path }),
103
+ spaceId,
104
+ });
105
+ const store = new OhSqliteStorePortV1(authority, binding);
106
+ let purge: OhSpacePurgeReceiptV1 | null = null;
107
+ const host: OhStoreHostControlV1 = Object.freeze({
108
+ binding,
109
+ purgeWorkingSpace: async (input: Readonly<{ purgedAt?: string }>) => {
110
+ if (profile.profileKind !== "working" || !profile.capabilities.wholeSpacePurge) {
111
+ throw new OhProfileError("This host handle is not bound to a purgeable working profile.");
112
+ }
113
+ if (purge !== null) return purge;
114
+ purge = authority.purgeWorkingSpace(binding, input.purgedAt);
115
+ authority.close();
116
+ return purge;
117
+ },
118
+ });
119
+ return Object.freeze({ host, store });
120
+ }
@@ -0,0 +1,68 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { createOhSqliteRuntime } from "./runtime";
4
+
5
+ describe("Bun SQLite runtime initialization", () => {
6
+ test("selects Homebrew SQLite before constructing the first database", () => {
7
+ const events: string[] = [];
8
+ const library = "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib";
9
+ const runtime = createOhSqliteRuntime({
10
+ exists: (path) => {
11
+ events.push(`exists:${path}`);
12
+ return path === library;
13
+ },
14
+ open: (path, options) => {
15
+ events.push(`open:${path}:${String(options.create)}:${String(options.strict)}`);
16
+ return { path };
17
+ },
18
+ platform: "darwin",
19
+ setCustomSQLite: (path) => {
20
+ events.push(`set:${path}`);
21
+ return true;
22
+ },
23
+ });
24
+
25
+ expect(runtime.customLibrary).toBe(library);
26
+ expect(events).toEqual([`exists:${library}`, `set:${library}`]);
27
+ expect(runtime.open("authority.sqlite")).toEqual({ path: "authority.sqlite" });
28
+ expect(events).toEqual([
29
+ `exists:${library}`,
30
+ `set:${library}`,
31
+ "open:authority.sqlite:true:true",
32
+ ]);
33
+ });
34
+
35
+ test("falls back across standard macOS paths without trying duplicates", () => {
36
+ const attempted: string[] = [];
37
+ const appleSilicon = "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib";
38
+ const intel = "/usr/local/opt/sqlite/lib/libsqlite3.dylib";
39
+ const runtime = createOhSqliteRuntime({
40
+ exists: () => true,
41
+ open: () => ({}),
42
+ platform: "darwin",
43
+ setCustomSQLite: (path) => {
44
+ attempted.push(path);
45
+ return path === intel;
46
+ },
47
+ });
48
+
49
+ expect(attempted).toEqual([appleSilicon, intel]);
50
+ expect(runtime.customLibrary).toBe(intel);
51
+ });
52
+
53
+ test("does not inspect or configure custom libraries outside macOS", () => {
54
+ let inspected = false;
55
+ let configured = false;
56
+ const runtime = createOhSqliteRuntime({
57
+ exists: () => { inspected = true; return true; },
58
+ open: (path) => path,
59
+ platform: "linux",
60
+ setCustomSQLite: () => { configured = true; return true; },
61
+ });
62
+
63
+ expect(runtime.customLibrary).toBeNull();
64
+ expect(inspected).toBe(false);
65
+ expect(configured).toBe(false);
66
+ expect(runtime.open(":memory:")).toBe(":memory:");
67
+ });
68
+ });
@@ -0,0 +1,53 @@
1
+ type SqliteOpenOptions = Readonly<{ create: true; strict: true }>;
2
+
3
+ const MACOS_SQLITE_LIBRARY_CANDIDATES = Object.freeze([
4
+ "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib",
5
+ "/usr/local/opt/sqlite/lib/libsqlite3.dylib",
6
+ ]);
7
+
8
+ export type OhSqliteRuntimeDependencies<TDatabase> = Readonly<{
9
+ exists: (path: string) => boolean;
10
+ open: (path: string, options: SqliteOpenOptions) => TDatabase;
11
+ platform: NodeJS.Platform;
12
+ setCustomSQLite: (path: string) => boolean;
13
+ }>;
14
+
15
+ export type OhSqliteRuntime<TDatabase> = Readonly<{
16
+ customLibrary: string | null;
17
+ open: (path: string) => TDatabase;
18
+ }>;
19
+
20
+ function macosSqliteLibraryCandidates(): readonly string[] {
21
+ return MACOS_SQLITE_LIBRARY_CANDIDATES;
22
+ }
23
+
24
+ /**
25
+ * Bun can replace macOS' extension-disabled system SQLite only before the first
26
+ * Database is constructed. Creating the opener here makes that ordering an
27
+ * invariant rather than leaving optional semantic backends to configure it
28
+ * after the authoritative store is already open.
29
+ */
30
+ export function createOhSqliteRuntime<TDatabase>(
31
+ dependencies: OhSqliteRuntimeDependencies<TDatabase>,
32
+ ): OhSqliteRuntime<TDatabase> {
33
+ let customLibrary: string | null = null;
34
+ if (dependencies.platform === "darwin") {
35
+ for (const candidate of macosSqliteLibraryCandidates()) {
36
+ if (!dependencies.exists(candidate)) continue;
37
+ try {
38
+ if (!dependencies.setCustomSQLite(candidate)) continue;
39
+ customLibrary = candidate;
40
+ break;
41
+ } catch {
42
+ // SQLite may already be loaded by another consumer. Oh remains usable
43
+ // without extensions; the optional semantic backend will report its
44
+ // own precise availability error when opened.
45
+ }
46
+ }
47
+ }
48
+
49
+ return Object.freeze({
50
+ customLibrary,
51
+ open: (path: string) => dependencies.open(path, { create: true, strict: true }),
52
+ });
53
+ }
@@ -0,0 +1,295 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+
6
+ import { createKnowledgeGraphRecordV1 } from "../graph";
7
+ import { createOhStoreBindingV1, OH_WORKING_STORE_PROFILE_V1 } from "../store";
8
+ import { OhConflictError, OhDependencyError, OhIntegrityError, OhSqliteStore } from "./store";
9
+
10
+ const roots: string[] = [];
11
+ afterEach(async () => {
12
+ await Promise.all(roots.splice(0).map((root) => rm(root, { force: true, recursive: true })));
13
+ });
14
+
15
+ async function databasePath(): Promise<string> {
16
+ const root = await mkdtemp(join(tmpdir(), "oh-store-test-"));
17
+ roots.push(root);
18
+ return join(root, "oh.sqlite");
19
+ }
20
+
21
+ const record = (key: string, name: string, dependencies: readonly string[] = []) =>
22
+ createKnowledgeGraphRecordV1({ dependencies, key, kind: "entity", v: 1, value: { name } });
23
+
24
+ describe("Oh SQLite authority", () => {
25
+ test("commits, reopens, searches, and reproduces the exact head by replay", async () => {
26
+ const path = await databasePath();
27
+ const store = new OhSqliteStore({ path });
28
+ const first = record("entity:ada", "Ada Lovelace");
29
+ const operation = store.commit({ actorId: "agent.test", changes: [{ kind: "put", record: first, v: 1 }],
30
+ expectedHead: store.head(), instant: "2026-08-27T12:00:00.000Z", operationId: "op_first" });
31
+ expect(operation.sequence).toBe(1);
32
+ expect(store.searchKeyword("Lovelace")[0]?.key).toBe(first.key);
33
+ expect(store.verifyReplay()).toMatchObject({ operations: 1, records: 1, sqliteIntegrity: "ok" });
34
+ store.close();
35
+
36
+ const reopened = new OhSqliteStore({ path });
37
+ expect(reopened.get(first.key)).toEqual(first);
38
+ expect(reopened.verifyReplay().head.operationSha256).toBe(operation.operationSha256);
39
+ reopened.close();
40
+ });
41
+
42
+ test("enforces compare-and-swap across independent connections", async () => {
43
+ const path = await databasePath();
44
+ const first = new OhSqliteStore({ path });
45
+ const second = new OhSqliteStore({ path });
46
+ const stale = second.head();
47
+ first.commit({ actorId: "agent.one", changes: [{ kind: "put", record: record("entity:first", "First"), v: 1 }],
48
+ expectedHead: first.head(), operationId: "op_one" });
49
+ expect(() => second.commit({ actorId: "agent.two", changes: [{ kind: "put", record: record("entity:second", "Second"), v: 1 }],
50
+ expectedHead: stale, operationId: "op_two" })).toThrow(OhConflictError);
51
+ expect(second.head().sequence).toBe(1);
52
+ first.close(); second.close();
53
+ });
54
+
55
+ test("makes operation IDs idempotent but never aliases different content", () => {
56
+ const store = new OhSqliteStore({ path: ":memory:" });
57
+ const input = { actorId: "agent.test", changes: [{ kind: "put" as const, record: record("entity:a", "A"), v: 1 as const }],
58
+ expectedHead: store.head(), operationId: "op_retry" };
59
+ const first = store.commit(input);
60
+ expect(store.commit(input)).toEqual(first);
61
+ expect(() => store.commit({ ...input, changes: [{ kind: "put", record: record("entity:a", "Other"), v: 1 }] }))
62
+ .toThrow(OhConflictError);
63
+ expect(store.log()).toHaveLength(1);
64
+ store.close();
65
+ });
66
+
67
+ test("rejects orphaned and cross-space rows as idempotent operations", () => {
68
+ const changes = [{ kind: "put" as const, record: record("entity:orphan", "Orphan"), v: 1 as const }];
69
+ const source = new OhSqliteStore({ path: ":memory:", spaceId: "orphan-target" });
70
+ const operation = source.commit({ actorId: "agent.test", changes, expectedHead: source.head(),
71
+ operationId: "op_orphan" });
72
+ const target = new OhSqliteStore({ path: ":memory:", spaceId: "orphan-target" });
73
+ target.database.query(`INSERT INTO oh_operations(operation_sha256, space_id, sequence,
74
+ operation_id, parent_operation_sha256, graph_revision_sha256, records_sha256,
75
+ operation_json, instant) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
76
+ operation.operationSha256, operation.spaceId, operation.sequence, operation.operationId,
77
+ operation.parentOperationSha256, operation.graphRevisionSha256, operation.recordsSha256,
78
+ JSON.stringify(operation), operation.instant,
79
+ );
80
+ expect(() => target.commit({ actorId: operation.actorId, changes, expectedHead: target.head(),
81
+ operationId: operation.operationId })).toThrow(OhIntegrityError);
82
+ expect(() => target.importOperation(operation)).toThrow(OhIntegrityError);
83
+ expect(target.head().sequence).toBe(0);
84
+ source.close(); target.close();
85
+
86
+ const alienSource = new OhSqliteStore({ path: ":memory:", spaceId: "alien-source" });
87
+ const alien = alienSource.commit({ actorId: "agent.test", changes, expectedHead: alienSource.head(),
88
+ operationId: "op_alien" });
89
+ const alienTarget = new OhSqliteStore({ path: ":memory:", spaceId: "alien-target" });
90
+ alienTarget.database.query(`INSERT INTO oh_operations(operation_sha256, space_id, sequence,
91
+ operation_id, parent_operation_sha256, graph_revision_sha256, records_sha256,
92
+ operation_json, instant) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
93
+ alien.operationSha256, alienTarget.spaceId, alien.sequence, alien.operationId,
94
+ alien.parentOperationSha256, alien.graphRevisionSha256, alien.recordsSha256,
95
+ JSON.stringify(alien), alien.instant,
96
+ );
97
+ alienTarget.database.query(`UPDATE oh_spaces SET generation = 1, head_operation_sha256 = ?,
98
+ graph_revision_sha256 = ?, records_sha256 = ?, sequence = 1 WHERE space_id = ?`).run(
99
+ alien.operationSha256, alien.graphRevisionSha256, alien.recordsSha256, alienTarget.spaceId,
100
+ );
101
+ expect(() => alienTarget.commit({ actorId: alien.actorId, changes,
102
+ expectedHead: alienTarget.head(), operationId: alien.operationId })).toThrow(OhIntegrityError);
103
+ alienSource.close(); alienTarget.close();
104
+ });
105
+
106
+ test("refuses to advance a space whose duplicated binding or current head drifted", () => {
107
+ const store = new OhSqliteStore({ path: ":memory:", spaceId: "authority-drift" });
108
+ const binding = createOhStoreBindingV1({ profile: OH_WORKING_STORE_PROFILE_V1,
109
+ realmId: "realm:authority-drift", spaceId: store.spaceId, v: 1 });
110
+ store.bind(binding);
111
+ store.database.query("UPDATE oh_space_bindings SET realm_id = ? WHERE space_id = ?")
112
+ .run("realm:alien", store.spaceId);
113
+ expect(() => store.binding()).toThrow(OhIntegrityError);
114
+ store.database.query("UPDATE oh_space_bindings SET realm_id = ? WHERE space_id = ?")
115
+ .run(binding.realmId, store.spaceId);
116
+ store.commit({ actorId: "agent.test", changes: [{ kind: "put",
117
+ record: record("entity:one", "One"), v: 1 }], expectedHead: store.head(), operationId: "op_one" });
118
+ store.database.query("UPDATE oh_spaces SET graph_revision_sha256 = ? WHERE space_id = ?")
119
+ .run("f".repeat(64), store.spaceId);
120
+ const drifted = store.head();
121
+ expect(() => store.commit({ actorId: "agent.test", changes: [{ kind: "put",
122
+ record: record("entity:two", "Two"), v: 1 }], expectedHead: drifted,
123
+ operationId: "op_two" })).toThrow(OhIntegrityError);
124
+ expect(store.head().sequence).toBe(1);
125
+ store.close();
126
+ });
127
+
128
+ test("checks final dependency closure before changing durable state", () => {
129
+ const store = new OhSqliteStore({ path: ":memory:" });
130
+ expect(() => store.commit({ actorId: "agent.test", changes: [{ kind: "put",
131
+ record: record("entity:child", "Child", ["entity:missing"]), v: 1 }],
132
+ expectedHead: store.head(), operationId: "op_missing" })).toThrow(OhDependencyError);
133
+ expect(store.head().sequence).toBe(0);
134
+ const parent = record("entity:parent", "Parent");
135
+ const child = record("entity:child", "Child", [parent.key]);
136
+ store.commit({ actorId: "agent.test", changes: [{ kind: "put", record: child, v: 1 },
137
+ { kind: "put", record: parent, v: 1 }], expectedHead: store.head(), operationId: "op_both" });
138
+ expect(() => store.commit({ actorId: "agent.test", changes: [{ key: parent.key, kind: "tombstone",
139
+ priorSha256: parent.recordSha256, v: 1 }], expectedHead: store.head(), operationId: "op_bad_delete" }))
140
+ .toThrow(OhDependencyError);
141
+ expect(store.head().sequence).toBe(1);
142
+ store.close();
143
+ });
144
+
145
+ test("rolls an interrupted write back atomically", () => {
146
+ const store = new OhSqliteStore({ path: ":memory:" });
147
+ store.database.exec(`CREATE TRIGGER test_abort_record BEFORE INSERT ON oh_records
148
+ BEGIN SELECT RAISE(ABORT, 'simulated interruption'); END`);
149
+ expect(() => store.commit({ actorId: "agent.test", changes: [{ kind: "put",
150
+ record: record("entity:a", "A"), v: 1 }], expectedHead: store.head(), operationId: "op_crash" })).toThrow();
151
+ store.database.exec("DROP TRIGGER test_abort_record");
152
+ expect(store.head().sequence).toBe(0);
153
+ expect(store.log()).toHaveLength(0);
154
+ expect(store.verifyReplay()).toMatchObject({ operations: 0, records: 0 });
155
+ store.close();
156
+ });
157
+
158
+ test("detects materialized-state tampering", () => {
159
+ const store = new OhSqliteStore({ path: ":memory:" });
160
+ store.commit({ actorId: "agent.test", changes: [{ kind: "put", record: record("entity:a", "A"), v: 1 }],
161
+ expectedHead: store.head(), operationId: "op_one" });
162
+ store.database.query("UPDATE oh_records SET record_json = ? WHERE space_id = ? AND record_key = ?")
163
+ .run('{"dependencies":[],"key":"entity:a","kind":"entity","recordSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","v":1,"value":{"name":"A"}}', store.spaceId, "entity:a");
164
+ expect(() => store.verifyReplay()).toThrow(OhIntegrityError);
165
+ store.close();
166
+ });
167
+
168
+ test("verifies dependency and operation-record materializations", () => {
169
+ const store = new OhSqliteStore({ path: ":memory:" });
170
+ const parent = record("entity:parent", "Parent");
171
+ const child = record("entity:child", "Child", [parent.key]);
172
+ const operation = store.commit({ actorId: "agent.test", changes: [
173
+ { kind: "put", record: child, v: 1 }, { kind: "put", record: parent, v: 1 },
174
+ ], expectedHead: store.head(), operationId: "op_materialized" });
175
+ store.database.query("DELETE FROM oh_dependencies WHERE space_id = ?").run(store.spaceId);
176
+ expect(() => store.verifyReplay()).toThrow("Materialized dependencies");
177
+ store.database.query("INSERT INTO oh_dependencies(space_id, record_key, dependency_key) VALUES (?, ?, ?)")
178
+ .run(store.spaceId, child.key, parent.key);
179
+ store.database.query("DELETE FROM oh_operation_records WHERE operation_sha256 = ?").run(operation.operationSha256);
180
+ expect(() => store.verifyReplay()).toThrow("Materialized operation records");
181
+ store.close();
182
+ });
183
+
184
+ test("verifies duplicated operation columns against canonical envelopes", () => {
185
+ const store = new OhSqliteStore({ path: ":memory:" });
186
+ store.commit({ actorId: "agent.test", changes: [{ kind: "put",
187
+ record: record("entity:a", "A"), v: 1 }], expectedHead: store.head(), operationId: "op_columns" });
188
+ store.database.query("UPDATE oh_operations SET operation_id = ? WHERE space_id = ?")
189
+ .run("op_alien", store.spaceId);
190
+ expect(() => store.verifyReplay()).toThrow(OhIntegrityError);
191
+ store.close();
192
+ });
193
+
194
+ test("checks canonical operation bytes beyond the export batch ceiling", () => {
195
+ const store = new OhSqliteStore({ path: ":memory:" });
196
+ for (let index = 0; index < 1001; index += 1) {
197
+ store.commit({ actorId: "agent.test", changes: [{ kind: "put",
198
+ record: record("entity:a", `A ${index}`), v: 1 }], expectedHead: store.head(),
199
+ operationId: `op_${String(index).padStart(4, "0")}` });
200
+ }
201
+ store.database.query(`UPDATE oh_operations SET operation_json = ' ' || operation_json
202
+ WHERE space_id = ? AND sequence = 1`).run(store.spaceId);
203
+ expect(() => store.verifyReplay()).toThrow(OhIntegrityError);
204
+ store.close();
205
+ });
206
+
207
+ test("never reports a feed page that omits its tail or hides a bad sentinel", () => {
208
+ const tail = new OhSqliteStore({ path: ":memory:", spaceId: "feed-tail" });
209
+ for (let index = 1; index <= 3; index += 1) {
210
+ tail.commit({ actorId: "agent.test", changes: [{ kind: "put",
211
+ record: record(`entity:tail-${index}`, `Tail ${index}`), v: 1 }], expectedHead: tail.head(),
212
+ operationId: `op_tail_${index}` });
213
+ }
214
+ tail.database.exec("PRAGMA foreign_keys = OFF");
215
+ tail.database.query("DELETE FROM oh_operations WHERE space_id = ? AND sequence = 3").run(tail.spaceId);
216
+ tail.database.exec("PRAGMA foreign_keys = ON");
217
+ expect(() => tail.changesSince({ operationSha256: null, sequence: 0 }, { limit: 3 }))
218
+ .toThrow(OhIntegrityError);
219
+ tail.close();
220
+
221
+ const sentinel = new OhSqliteStore({ path: ":memory:", spaceId: "feed-sentinel" });
222
+ for (let index = 1; index <= 3; index += 1) {
223
+ sentinel.commit({ actorId: "agent.test", changes: [{ kind: "put",
224
+ record: record(`entity:sentinel-${index}`, `Sentinel ${index}`), v: 1 }],
225
+ expectedHead: sentinel.head(), operationId: `op_sentinel_${index}` });
226
+ }
227
+ sentinel.database.exec("PRAGMA foreign_keys = OFF");
228
+ sentinel.database.query("DELETE FROM oh_operations WHERE space_id = ? AND sequence = 2")
229
+ .run(sentinel.spaceId);
230
+ sentinel.database.exec("PRAGMA foreign_keys = ON");
231
+ expect(() => sentinel.changesSince({ operationSha256: null, sequence: 0 }, { limit: 1 }))
232
+ .toThrow(OhIntegrityError);
233
+ sentinel.close();
234
+ });
235
+
236
+ test("rolls a working-space purge back when a payload delete is masked", () => {
237
+ const store = new OhSqliteStore({ path: ":memory:", spaceId: "purge-postcondition" });
238
+ const binding = createOhStoreBindingV1({ profile: OH_WORKING_STORE_PROFILE_V1,
239
+ realmId: "realm:purge-postcondition", spaceId: store.spaceId, v: 1 });
240
+ store.bind(binding);
241
+ store.commit({ actorId: "agent.test", changes: [{ kind: "put",
242
+ record: record("entity:private", "Private"), v: 1 }], expectedHead: store.head(),
243
+ operationId: "op_private" });
244
+ store.database.exec(`CREATE TRIGGER mask_private_operation_delete BEFORE DELETE ON oh_operations
245
+ BEGIN SELECT RAISE(IGNORE); END`);
246
+ store.database.exec(`CREATE TRIGGER mask_private_space_delete BEFORE DELETE ON oh_spaces
247
+ BEGIN SELECT RAISE(IGNORE); END`);
248
+ expect(() => store.purgeWorkingSpace(binding, "2026-08-29T13:00:00.000Z"))
249
+ .toThrow(OhIntegrityError);
250
+ expect(store.database.query<{ count: number }, []>(
251
+ "SELECT count(*) AS count FROM oh_space_purges").get()?.count).toBe(0);
252
+ expect(store.get("entity:private")).not.toBeNull();
253
+ expect(store.head().sequence).toBe(1);
254
+ store.close();
255
+ });
256
+
257
+ test("cross-checks every stored purge receipt column before refusing resurrection", () => {
258
+ const store = new OhSqliteStore({ path: ":memory:", spaceId: "purge-columns" });
259
+ const binding = createOhStoreBindingV1({ profile: OH_WORKING_STORE_PROFILE_V1,
260
+ realmId: "realm:purge-columns", spaceId: store.spaceId, v: 1 });
261
+ store.bind(binding);
262
+ store.purgeWorkingSpace(binding, "2026-08-29T13:00:00.000Z");
263
+ store.database.query("UPDATE oh_space_purges SET prior_sequence = 99 WHERE space_id = ?")
264
+ .run(store.spaceId);
265
+ expect(() => store.ensureSpace()).toThrow(OhIntegrityError);
266
+ store.close();
267
+ });
268
+
269
+ test("imports an exact operation once and rejects a fork", () => {
270
+ const source = new OhSqliteStore({ path: ":memory:" });
271
+ const target = new OhSqliteStore({ path: ":memory:" });
272
+ const operation = source.commit({ actorId: "agent.test", changes: [{ kind: "put", record: record("entity:a", "A"), v: 1 }],
273
+ expectedHead: source.head(), operationId: "op_one" });
274
+ expect(target.importOperation(operation).imported).toBe(true);
275
+ expect(target.importOperation(operation).imported).toBe(false);
276
+ const fork = source.commit({ actorId: "agent.test", changes: [{ kind: "put", record: record("entity:b", "B"), v: 1 }],
277
+ expectedHead: source.head(), operationId: "op_two" });
278
+ target.commit({ actorId: "agent.other", changes: [{ kind: "put", record: record("entity:c", "C"), v: 1 }],
279
+ expectedHead: target.head(), operationId: "op_other" });
280
+ expect(() => target.importOperation(fork)).toThrow(OhConflictError);
281
+ source.close(); target.close();
282
+ });
283
+
284
+ test("keeps a thousand-record batch bounded", () => {
285
+ const store = new OhSqliteStore({ path: ":memory:" });
286
+ const changes = Array.from({ length: 1000 }, (_, index) => ({ kind: "put" as const,
287
+ record: record(`entity:item-${String(index).padStart(4, "0")}`, `Item ${index}`), v: 1 as const }));
288
+ const started = performance.now();
289
+ store.commit({ actorId: "agent.test", changes, expectedHead: store.head(), operationId: "op_batch" });
290
+ expect(store.snapshotRecords()).toHaveLength(1000);
291
+ expect(() => store.snapshotRecords(65_537)).toThrow("65536");
292
+ expect(performance.now() - started).toBeLessThan(5000);
293
+ store.close();
294
+ });
295
+ });