Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 75 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ direct libSQL authority also support Node 24 serverless runtimes. Install the
current immutable release directly from GitHub:

```sh
bun add --global github:hraness/oh#v0.2.2
bun add --global github:hraness/oh#v0.2.3
oh --help
```

Expand Down Expand Up @@ -100,7 +100,7 @@ For a project dependency, pin the same immutable release in `package.json`:
```json
{
"dependencies": {
"@hraness/oh": "github:hraness/oh#v0.2.2"
"@hraness/oh": "github:hraness/oh#v0.2.3"
}
}
```
Expand Down Expand Up @@ -252,6 +252,78 @@ const result = await memory.query({
});
```

When the model must bind a small set of values and traverse a larger result,
use the additive V2 factory. The host still owns the query and rules. It names
only query-body variables as parameters and fixes every evaluation, row, page,
and page-byte limit before exposing the agent object:

```ts
import { createOhMemoryAgentV2 } from "@hraness/oh/experimental/memory";

const memoryV2 = await createOhMemoryAgentV2({
actorId: "research.memory-agent",
canonical: {
authorityId: "project-reviewed",
expectedBindingSha256: canonical.store.binding.bindingSha256,
expectedHead: await canonical.store.head(),
store: canonical.store,
},
extractors: [valueChunkExtractor],
programs: [{
evaluation: {
maximumDerivedTuples: 8_192,
maximumProofDepth: 32,
maximumProofNodes: 1_024,
maximumResultBytes: 8 * 1024 * 1024,
maximumRounds: 64,
maximumTotalProofNodes: 16_384,
maximumWorkUnits: 1_000_000,
},
maximumPageBytes: 1024 * 1024,
maximumRows: 4_096,
pageSize: 128,
parameters: ["key", "lane"],
programId: "memory.value-chunks",
purpose: "answer.memory",
query: valueChunkQuery,
rulePack: valueChunkRules,
v: 2,
}],
working: {
authorityId: "thread-working",
codecs,
expectedBindingSha256: working.store.binding.bindingSha256,
store: working.store,
},
});

let continuation: string | null = null;
do {
const page = await memoryV2.query({
bindings: { key: "entity:research", lane: "working" },
continuation,
programId: "memory.value-chunks",
v: 2,
});
continuation = page.continuation;
} while (continuation !== null);
```

V2 evaluates one complete bounded result before paging it. Any projection row
or byte truncation returns no page. A continuation is an authenticated bearer
cursor that binds its offset to the exact physical heads, program, bindings,
projection result, page size, and row count, so a working-head change fails
instead of mixing snapshots. Pass it back only to the same exact named query;
do not synthesize, edit, or log it.

The factory generates a random continuation key by default, which makes a
cursor valid only for that agent instance. If the host reconstructs agents or
routes queries across replicas, pass the same host-owned 32 through 64 byte
`Uint8Array` as `continuationKey`. The factory clones it; key rotation
invalidates outstanding cursors. The result publishes a deterministic
`continuationSha256` separately, and `resultSha256` commits that digest rather
than the opaque key-dependent token.

The returned object has only `remember`, `query`, `explain`, and `nominate`.
The host fixes the working actor, each program purpose, and every nomination
destination before exposing those methods. `remember` accepts an idempotency
Expand Down Expand Up @@ -466,7 +538,7 @@ keep remote sync explicit.
You can also give an agent this prompt:

```text
Install hraness/oh and its Oh Agent Skill from the immutable v0.2.2 tag at
Install hraness/oh and its Oh Agent Skill from the immutable v0.2.3 tag at
https://github.com/hraness/oh. Verify the CLI with `oh --help` and `oh version`.
Do not create or modify an Oh database until I name its path and ask you to.
```
Expand Down
2 changes: 1 addition & 1 deletion dist/cli.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bun
export declare const OH_PACKAGE_VERSION: "0.2.2";
export declare const OH_PACKAGE_VERSION: "0.2.3";
export declare function runOhCli(arguments_: readonly string[]): Promise<number>;
//# sourceMappingURL=cli.d.ts.map
2 changes: 1 addition & 1 deletion dist/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3072,7 +3072,7 @@ class Oh {

// src/cli.ts
import { readFile } from "fs/promises";
var OH_PACKAGE_VERSION = "0.2.2";
var OH_PACKAGE_VERSION = "0.2.3";
var KNOWN_OPTIONS = new Set([
"actor",
"after",
Expand Down
135 changes: 135 additions & 0 deletions dist/memory.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,145 @@ export interface OhMemoryAgentV1 {
query(value: unknown): Promise<OhMemoryQueryResultV1>;
remember(value: unknown): Promise<OhMemoryRememberReceiptV1>;
}
/** Additive experimental query/pagination limits; V1 contracts are unchanged. */
export declare const OH_MEMORY_QUERY_LIMITS_V2: Readonly<{
bindingBytes: number;
bindings: 32;
continuationBytes: number;
continuationKeyMaximumBytes: 64;
continuationKeyMinimumBytes: 32;
maximumPageBytes: number;
maximumPageRows: 256;
maximumProgramRows: 65536;
minimumPageBytes: number;
requestBytes: number;
}>;
export type OhMemoryEvaluationLimitsV2 = Readonly<{
maximumDerivedTuples: number;
maximumProofDepth: number;
maximumProofNodes: number;
maximumResultBytes: number;
maximumRounds: number;
maximumTotalProofNodes: number;
maximumWorkUnits: number;
}>;
/**
* A host-owned parameterized program. Parameter names refer only to variables
* in the query body, never to rule variables or projected output variables.
*/
export type OhMemoryNamedProgramV2 = Readonly<{
evaluation: OhMemoryEvaluationLimitsV2;
maximumPageBytes: number;
maximumRows: number;
pageSize: number;
parameters: readonly string[];
programId: string;
purpose: string;
query: OhProjectionQueryV1;
rulePack: OhProjectionRulePackV1;
v: 2;
}>;
export type OhMemoryFacadeOptionsV2 = Readonly<Omit<OhMemoryFacadeOptionsV1, "programs"> & Readonly<{
/** Raw HMAC key for continuations that must survive agent reconstruction. */
continuationKey?: Uint8Array;
programs: readonly OhMemoryNamedProgramV2[];
}>>;
export type OhMemoryIdentityV2 = Readonly<{
bindings: Readonly<Record<string, JsonPrimitive>>;
bindingsSha256: Sha256Hex;
boundQuerySha256: Sha256Hex;
canonical: OhMemoryLaneIdentityV1;
compositeDatasetSha256: Sha256Hex;
conflictPolicy: typeof OH_MEMORY_CONFLICT_POLICY_V1;
evaluationSha256: Sha256Hex;
memorySha256: Sha256Hex;
programId: string;
programSha256: Sha256Hex;
projectionSha256: Sha256Hex;
purpose: string;
rulePackSha256: Sha256Hex;
templateQuerySha256: Sha256Hex;
v: 2;
working: OhMemoryLaneIdentityV1;
}>;
export type OhMemoryResultRowV2 = Readonly<{
premiseAuthority: "canonical" | "unknown" | "working";
premiseLanes: readonly OhMemoryLaneV1[];
proofsTruncated: boolean;
resultRowSha256: Sha256Hex;
supportCount: number;
v: 2;
values: readonly OhProjectionAtomV1[];
}>;
export type OhMemoryPageV2 = Readonly<{
completeness: "complete" | "partial";
endExclusive: number;
hasMore: boolean;
maximumPageBytes: number;
pageSize: number;
returnedRows: number;
start: number;
totalRows: number;
truncation: Readonly<{
reasons: readonly [];
truncated: false;
v: 2;
}>;
v: 2;
}>;
export type OhMemoryQueryResultV2 = Readonly<{
authority: "derived";
conflicts: Readonly<{
count: number;
conflictsSha256: Sha256Hex;
v: 2;
}>;
continuation: string | null;
continuationSha256: Sha256Hex | null;
explainCapability: Readonly<{
expiresAt: string;
token: string;
v: 2;
}>;
identity: OhMemoryIdentityV2;
page: OhMemoryPageV2;
projectionResultSha256: Sha256Hex;
resultSha256: Sha256Hex;
rows: readonly OhMemoryResultRowV2[];
v: 2;
}>;
export type OhMemoryExplanationV2 = Readonly<{
authority: "derived";
explanationSha256: Sha256Hex;
identity: OhMemoryIdentityV2;
page: OhMemoryPageV2;
pageRow: number;
premiseAuthority: OhMemoryResultRowV2["premiseAuthority"];
premiseLanes: readonly OhMemoryLaneV1[];
proofs: readonly OhMemoryProofV1[];
proofsTruncated: boolean;
resultRowSha256: Sha256Hex;
resultSha256: Sha256Hex;
supportCount: number;
v: 2;
values: readonly OhProjectionAtomV1[];
}>;
export interface OhMemoryAgentV2 {
explain(value: unknown): Promise<OhMemoryExplanationV2>;
nominate(value: unknown): Promise<OhMemoryNominationV1>;
query(value: unknown): Promise<OhMemoryQueryResultV2>;
remember(value: unknown): Promise<OhMemoryRememberReceiptV1>;
}
/**
* Creates a model-facing memory surface over two host-bound physical Oh
* authorities. The returned object has no store, locator, rule, sync, canonical
* write, or purge handle.
*/
export declare function createOhMemoryAgentV1(options: OhMemoryFacadeOptionsV1): Promise<OhMemoryAgentV1>;
/**
* Creates the additive V2 memory facade. V2 adds only host-declared primitive
* bindings and fail-closed stable pagination; V1 request and digest contracts
* remain untouched.
*/
export declare function createOhMemoryAgentV2(options: OhMemoryFacadeOptionsV2): Promise<OhMemoryAgentV2>;
//# sourceMappingURL=memory.d.ts.map
Loading