@hraness/oh 0.3.1 → 0.4.0

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 (72) hide show
  1. package/README.md +194 -127
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +811 -97
  5. package/dist/errors.d.ts +39 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/graph.d.ts.map +1 -1
  8. package/dist/index.js +676 -61
  9. package/dist/libsql-semantic-v2.d.ts +160 -0
  10. package/dist/libsql-semantic-v2.d.ts.map +1 -0
  11. package/dist/libsql.d.ts.map +1 -1
  12. package/dist/libsql.js +162 -35
  13. package/dist/memory-page.js +2 -2
  14. package/dist/memory.d.ts +87 -6
  15. package/dist/memory.d.ts.map +1 -1
  16. package/dist/memory.js +1106 -148
  17. package/dist/operation.d.ts +3 -1
  18. package/dist/operation.d.ts.map +1 -1
  19. package/dist/projection-public.js +2 -2
  20. package/dist/projection-suss.js +2 -2
  21. package/dist/sdk.js +780 -88
  22. package/dist/semantic-cloud.d.ts +1 -0
  23. package/dist/semantic-cloud.d.ts.map +1 -1
  24. package/dist/semantic-cloud.js +1545 -2
  25. package/dist/semantic.js +2 -2
  26. package/dist/sqlite/index.js +1251 -306
  27. package/dist/sqlite/port.d.ts +31 -3
  28. package/dist/sqlite/port.d.ts.map +1 -1
  29. package/dist/sqlite/store.d.ts +18 -2
  30. package/dist/sqlite/store.d.ts.map +1 -1
  31. package/dist/store.d.ts +3 -12
  32. package/dist/store.d.ts.map +1 -1
  33. package/dist/store.js +154 -32
  34. package/dist/sync.d.ts +7 -1
  35. package/dist/sync.d.ts.map +1 -1
  36. package/dist/sync.js +668 -35
  37. package/package.json +5 -1
  38. package/skills/oh/SKILL.md +42 -16
  39. package/spec/README.md +10 -3
  40. package/spec/manifest.json +5 -1
  41. package/spec/v1/libsql-semantic-cache-schema-v1.sql +114 -0
  42. package/spec/v1/libsql-semantic-digest-fixture-v1.json +13 -0
  43. package/spec/v1/memory.md +134 -16
  44. package/spec/v1/storage.md +8 -5
  45. package/spec/v1/store.md +20 -0
  46. package/spec/v1/sync.md +77 -8
  47. package/spec/v2/libsql-semantic-cache-schema-v2.sql +175 -0
  48. package/spec/v2/libsql-semantic-digest-fixture-v2.json +14 -0
  49. package/spec/v2/manifest.json +11 -0
  50. package/spec/v2/semantic-cloud.md +106 -0
  51. package/src/cli.test.ts +53 -1
  52. package/src/cli.ts +34 -8
  53. package/src/errors.test.ts +87 -0
  54. package/src/errors.ts +185 -0
  55. package/src/graph.ts +2 -2
  56. package/src/libsql-semantic-v2.test.ts +1114 -0
  57. package/src/libsql-semantic-v2.ts +1891 -0
  58. package/src/libsql-semantic.test.ts +50 -1
  59. package/src/libsql.test.ts +36 -0
  60. package/src/libsql.ts +26 -5
  61. package/src/memory.test.ts +1488 -18
  62. package/src/memory.ts +1199 -122
  63. package/src/operation.ts +13 -3
  64. package/src/semantic-cloud.ts +1 -0
  65. package/src/sqlite/port.test.ts +209 -0
  66. package/src/sqlite/port.ts +118 -4
  67. package/src/sqlite/store.test.ts +106 -1
  68. package/src/sqlite/store.ts +168 -30
  69. package/src/store.test.ts +12 -0
  70. package/src/store.ts +30 -20
  71. package/src/sync.test.ts +570 -2
  72. package/src/sync.ts +586 -36
package/README.md CHANGED
@@ -2,15 +2,49 @@
2
2
 
3
3
  [![skills.sh](https://skills.sh/b/hraness/oh)](https://skills.sh/hraness/oh)
4
4
 
5
- Oh is a local-first ontology kernel, SQLite store, CLI, TypeScript SDK, and
6
- Agent Skill for building durable, inspectable research graphs. It stores
7
- content-addressed records and an append-only operation log, checks every
8
- mutation against an explicit versioned contract, and keeps keyword and semantic
9
- indexes derived and replaceable.
5
+ Turn a research question into an artifact whose sources, claims, citations,
6
+ dependencies, and change history remain inspectable in one local SQLite file.
7
+ Oh is the ontology kernel, store, CLI, TypeScript SDK, and Agent Skill behind
8
+ that path. It stores content-addressed records and an append-only operation log,
9
+ checks every mutation against an explicit versioned contract, and keeps keyword
10
+ and semantic indexes derived and replaceable.
10
11
 
11
12
  [Website](https://oh.computer) · [Versioned specification](spec/README.md) ·
12
13
  [Agent Skill](skills/oh/SKILL.md)
13
14
 
15
+ ## From a question to an inspectable artifact
16
+
17
+ Oh supplies a versioned graph envelope and a small ontology kernel. A research
18
+ application can map familiar work onto explicit records without hiding meaning
19
+ in a database convention:
20
+
21
+ | Research object | Oh record kind | What becomes inspectable |
22
+ | --- | --- | --- |
23
+ | Question | `inquiry` | The question and its durable investigation trail. |
24
+ | Source | `entity` | A stable identity for a paper, dataset, person, or system. |
25
+ | Capture | `edition` | A bounded source edition or extract under an application profile. |
26
+ | Claim | `statement` | The proposition, separate from who accepts it. |
27
+ | Citation | `evidence` | How a passage, table, or observation bears on an assertion. |
28
+ | Artifact | `view` | A derived brief or answer with addressable inputs. |
29
+
30
+ An attributable `assertion` sits between a claim and the evidence that bears on
31
+ it. A small review can therefore leave an inspectable path instead of one
32
+ opaque answer:
33
+
34
+ ```text
35
+ inquiry:primary-endpoint
36
+ → entity:trial-report
37
+ → edition:trial-report-v1
38
+ → statement:endpoint-12-weeks
39
+ → assertion:endpoint-12-weeks
40
+ → evidence:table-2
41
+ → view:review-brief
42
+ ```
43
+
44
+ The [homepage trace](https://oh.computer/#trace) shows the exact CLI read and a
45
+ schema-checked illustrative evidence record. It is a model of record custody,
46
+ not a claim about a real study.
47
+
14
48
  ## Why Oh
15
49
 
16
50
  - **Make meaning explicit.** Every record declares a kind, stable logical key,
@@ -28,7 +62,7 @@ indexes derived and replaceable.
28
62
  - **Derive without silently asserting.** Positive recursive rules run against
29
63
  one exact graph head and fact-pack digest. Their tuples and bounded proofs
30
64
  are deterministic, disposable output rather than accepted graph records.
31
- - **Remember without conflating authority.** An experimental facade composes a
65
+ - **Remember without conflating authority.** A stable host-bound facade composes a
32
66
  purgeable working authority with one pinned canonical head while preserving
33
67
  lane, conflict, record, and proof provenance.
34
68
 
@@ -40,16 +74,16 @@ direct libSQL authority also support Node 24 serverless runtimes. Install the
40
74
  exact current release from npm:
41
75
 
42
76
  ```sh
43
- bun add --global @hraness/oh@0.3.1
77
+ bun add --global @hraness/oh@0.4.0
44
78
  oh --help
45
79
  ```
46
80
 
47
81
  The identical package bytes and their checksum are available from the
48
- [immutable GitHub Release](https://github.com/hraness/oh/releases/tag/v0.3.1),
82
+ [immutable GitHub Release](https://github.com/hraness/oh/releases/tag/v0.4.0),
49
83
  including the mirrored
50
- [`hraness-oh-0.3.1.tgz`](https://github.com/hraness/oh/releases/download/v0.3.1/hraness-oh-0.3.1.tgz)
84
+ [`hraness-oh-0.4.0.tgz`](https://github.com/hraness/oh/releases/download/v0.4.0/hraness-oh-0.4.0.tgz)
51
85
  and
52
- [`SHA256SUMS`](https://github.com/hraness/oh/releases/download/v0.3.1/SHA256SUMS).
86
+ [`SHA256SUMS`](https://github.com/hraness/oh/releases/download/v0.4.0/SHA256SUMS).
53
87
 
54
88
  Oh writes to `.oh/oh.sqlite` and the `default` space unless you select another
55
89
  path or space. Keep `.oh/` out of source control.
@@ -107,7 +141,7 @@ For a project dependency, pin the same immutable release in `package.json`:
107
141
  ```json
108
142
  {
109
143
  "dependencies": {
110
- "@hraness/oh": "0.3.1"
144
+ "@hraness/oh": "0.4.0"
111
145
  }
112
146
  }
113
147
  ```
@@ -145,6 +179,13 @@ Pass the head you actually reviewed when concurrent writers matter. Do not
145
179
  retry `OhConflictError` blindly. Read the new head and records, reconcile the
146
180
  intended change, then submit a new operation.
147
181
 
182
+ The core `OhConflictError`, `OhIntegrityError`, `OhDependencyError`, and
183
+ `OhProfileError` classes keep their `instanceof` identity across separately
184
+ bundled Oh entrypoints. Their matching `isOhConflictError`,
185
+ `isOhIntegrityError`, `isOhDependencyError`, and `isOhProfileError` guards
186
+ accept unknown caught values only when they are native, immutably branded Oh
187
+ errors; copying a name or prototype onto a plain object is not enough.
188
+
148
189
  The root entrypoint exports canonical JSON, ontology, schema, graph, operation,
149
190
  store, and sync contracts. Use `@hraness/oh/store` for the runtime-neutral
150
191
  promise interface, `@hraness/oh/libsql` for a direct Node 24 or serverless
@@ -155,8 +196,9 @@ the local `Oh` facade, `@hraness/oh/sync` for transport seams,
155
196
  `@hraness/oh/semantic-cloud` for the Cloudflare EmbeddingGemma plus direct
156
197
  libSQL derived-cache adapter. Use the narrow stable `@hraness/oh/memory-page`
157
198
  entrypoint for model-neutral page records and `.oh.md` interchange. The
158
- `@hraness/oh/experimental/memory` subpath composes host-bound working and
159
- canonical stores behind a smaller agent-facing surface.
199
+ stable `@hraness/oh/memory` subpath composes host-bound working and canonical
200
+ stores behind separate agent and host-control surfaces. The former
201
+ `@hraness/oh/experimental/memory` path remains as a compatibility alias.
160
202
 
161
203
  ## Open a scoped working store
162
204
 
@@ -219,19 +261,42 @@ only on `authority.host`; do not expose that object or raw database credentials
219
261
  through a model tool. Read the [store-port specification](spec/v1/store.md) for
220
262
  exact snapshot, change-feed, codec ingress, closure, and purge behavior.
221
263
 
264
+ A Bun SQLite authority opened with the canonical profile instead exposes
265
+ `authority.host.replication`. Its `exportBundle` result preserves the pinned
266
+ change-feed `from`, `to`, `through`, and `hasMore` evidence; `importBundle`
267
+ strictly parses and applies the complete bundle atomically. The capability is
268
+ absent from `authority.store` and is `null` for working profiles. Long-running
269
+ hosts can capture a bounded bundle, close local custody for network I/O, then
270
+ reopen and revalidate before one atomic import.
271
+
222
272
  ## Compose working and canonical memory
223
273
 
224
- The experimental memory facade uses the same Oh kernel twice, not a separate
225
- memory database model. Trusted host code supplies two distinct physical store
226
- handles, their expected binding digests, one exact canonical head, sealed
227
- working codecs, digest-identified fact extractors, and a closed registry of
228
- named projection programs:
274
+ The stable `@hraness/oh/memory` entrypoint uses the same Oh kernel twice, not a
275
+ separate memory database model. Trusted host code supplies two distinct
276
+ physical store handles, their expected binding digests, one exact canonical
277
+ head, sealed working codecs, digest-identified fact extractors, and closed
278
+ registries of named projection programs and nomination routes.
279
+
280
+ Hosts that must fit canonical history into a narrower encrypted transport can
281
+ set `maximumCanonicalOperationBytes`. The exact canonical operation is checked
282
+ before its compare-and-swap persists; working-memory capacity is unaffected.
283
+ This pre-effect refusal is an `OhOperationSizeError`, a `RangeError` subtype
284
+ with the actual and configured canonical byte counts and the stable code
285
+ `oh.operation-size.v1`. The class remains recognizable across Oh package
286
+ entrypoints; `isOhOperationSizeError` is also exported for validating unknown
287
+ caught values without accepting a plain object that merely copied those public
288
+ fields.
289
+
290
+ Use `createOhMemoryAuthorityV1` for an application integration. It returns an
291
+ `agent` object with only `remember`, `query`, `explain`, and `nominate`, plus a
292
+ separate `host` object for canonical-head rollover and reviewed adoption:
229
293
 
230
294
  ```ts
231
- import { createOhMemoryAgentV1 } from "@hraness/oh/experimental/memory";
295
+ import { createOhMemoryAuthorityV1 } from "@hraness/oh/memory";
232
296
 
233
- const memory = await createOhMemoryAgentV1({
297
+ const memory = await createOhMemoryAuthorityV1({
234
298
  actorId: "research.memory-agent",
299
+ adoptionActorId: "research.memory-reviewer",
235
300
  canonical: {
236
301
  authorityId: "project-reviewed",
237
302
  expectedBindingSha256: canonical.store.binding.bindingSha256,
@@ -242,12 +307,7 @@ const memory = await createOhMemoryAgentV1({
242
307
  destinationPurpose: "kb.review",
243
308
  nominationId: "knowledge-review",
244
309
  }],
245
- programs: [{
246
- programId: "project.dependencies",
247
- purpose: "answer.research",
248
- query,
249
- rulePack,
250
- }],
310
+ programs: [projectDependenciesProgramV2],
251
311
  working: {
252
312
  authorityId: "thread-working",
253
313
  codecs,
@@ -256,97 +316,79 @@ const memory = await createOhMemoryAgentV1({
256
316
  },
257
317
  });
258
318
 
259
- const result = await memory.query({
319
+ const result = await memory.agent.query({
320
+ bindings: {},
321
+ continuation: null,
260
322
  programId: "project.dependencies",
261
- v: 1,
323
+ v: 2,
262
324
  });
263
- ```
264
325
 
265
- When the model must bind a small set of values and traverse a larger result,
266
- use the additive V2 factory. The host still owns the query and rules. It names
267
- only query-body variables as parameters and fixes every evaluation, row, page,
268
- and page-byte limit before exposing the agent object:
269
-
270
- ```ts
271
- import { createOhMemoryAgentV2 } from "@hraness/oh/experimental/memory";
326
+ const nomination = await memory.agent.nominate({
327
+ nominationId: "knowledge-review",
328
+ roots: ["edition:reviewed-summary"],
329
+ v: 1,
330
+ });
331
+ await memory.host.adoptNomination({
332
+ expectedCanonicalHead: result.identity.canonical.head,
333
+ nomination,
334
+ v: 1,
335
+ });
272
336
 
273
- const memoryV2 = await createOhMemoryAgentV2({
274
- actorId: "research.memory-agent",
275
- canonical: {
276
- authorityId: "project-reviewed",
277
- expectedBindingSha256: canonical.store.binding.bindingSha256,
278
- expectedHead: await canonical.store.head(),
279
- store: canonical.store,
280
- },
281
- extractors: [valueChunkExtractor],
282
- programs: [{
283
- evaluation: {
284
- maximumDerivedTuples: 8_192,
285
- maximumProofDepth: 32,
286
- maximumProofNodes: 1_024,
287
- maximumResultBytes: 8 * 1024 * 1024,
288
- maximumRounds: 64,
289
- maximumTotalProofNodes: 16_384,
290
- maximumWorkUnits: 1_000_000,
291
- },
292
- maximumPageBytes: 1024 * 1024,
293
- maximumRows: 4_096,
294
- pageSize: 128,
295
- parameters: ["key", "lane"],
296
- programId: "memory.value-chunks",
297
- purpose: "answer.memory",
298
- query: valueChunkQuery,
299
- rulePack: valueChunkRules,
300
- v: 2,
337
+ // To replace an existing key, trusted host code must prove the reviewed
338
+ // canonical digest. Omit replacements to retain strict insert-only adoption.
339
+ await memory.host.adoptNomination({
340
+ expectedCanonicalHead: reviewedCanonicalHead,
341
+ nomination: revisedNomination,
342
+ replacements: [{
343
+ expectedPriorRecordSha256: reviewedRecord.recordSha256,
344
+ key: reviewedRecord.key,
345
+ v: 1,
301
346
  }],
302
- working: {
303
- authorityId: "thread-working",
304
- codecs,
305
- expectedBindingSha256: working.store.binding.bindingSha256,
306
- store: working.store,
307
- },
347
+ v: 1,
308
348
  });
309
-
310
- let continuation: string | null = null;
311
- do {
312
- const page = await memoryV2.query({
313
- bindings: { key: "entity:research", lane: "working" },
314
- continuation,
315
- programId: "memory.value-chunks",
316
- v: 2,
317
- });
318
- continuation = page.continuation;
319
- } while (continuation !== null);
320
349
  ```
321
350
 
322
- V2 evaluates one complete bounded result before paging it. Any projection row
323
- or byte truncation returns no page. A continuation is an authenticated bearer
324
- cursor that binds its offset to the exact physical heads, program, bindings,
325
- projection result, page size, and row count, so a working-head change fails
326
- instead of mixing snapshots. Pass it back only to the same exact named query;
327
- do not synthesize, edit, or log it.
328
-
329
- The factory generates a random continuation key by default, which makes a
330
- cursor valid only for that agent instance. If the host reconstructs agents or
331
- routes queries across replicas, pass the same host-owned 32 through 64 byte
332
- `Uint8Array` as `continuationKey`. The factory clones it; key rotation
333
- invalidates outstanding cursors. The result publishes a deterministic
334
- `continuationSha256` separately, and `resultSha256` commits that digest rather
335
- than the opaque key-dependent token.
336
-
337
- The returned object has only `remember`, `query`, `explain`, and `nominate`.
338
- The host fixes the working actor, each program purpose, and every nomination
339
- destination before exposing those methods. `remember` accepts an idempotency
340
- request plus semantic changes and returns a locator-free working-lane receipt;
341
- it does not accept caller-supplied actor or time claims. The object cannot
342
- select a store, install a rule, write canonical knowledge, sync, or purge.
343
- Query identity binds both exact physical lanes and all projection policy;
344
- conflicting same-key records remain visible. Explanation requires a bounded,
345
- short-lived opaque capability bound to the exact result. Nomination chooses
346
- only a host-registered route and creates a verified working dependency-closure
347
- proposal; it never promotes it. Read the
348
- [experimental memory specification](spec/v1/memory.md) for the complete
349
- authority and lifecycle boundary.
351
+ `adoptNomination` parses the complete proposal, checks its host-selected route
352
+ and working authority, and re-exports the closure from the exact nominated
353
+ working head. An absent record is inserted, an equal digest is already present,
354
+ and a different digest fails closed by default. Trusted host code can authorize
355
+ an intentional replacement only by naming the logical key and its exact
356
+ reviewed prior digest in a bounded `replacements` list. Missing, stale, wrong,
357
+ duplicate, or absent-key claims fail without a partial write. Every supplied
358
+ claim is checked, including claims for keys already at their nominated digest;
359
+ an exact replay validates those claims against its exact reviewed head. The
360
+ inserts and authorized replacements share one compare-and-swap operation; the existing
361
+ parent-head and record-change bytes carry the transition without a new persisted
362
+ format. Before that commit, the authority proves that the prospective canonical
363
+ snapshot stays within 8,192 records and 32 MiB. After it, the authority re-reads
364
+ the physical head, so a reachable duplicate operation or a later writer cannot
365
+ make it install an obsolete intermediate head. A stale expected head succeeds
366
+ only when the current physical snapshot contains every nominated digest exactly.
367
+
368
+ `advanceCanonical` moves the facade's pin only from its current exact head to
369
+ the same head or a proven descendant in the bound canonical operation chain.
370
+ One call proves at most 16,384 operations over at most 64 bounded pages; callers
371
+ must advance longer histories in reviewed chunks. Host mutations are
372
+ serialized. A query already in flight keeps the immutable pin it captured,
373
+ existing explanation capabilities survive rollover in one shared 64 MiB cache,
374
+ and an old continuation fails when the memory identity changes.
375
+
376
+ Every stable method snapshots unknown JSON input through data-property
377
+ descriptors before validation. Accessors, symbols, proxies, sparse arrays, and
378
+ non-JSON values fail closed, and execution uses only the detached bytes. The
379
+ snapshot walk is capped at 128 levels, 65,536 entries per container, and
380
+ 1,048,576 total nodes, and counts canonical bytes before it clones each child.
381
+
382
+ V2 evaluates one complete bounded result before paging it. Agent input may bind
383
+ only host-declared primitive query-body parameters; it cannot choose sources,
384
+ rules, purpose, page size, or evaluation limits. Pass an issued continuation
385
+ back unchanged only to the same named query and do not log it. Catch
386
+ `OhMemoryContinuationError` when a supplied cursor needs to be restarted; its
387
+ `reason` distinguishes encoding, authentication, and exact-identity failures.
388
+ Other store and projection failures keep their original types. The compatibility
389
+ alias `@hraness/oh/experimental/memory` remains available, but new integrations
390
+ should use `@hraness/oh/memory`. Read the [memory specification](spec/v1/memory.md)
391
+ for the complete authority, conflict, pagination, and lifecycle boundary.
350
392
 
351
393
  ## Use a memory page or `.oh.md` file
352
394
 
@@ -528,18 +570,24 @@ is absent.
528
570
 
529
571
  ## Add a hosted semantic cache
530
572
 
531
- The hosted adapter uses the same source-record principle with a distinct,
573
+ The hosted V2 adapter uses the same source-record principle with a distinct,
532
574
  profile-bound cache. It sends bounded inputs to Cloudflare Workers AI's
533
575
  EmbeddingGemma model and stores only float32 vectors, input digests, record
534
576
  digests, immutable generation membership, and a published pointer in direct
535
577
  libSQL. It stores no title, body, record JSON, or query text.
536
578
 
579
+ Every cache call also binds an isolation SHA-256. The helper below derives a
580
+ safe authority-specific default. A private multi-tenant host should instead
581
+ derive an opaque digest from its private authority handle, cache epoch, and
582
+ profile identity, then pass that same digest to every cache operation.
583
+
537
584
  ```ts
538
585
  import { createClient } from "@libsql/client";
539
586
  import {
540
587
  OhCloudflareEmbeddingClientV1,
541
- bootstrapOhLibSqlSemanticCacheV1,
542
- openOhLibSqlSemanticCacheV1,
588
+ bootstrapOhLibSqlSemanticCacheV2,
589
+ deriveOhSemanticIsolationSha256V2,
590
+ openOhLibSqlSemanticCacheV2,
543
591
  } from "@hraness/oh/semantic-cloud";
544
592
 
545
593
  // Deploy once with a short-lived schema credential.
@@ -547,43 +595,54 @@ const schemaClient = createClient({
547
595
  authToken: process.env.OH_SEMANTIC_SCHEMA_TOKEN!,
548
596
  url: process.env.OH_SEMANTIC_DATABASE_URL!,
549
597
  });
550
- await bootstrapOhLibSqlSemanticCacheV1(schemaClient);
598
+ await bootstrapOhLibSqlSemanticCacheV2(schemaClient);
551
599
  schemaClient.close();
552
600
 
553
601
  const client = createClient({
554
602
  authToken: process.env.OH_SEMANTIC_RUNTIME_TOKEN!,
555
603
  url: process.env.OH_SEMANTIC_DATABASE_URL!,
556
604
  });
557
- const cache = await openOhLibSqlSemanticCacheV1(client, { closeClient: true });
605
+ const cache = await openOhLibSqlSemanticCacheV2(client, { closeClient: true });
558
606
  const embedder = new OhCloudflareEmbeddingClientV1({
559
607
  accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
560
608
  apiToken: process.env.CLOUDFLARE_WORKERS_AI_TOKEN!,
561
609
  });
610
+ const authorityId = "thread:research/epoch:1";
611
+ const isolationSha256 = deriveOhSemanticIsolationSha256V2(authorityId);
562
612
 
563
613
  const staged = await cache.stage({
564
- authorityId: "thread:research/epoch:1",
614
+ authorityId,
565
615
  authoritySha256: snapshot.head.recordsSha256,
566
616
  documents,
567
617
  embeddingClient: embedder,
568
618
  generation: snapshot.head.generation,
619
+ isolationSha256,
569
620
  });
570
621
  const published = await cache.publishedHead({
571
- authorityId: "thread:research/epoch:1",
622
+ authorityId,
623
+ isolationSha256,
572
624
  });
573
625
  await cache.publish({
574
- authorityId: "thread:research/epoch:1",
626
+ authorityId,
575
627
  expectedPublishedGeneration: published?.generation ?? null,
576
628
  generation: staged.generation,
629
+ isolationSha256,
577
630
  });
578
631
  ```
579
632
 
580
633
  Search requires the exact current authority generation and record digests, and
581
634
  returns nothing from a stale or concurrently replaced head. Purge writes a
582
- permanent authority tombstone before deleting memberships and unused vectors.
583
- The same authority ID cannot be resurrected; allocate a new epoch for a new
584
- lifetime. Hosted failure is a missing convenience lane, never permission to
585
- weaken exact graph or Datalog operations. Read the
586
- [hosted semantic-cache specification](spec/v1/semantic-cloud.md).
635
+ permanent authority tombstone before deleting memberships and every vector in
636
+ that authority's reserved isolation scopes. Its immutable marker and receipt
637
+ make retries return the same first-run counts while proving zero residual cache
638
+ rows. Distinct authority or cache-epoch isolation digests never reuse a vector,
639
+ and the digest never enters provider text. The same authority ID cannot be
640
+ resurrected; allocate a new epoch for a new lifetime. Hosted failure is a
641
+ missing convenience lane, never permission to weaken exact graph or Datalog
642
+ operations. Read the
643
+ [isolated hosted semantic-cache V2 specification](spec/v2/semantic-cloud.md).
644
+ The released V1 API and digests remain available unchanged for compatibility;
645
+ V1 and V2 cannot open the same semantic database simultaneously.
587
646
 
588
647
  ## Sync through libSQL or Turso
589
648
 
@@ -615,10 +674,18 @@ try {
615
674
 
616
675
  The consumer owns credentials, client construction, retry policy, and remote
617
676
  availability. The transport handshakes before exchanging data and refuses a
618
- different contract or a non-fast-forward history.
677
+ different contract or a non-fast-forward history. Canonical bundles share one
678
+ 64 MiB plus 4 KiB byte/node budget across at most 1,000 operations. If a retry
679
+ finds that another writer has advanced beyond its submitted tail, the libSQL
680
+ adapter acknowledges that tail only after every submitted sequence, digest,
681
+ and canonical operation still matches the bounded remote row range. Histories
682
+ larger than one bundle advance through the largest fitting operation prefix in
683
+ each direction.
619
684
 
620
685
  For offline transfer, `oh sync export` writes a bounded bundle to stdout and
621
- `oh sync import --file <path>` verifies and imports it idempotently.
686
+ `oh sync import --file <path>` verifies and imports it idempotently in one
687
+ atomic transaction. Import refuses non-regular or oversized bundle files before
688
+ opening the local authority.
622
689
 
623
690
  ## Boundaries and limitations
624
691
 
@@ -652,9 +719,9 @@ keep remote sync explicit.
652
719
  You can also give an agent this prompt:
653
720
 
654
721
  ```text
655
- Install @hraness/oh@0.3.1 from npm and use its packaged Oh Agent Skill. The
656
- exact npm tarball and SHA256SUMS are mirrored by the immutable v0.3.1 Release at
657
- https://github.com/hraness/oh/releases/tag/v0.3.1. Verify the CLI with
722
+ Install @hraness/oh@0.4.0 from npm and use its packaged Oh Agent Skill. The
723
+ exact npm tarball and SHA256SUMS are mirrored by the immutable v0.4.0 Release at
724
+ https://github.com/hraness/oh/releases/tag/v0.4.0. Verify the CLI with
658
725
  `oh --help` and `oh version`.
659
726
  Do not create or modify an Oh database until I name its path and ask you to.
660
727
  ```
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env bun
2
- export declare const OH_PACKAGE_VERSION: "0.3.1";
2
+ export declare const OH_PACKAGE_VERSION: "0.4.0";
3
3
  export declare function runOhCli(arguments_: readonly string[]): Promise<number>;
4
4
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAWA,eAAO,MAAM,kBAAkB,EAAG,OAAgB,CAAC;AA0MnD,wBAAsB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuF7E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAWA,eAAO,MAAM,kBAAkB,EAAG,OAAgB,CAAC;AAyNnD,wBAAsB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkG7E"}