@hraness/oh 0.3.0 → 0.3.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.
@@ -85,6 +85,22 @@ class InterleavingLibSqlClient extends SqliteCompatibleLibSqlClient {
85
85
  }
86
86
  }
87
87
 
88
+ class PublishedHeadInterleavingLibSqlClient extends SqliteCompatibleLibSqlClient {
89
+ afterHeadRead: (() => Promise<void>) | null = null;
90
+
91
+ override async execute(statement: OhLibSqlStatementV1 | string): Promise<OhLibSqlResultV1> {
92
+ const result = await super.execute(statement);
93
+ const sql = typeof statement === "string" ? statement : statement.sql;
94
+ if (/FROM\s+oh_semantic_heads\s+WHERE\s+authority_id\s*=\s*\?/iu.test(sql)
95
+ && this.afterHeadRead !== null) {
96
+ const hook = this.afterHeadRead;
97
+ this.afterHeadRead = null;
98
+ await hook();
99
+ }
100
+ return result;
101
+ }
102
+ }
103
+
88
104
  const instant1 = "2026-08-31T12:00:00.000Z";
89
105
  const instant2 = "2026-08-31T12:01:00.000Z";
90
106
  const instant3 = "2026-08-31T12:02:00.000Z";
@@ -131,7 +147,11 @@ function document(
131
147
  async function bootstrapped(): Promise<SqliteCompatibleLibSqlClient> {
132
148
  const client = new SqliteCompatibleLibSqlClient();
133
149
  expect(await bootstrapOhLibSqlSemanticCacheV1(client, { appliedAt: instant1 }))
134
- .toMatchObject({ schemaVersion: 1, v: 1 });
150
+ .toEqual({
151
+ schemaSha256: "d9903735e900e08a8c49c9ad36ea8f277b1689117ef88b9a2c6b906456dfbace" as Sha256Hex,
152
+ schemaVersion: 1,
153
+ v: 1,
154
+ });
135
155
  return client;
136
156
  }
137
157
 
@@ -151,6 +171,51 @@ function authority(
151
171
  }
152
172
 
153
173
  describe("libSQL derived semantic cache", () => {
174
+ test("reproduces the fixed released V1 digest fixture", async () => {
175
+ const fixture = await Bun.file(new URL(
176
+ "../spec/v1/libsql-semantic-digest-fixture-v1.json",
177
+ import.meta.url,
178
+ )).json() as Readonly<{
179
+ authorityId: string;
180
+ authoritySha256: Sha256Hex;
181
+ content: string;
182
+ generation: number;
183
+ generationSha256: Sha256Hex;
184
+ inputSha256: Sha256Hex;
185
+ membershipSha256: Sha256Hex;
186
+ recordKey: string;
187
+ recordSha256: Sha256Hex;
188
+ title: string;
189
+ v: 1;
190
+ }>;
191
+ const client = await bootstrapped();
192
+ const cache = await openOhLibSqlSemanticCacheV1(client);
193
+ const calls: string[][] = [];
194
+ const staged = await cache.stage({
195
+ authorityId: fixture.authorityId,
196
+ authoritySha256: fixture.authoritySha256,
197
+ createdAt: instant1,
198
+ documents: [{
199
+ content: fixture.content,
200
+ key: fixture.recordKey,
201
+ recordSha256: fixture.recordSha256,
202
+ title: fixture.title,
203
+ v: fixture.v,
204
+ }],
205
+ embeddingClient: embeddingClient(calls),
206
+ generation: fixture.generation,
207
+ });
208
+ expect(staged).toMatchObject({
209
+ generationSha256: fixture.generationSha256,
210
+ membershipSha256: fixture.membershipSha256,
211
+ v: 1,
212
+ });
213
+ expect(calls).toHaveLength(1);
214
+ expect(sha256Hex(calls[0]?.[0] ?? "")).toBe(fixture.inputSha256);
215
+ await cache.close();
216
+ client.close();
217
+ });
218
+
154
219
  test("requires explicit bootstrap and refuses a partial or drifted schema", async () => {
155
220
  const empty = new SqliteCompatibleLibSqlClient();
156
221
  await expect(openOhLibSqlSemanticCacheV1(empty)).rejects.toMatchObject({
@@ -197,6 +262,7 @@ describe("libSQL derived semantic cache", () => {
197
262
  document("memory:two", "needle-beta"),
198
263
  ] as const;
199
264
  const firstAuthoritySha256 = digest("authority:first");
265
+ expect(await cache.publishedHead({ authorityId })).toBeNull();
200
266
  const first = await cache.stage({
201
267
  authorityId,
202
268
  authoritySha256: firstAuthoritySha256,
@@ -207,6 +273,7 @@ describe("libSQL derived semantic cache", () => {
207
273
  });
208
274
  expect(first).toMatchObject({ chunks: 2, documents: 2, embedded: 2, reused: 0 });
209
275
  expect(calls).toHaveLength(1);
276
+ expect(await cache.publishedHead({ authorityId })).toBeNull();
210
277
  expect(await cache.stage({
211
278
  authorityId,
212
279
  authoritySha256: firstAuthoritySha256,
@@ -235,6 +302,17 @@ describe("libSQL derived semantic cache", () => {
235
302
  generation: 1,
236
303
  publishedAt: instant1,
237
304
  })).toMatchObject({ published: true });
305
+ expect(await cache.publishedHead({ authorityId })).toEqual({
306
+ authorityId,
307
+ authoritySha256: firstAuthoritySha256,
308
+ generation: 1,
309
+ generationSha256: first.generationSha256,
310
+ membershipSha256: first.membershipSha256,
311
+ profileSha256: OH_CLOUDFLARE_EMBEDDING_PROFILE_V1.profileSha256,
312
+ publishedAt: instant1,
313
+ rendererSha256: expect.stringMatching(/^[a-f0-9]{64}$/u),
314
+ v: 1,
315
+ });
238
316
  expect(await cache.publish({
239
317
  authorityId,
240
318
  expectedPublishedGeneration: null,
@@ -302,6 +380,12 @@ describe("libSQL derived semantic cache", () => {
302
380
  generation: 2,
303
381
  publishedAt: instant2,
304
382
  })).toMatchObject({ published: true });
383
+ expect(await cache.publishedHead({ authorityId })).toMatchObject({
384
+ authoritySha256: secondAuthoritySha256,
385
+ generation: 2,
386
+ publishedAt: instant2,
387
+ v: 1,
388
+ });
305
389
  expect(await cache.search({
306
390
  authority: authority(authorityId, 1, firstAuthoritySha256, firstDocuments),
307
391
  embeddingClient: embedder,
@@ -361,6 +445,9 @@ describe("libSQL derived semantic cache", () => {
361
445
  embeddingClient: embedder,
362
446
  query: "needle-alpha",
363
447
  })).toEqual([]);
448
+ expect(await cache.publishedHead({
449
+ authorityId: "agent:session-a:epoch-1",
450
+ })).toBeNull();
364
451
 
365
452
  expect((await cache.purgeAuthority({
366
453
  authorityId: "agent:session-b:epoch-1",
@@ -472,6 +559,75 @@ describe("libSQL derived semantic cache", () => {
472
559
  embeddingClient: embedder,
473
560
  query: "needle-alpha",
474
561
  })).rejects.toBeInstanceOf(OhLibSqlSemanticError);
562
+ await expect(cache.publishedHead({ authorityId: "INVALID" }))
563
+ .rejects.toMatchObject({ code: "invalid-input" });
564
+ await cache.close();
565
+ client.close();
566
+ });
567
+
568
+ test("rejects a published pointer that diverges from its immutable generation", async () => {
569
+ const client = await bootstrapped();
570
+ const cache = await openOhLibSqlSemanticCacheV1(client);
571
+ const authorityId = "agent:published-head-integrity:epoch-1";
572
+ await cache.stage({
573
+ authorityId,
574
+ authoritySha256: digest("published-head-integrity"),
575
+ createdAt: instant1,
576
+ documents: [document("memory:one", "needle-alpha")],
577
+ embeddingClient: embeddingClient([]),
578
+ generation: 1,
579
+ });
580
+ await cache.publish({
581
+ authorityId,
582
+ expectedPublishedGeneration: null,
583
+ generation: 1,
584
+ publishedAt: instant1,
585
+ });
586
+ client.database.query(`UPDATE oh_semantic_heads SET authority_sha256 = ?
587
+ WHERE authority_id = ?`).run(digest("forged-head"), authorityId);
588
+ await expect(cache.publishedHead({ authorityId }))
589
+ .rejects.toMatchObject({ code: "integrity" });
590
+ await cache.close();
591
+ client.close();
592
+ });
593
+
594
+ test("does not return a head that changed during its bounded read", async () => {
595
+ const client = new PublishedHeadInterleavingLibSqlClient();
596
+ await bootstrapOhLibSqlSemanticCacheV1(client, { appliedAt: instant1 });
597
+ const cache = await openOhLibSqlSemanticCacheV1(client);
598
+ const authorityId = "agent:published-head-race:epoch-1";
599
+ const embedder = embeddingClient([]);
600
+ for (const generation of [1, 2] as const) {
601
+ await cache.stage({
602
+ authorityId,
603
+ authoritySha256: digest(`published-head-race:${generation}`),
604
+ createdAt: generation === 1 ? instant1 : instant2,
605
+ documents: [document(
606
+ `memory:${generation}`,
607
+ generation === 1 ? "needle-alpha" : "needle-beta",
608
+ )],
609
+ embeddingClient: embedder,
610
+ generation,
611
+ });
612
+ }
613
+ await cache.publish({
614
+ authorityId,
615
+ expectedPublishedGeneration: null,
616
+ generation: 1,
617
+ publishedAt: instant1,
618
+ });
619
+ client.afterHeadRead = async () => {
620
+ await cache.publish({
621
+ authorityId,
622
+ expectedPublishedGeneration: 1,
623
+ generation: 2,
624
+ publishedAt: instant2,
625
+ });
626
+ };
627
+ await expect(cache.publishedHead({ authorityId }))
628
+ .rejects.toMatchObject({ code: "conflict" });
629
+ expect(await cache.publishedHead({ authorityId }))
630
+ .toMatchObject({ generation: 2, publishedAt: instant2 });
475
631
  await cache.close();
476
632
  client.close();
477
633
  });
@@ -79,6 +79,19 @@ export type OhSemanticPublishResultV1 = Readonly<{
79
79
  v: 1;
80
80
  }>;
81
81
 
82
+ /** The exact, currently published cache pointer for one semantic authority. */
83
+ export type OhSemanticPublishedHeadV1 = Readonly<{
84
+ authorityId: string;
85
+ authoritySha256: Sha256Hex;
86
+ generation: number;
87
+ generationSha256: Sha256Hex;
88
+ membershipSha256: Sha256Hex;
89
+ profileSha256: Sha256Hex;
90
+ publishedAt: string;
91
+ rendererSha256: Sha256Hex;
92
+ v: 1;
93
+ }>;
94
+
82
95
  export type OhSemanticSearchResultV1 = Readonly<{
83
96
  chunkOrdinal: number;
84
97
  key: string;
@@ -749,6 +762,44 @@ export class OhLibSqlSemanticCacheV1 {
749
762
  if (this.#closeClient) this.#client.close?.();
750
763
  }
751
764
 
765
+ /**
766
+ * Reads the current compare-and-swap base without exposing cache rows or
767
+ * private source text. An absent or purged authority has no published head.
768
+ */
769
+ async publishedHead(input: Readonly<{
770
+ authorityId: string;
771
+ }>): Promise<OhSemanticPublishedHeadV1 | null> {
772
+ this.#open();
773
+ const authorityId = parseAuthorityId(input.authorityId);
774
+ if (await readPurge(this.#client, authorityId) !== null) return null;
775
+ const head = await readHead(this.#client, authorityId);
776
+ if (head === null) return null;
777
+ const generation = await readGeneration(this.#client, authorityId, head.generation);
778
+ if (generation === null || !headMatchesGeneration(head, generation)) {
779
+ if (await readPurge(this.#client, authorityId) !== null) return null;
780
+ throw new OhLibSqlSemanticError(
781
+ "integrity",
782
+ "The semantic published head does not match its immutable generation.",
783
+ );
784
+ }
785
+ if (await readPurge(this.#client, authorityId) !== null) return null;
786
+ const finalHead = await readHead(this.#client, authorityId);
787
+ if (finalHead === null) {
788
+ if (await readPurge(this.#client, authorityId) !== null) return null;
789
+ throw new OhLibSqlSemanticError(
790
+ "integrity",
791
+ "The semantic published head disappeared during its read.",
792
+ );
793
+ }
794
+ if (canonicalJson(finalHead) !== canonicalJson(head)) {
795
+ throw new OhLibSqlSemanticError(
796
+ "conflict",
797
+ "The semantic published head changed during its read.",
798
+ );
799
+ }
800
+ return Object.freeze({ ...head, v: 1 });
801
+ }
802
+
752
803
  async stage(input: Readonly<{
753
804
  authorityId: string;
754
805
  authoritySha256: Sha256Hex;
@@ -1,2 +1,3 @@
1
1
  export * from "./cloudflare-embedding";
2
2
  export * from "./libsql-semantic";
3
+ export * from "./libsql-semantic-v2";