@oxdeai/sdk 1.0.1

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.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @oxdeai/sdk
2
+
3
+ Developer-facing integration layer on top of `@oxdeai/core`.
4
+
5
+ ## What It Adds
6
+
7
+ - Intent/state builder helpers
8
+ - Typed client wrapper for common flow: evaluate + persist + verify
9
+ - Runtime adapters (in-memory and file-based)
10
+
11
+ ## Quick Example
12
+
13
+ ```ts
14
+ import { PolicyEngine } from "@oxdeai/core";
15
+ import {
16
+ OxDeAIClient,
17
+ buildState,
18
+ buildIntent,
19
+ InMemoryStateAdapter,
20
+ InMemoryAuditAdapter
21
+ } from "@oxdeai/sdk";
22
+
23
+ const engine = new PolicyEngine({
24
+ policy_version: "v1",
25
+ engine_secret: "dev-secret",
26
+ authorization_ttl_seconds: 120
27
+ });
28
+
29
+ const stateAdapter = new InMemoryStateAdapter(
30
+ buildState({
31
+ policy_version: "v1",
32
+ agent_id: "agent-1",
33
+ allow_action_types: ["PROVISION"],
34
+ allow_targets: ["us-east-1"]
35
+ })
36
+ );
37
+
38
+ const auditAdapter = new InMemoryAuditAdapter();
39
+
40
+ const client = new OxDeAIClient({
41
+ engine,
42
+ stateAdapter,
43
+ auditAdapter,
44
+ clock: { now: () => 1770000000 }
45
+ });
46
+
47
+ const intent = buildIntent({
48
+ intent_id: "intent-1",
49
+ agent_id: "agent-1",
50
+ action_type: "PROVISION",
51
+ amount: 320n,
52
+ target: "us-east-1",
53
+ nonce: 1n
54
+ });
55
+
56
+ const result = await client.evaluateAndCommit(intent);
57
+ ```
58
+
59
+ ## Main Exports
60
+
61
+ - `buildIntent`, `buildState`
62
+ - `OxDeAIClient`
63
+ - `InMemoryStateAdapter`, `InMemoryAuditAdapter`
64
+ - `JsonFileStateAdapter`, `NdjsonFileAuditAdapter`
65
+
66
+ ## Scripts
67
+
68
+ ```bash
69
+ pnpm -C packages/sdk build
70
+ pnpm -C packages/sdk test
71
+ ```
@@ -0,0 +1,24 @@
1
+ import type { State } from "@oxdeai/core";
2
+ import type { AuditAdapter, StateAdapter } from "./types.js";
3
+ export declare class InMemoryStateAdapter implements StateAdapter {
4
+ private state;
5
+ constructor(initial: State);
6
+ load(): State;
7
+ save(state: State): void;
8
+ }
9
+ export declare class InMemoryAuditAdapter implements AuditAdapter {
10
+ private readonly events;
11
+ append(events: readonly unknown[]): void;
12
+ snapshot(): unknown[];
13
+ }
14
+ export declare class JsonFileStateAdapter implements StateAdapter {
15
+ private readonly path;
16
+ constructor(path: string);
17
+ load(): Promise<State>;
18
+ save(state: State): Promise<void>;
19
+ }
20
+ export declare class NdjsonFileAuditAdapter implements AuditAdapter {
21
+ private readonly path;
22
+ constructor(path: string);
23
+ append(events: readonly unknown[]): Promise<void>;
24
+ }
@@ -0,0 +1,104 @@
1
+ import { appendFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { randomUUID } from "node:crypto";
4
+ function canonicalize(value) {
5
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string")
6
+ return value;
7
+ if (typeof value === "bigint")
8
+ return `${value.toString()}n`;
9
+ if (Array.isArray(value))
10
+ return value.map((v) => canonicalize(v));
11
+ if (typeof value === "object") {
12
+ const obj = value;
13
+ const out = {};
14
+ for (const key of Object.keys(obj).sort())
15
+ out[key] = canonicalize(obj[key]);
16
+ return out;
17
+ }
18
+ return String(value);
19
+ }
20
+ function stringifyCanonical(value) {
21
+ return JSON.stringify(canonicalize(value));
22
+ }
23
+ function parseBigIntLike(value) {
24
+ if (typeof value === "bigint")
25
+ return value;
26
+ if (typeof value === "number")
27
+ return BigInt(value);
28
+ if (typeof value === "string") {
29
+ const s = value.endsWith("n") ? value.slice(0, -1) : value;
30
+ return BigInt(s);
31
+ }
32
+ throw new Error("invalid bigint-like value");
33
+ }
34
+ function mapRecordBigInt(input) {
35
+ const rec = (input ?? {});
36
+ const out = {};
37
+ for (const key of Object.keys(rec))
38
+ out[key] = parseBigIntLike(rec[key]);
39
+ return out;
40
+ }
41
+ function normalizeStateBigInts(raw) {
42
+ const s = raw;
43
+ return {
44
+ ...s,
45
+ budget: {
46
+ ...s.budget,
47
+ budget_limit: mapRecordBigInt(s.budget?.budget_limit),
48
+ spent_in_period: mapRecordBigInt(s.budget?.spent_in_period)
49
+ },
50
+ max_amount_per_action: mapRecordBigInt(s.max_amount_per_action)
51
+ };
52
+ }
53
+ export class InMemoryStateAdapter {
54
+ state;
55
+ constructor(initial) {
56
+ this.state = structuredClone(initial);
57
+ }
58
+ load() {
59
+ return structuredClone(this.state);
60
+ }
61
+ save(state) {
62
+ this.state = structuredClone(state);
63
+ }
64
+ }
65
+ export class InMemoryAuditAdapter {
66
+ events = [];
67
+ append(events) {
68
+ for (const event of events)
69
+ this.events.push(structuredClone(event));
70
+ }
71
+ snapshot() {
72
+ return structuredClone(this.events);
73
+ }
74
+ }
75
+ export class JsonFileStateAdapter {
76
+ path;
77
+ constructor(path) {
78
+ this.path = path;
79
+ }
80
+ async load() {
81
+ const txt = await readFile(this.path, "utf8");
82
+ return normalizeStateBigInts(JSON.parse(txt));
83
+ }
84
+ async save(state) {
85
+ await mkdir(dirname(this.path), { recursive: true });
86
+ const tmp = `${this.path}.tmp-${randomUUID()}`;
87
+ await writeFile(tmp, `${stringifyCanonical(state)}\n`, "utf8");
88
+ await rename(tmp, this.path);
89
+ }
90
+ }
91
+ export class NdjsonFileAuditAdapter {
92
+ path;
93
+ constructor(path) {
94
+ this.path = path;
95
+ }
96
+ async append(events) {
97
+ if (events.length === 0)
98
+ return;
99
+ await mkdir(dirname(this.path), { recursive: true });
100
+ const body = events.map((e) => stringifyCanonical(e)).join("\n") + "\n";
101
+ await appendFile(this.path, body, "utf8");
102
+ }
103
+ }
104
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Intent, State } from "@oxdeai/core";
2
+ import type { IntentBuilderInput, StateBuilderInput } from "./types.js";
3
+ export declare function buildIntent(input: IntentBuilderInput): Intent;
4
+ export declare function buildState(input: StateBuilderInput): State;
@@ -0,0 +1,76 @@
1
+ export function buildIntent(input) {
2
+ const base = {
3
+ intent_id: input.intent_id,
4
+ agent_id: input.agent_id,
5
+ action_type: input.action_type,
6
+ amount: input.amount,
7
+ asset: input.asset,
8
+ target: input.target,
9
+ timestamp: input.timestamp ?? 0,
10
+ metadata_hash: input.metadata_hash ?? "0x" + "0".repeat(64),
11
+ nonce: input.nonce,
12
+ signature: input.signature ?? "sdk-signature-placeholder",
13
+ depth: input.depth,
14
+ tool: input.tool,
15
+ tool_call: input.tool_call
16
+ };
17
+ if (input.type === "RELEASE") {
18
+ if (!input.authorization_id)
19
+ throw new Error("authorization_id is required for RELEASE intent");
20
+ return {
21
+ ...base,
22
+ type: "RELEASE",
23
+ authorization_id: input.authorization_id
24
+ };
25
+ }
26
+ return {
27
+ ...base,
28
+ type: "EXECUTE",
29
+ authorization_id: input.authorization_id
30
+ };
31
+ }
32
+ export function buildState(input) {
33
+ const agent = input.agent_id;
34
+ return {
35
+ policy_version: input.policy_version ?? "v1",
36
+ period_id: input.period_id ?? "default-period",
37
+ kill_switch: { global: false, agents: {} },
38
+ allowlists: {
39
+ action_types: input.allow_action_types ?? ["PROVISION"],
40
+ assets: input.allow_assets ?? [],
41
+ targets: input.allow_targets ?? []
42
+ },
43
+ budget: {
44
+ budget_limit: { [agent]: input.budget_limit ?? 1000000n },
45
+ spent_in_period: { [agent]: input.spent_in_period ?? 0n }
46
+ },
47
+ max_amount_per_action: { [agent]: input.max_amount_per_action ?? 1000000n },
48
+ velocity: {
49
+ config: {
50
+ window_seconds: input.velocity_window_seconds ?? 60,
51
+ max_actions: input.velocity_max_actions ?? 1000
52
+ },
53
+ counters: {}
54
+ },
55
+ replay: {
56
+ window_seconds: input.replay_window_seconds ?? 3600,
57
+ max_nonces_per_agent: input.replay_max_nonces_per_agent ?? 256,
58
+ nonces: {}
59
+ },
60
+ concurrency: {
61
+ max_concurrent: { [agent]: input.max_concurrent ?? 16 },
62
+ active: {},
63
+ active_auths: {}
64
+ },
65
+ recursion: {
66
+ max_depth: { [agent]: input.max_depth ?? 8 }
67
+ },
68
+ tool_limits: {
69
+ window_seconds: input.tool_window_seconds ?? 60,
70
+ max_calls: { [agent]: input.tool_max_calls ?? 1000 },
71
+ max_calls_by_tool: {},
72
+ calls: {}
73
+ }
74
+ };
75
+ }
76
+ //# sourceMappingURL=builders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builders.js","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { PolicyEngine } from "@oxdeai/core";
2
+ import type { Authorization, Intent } from "@oxdeai/core";
3
+ import type { AuditAdapter, ClockAdapter, EvaluateAndCommitResult, StateAdapter, VerifyBundleResult } from "./types.js";
4
+ type ClientOptions = {
5
+ engine: PolicyEngine;
6
+ stateAdapter: StateAdapter;
7
+ auditAdapter?: AuditAdapter;
8
+ clock?: ClockAdapter;
9
+ };
10
+ type EvaluateInput = Omit<Intent, "timestamp"> & {
11
+ timestamp?: number;
12
+ };
13
+ export declare class OxDeAIClient {
14
+ private readonly engine;
15
+ private readonly stateAdapter;
16
+ private readonly auditAdapter?;
17
+ private readonly clock;
18
+ private auditCursor;
19
+ constructor(opts: ClientOptions);
20
+ evaluateAndCommit(input: EvaluateInput): Promise<EvaluateAndCommitResult>;
21
+ verifyAuthorization(intent: Intent, authorization: Authorization): Promise<{
22
+ valid: boolean;
23
+ reason?: string;
24
+ }>;
25
+ verifyCurrentArtifacts(opts?: {
26
+ mode?: "strict" | "best-effort";
27
+ }): Promise<VerifyBundleResult>;
28
+ }
29
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,70 @@
1
+ import { encodeCanonicalState, encodeEnvelope, verifyAuditEvents, verifyEnvelope, verifySnapshot } from "@oxdeai/core";
2
+ function collectNewEvents(engine, cursor) {
3
+ const all = engine.audit.snapshot();
4
+ const events = all.slice(cursor);
5
+ return { nextCursor: all.length, events };
6
+ }
7
+ export class OxDeAIClient {
8
+ engine;
9
+ stateAdapter;
10
+ auditAdapter;
11
+ clock;
12
+ auditCursor = 0;
13
+ constructor(opts) {
14
+ this.engine = opts.engine;
15
+ this.stateAdapter = opts.stateAdapter;
16
+ this.auditAdapter = opts.auditAdapter;
17
+ this.clock = opts.clock ?? { now: () => Math.floor(Date.now() / 1000) };
18
+ }
19
+ async evaluateAndCommit(input) {
20
+ const state = await this.stateAdapter.load();
21
+ const resolvedTimestamp = input.timestamp === undefined || input.timestamp === 0
22
+ ? this.clock.now()
23
+ : input.timestamp;
24
+ const intent = {
25
+ ...input,
26
+ timestamp: resolvedTimestamp
27
+ };
28
+ const out = this.engine.evaluatePure(intent, state, { mode: "fail-fast" });
29
+ const emitted = collectNewEvents(this.engine, this.auditCursor);
30
+ this.auditCursor = emitted.nextCursor;
31
+ if (this.auditAdapter)
32
+ await this.auditAdapter.append(emitted.events);
33
+ if (out.decision === "ALLOW") {
34
+ await this.stateAdapter.save(out.nextState);
35
+ const output = {
36
+ decision: "ALLOW",
37
+ reasons: [],
38
+ authorization: out.authorization
39
+ };
40
+ return { output, state: out.nextState, auditEvents: emitted.events };
41
+ }
42
+ const output = {
43
+ decision: "DENY",
44
+ reasons: out.reasons
45
+ };
46
+ return { output, state, auditEvents: emitted.events };
47
+ }
48
+ async verifyAuthorization(intent, authorization) {
49
+ const state = await this.stateAdapter.load();
50
+ const result = this.engine.verifyAuthorization(intent, authorization, state, intent.timestamp);
51
+ return result.valid ? { valid: true } : { valid: false, reason: result.reason };
52
+ }
53
+ async verifyCurrentArtifacts(opts) {
54
+ const state = await this.stateAdapter.load();
55
+ const snapshotBytes = encodeCanonicalState(this.engine.exportState(state));
56
+ const snapshot = verifySnapshot(snapshotBytes);
57
+ const allEvents = this.engine.audit.snapshot();
58
+ const audit = verifyAuditEvents(allEvents, {
59
+ mode: opts?.mode ?? "best-effort"
60
+ });
61
+ const envelopeBytes = encodeEnvelope({
62
+ formatVersion: 1,
63
+ snapshot: snapshotBytes,
64
+ events: allEvents
65
+ });
66
+ const envelope = verifyEnvelope(envelopeBytes, { mode: opts?.mode ?? "best-effort" });
67
+ return { snapshot, audit, envelope };
68
+ }
69
+ }
70
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** OxDeAI SDK — integration layer on top of @oxdeai/core. */
2
+ export * from "@oxdeai/core";
3
+ export * from "./types.js";
4
+ export * from "./builders.js";
5
+ export * from "./adapters.js";
6
+ export * from "./client.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /** OxDeAI SDK — integration layer on top of @oxdeai/core. */
2
+ export * from "@oxdeai/core";
3
+ export * from "./types.js";
4
+ export * from "./builders.js";
5
+ export * from "./adapters.js";
6
+ export * from "./client.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { PolicyEngine } from "@oxdeai/core";
4
+ import { InMemoryAuditAdapter, InMemoryStateAdapter } from "./adapters.js";
5
+ import { buildIntent, buildState } from "./builders.js";
6
+ import { OxDeAIClient } from "./client.js";
7
+ test("builder helpers create engine-compatible intent/state", () => {
8
+ const state = buildState({
9
+ policy_version: "v1",
10
+ agent_id: "agent-1",
11
+ allow_action_types: ["PROVISION"],
12
+ allow_targets: ["us-east-1"]
13
+ });
14
+ const intent = buildIntent({
15
+ intent_id: "intent-1",
16
+ agent_id: "agent-1",
17
+ action_type: "PROVISION",
18
+ amount: 100n,
19
+ target: "us-east-1",
20
+ nonce: 1n,
21
+ timestamp: 1_770_000_000
22
+ });
23
+ const engine = new PolicyEngine({
24
+ policy_version: "v1",
25
+ engine_secret: "dev-secret",
26
+ authorization_ttl_seconds: 60
27
+ });
28
+ const out = engine.evaluatePure(intent, state, { mode: "fail-fast" });
29
+ assert.equal(out.decision, "ALLOW");
30
+ });
31
+ test("OxDeAIClient evaluate+persist+verify flow", async () => {
32
+ const engine = new PolicyEngine({
33
+ policy_version: "v1",
34
+ engine_secret: "dev-secret",
35
+ authorization_ttl_seconds: 60
36
+ });
37
+ const stateAdapter = new InMemoryStateAdapter(buildState({
38
+ policy_version: "v1",
39
+ agent_id: "agent-1",
40
+ allow_action_types: ["PROVISION"],
41
+ allow_targets: ["us-east-1"]
42
+ }));
43
+ const auditAdapter = new InMemoryAuditAdapter();
44
+ const client = new OxDeAIClient({
45
+ engine,
46
+ stateAdapter,
47
+ auditAdapter,
48
+ clock: { now: () => 1_770_000_000 }
49
+ });
50
+ const intent = buildIntent({
51
+ intent_id: "intent-2",
52
+ agent_id: "agent-1",
53
+ action_type: "PROVISION",
54
+ amount: 320n,
55
+ target: "us-east-1",
56
+ nonce: 2n
57
+ });
58
+ const res = await client.evaluateAndCommit(intent);
59
+ assert.equal(res.output.decision, "ALLOW");
60
+ if (res.output.decision !== "ALLOW")
61
+ return;
62
+ assert.ok(res.auditEvents.length >= 3);
63
+ assert.equal(auditAdapter.snapshot().length, res.auditEvents.length);
64
+ const auth = await client.verifyAuthorization({ ...intent, timestamp: 1_770_000_000 }, res.output.authorization);
65
+ assert.equal(auth.valid, true);
66
+ const verify = await client.verifyCurrentArtifacts({ mode: "best-effort" });
67
+ assert.equal(verify.snapshot.status, "ok");
68
+ assert.equal(verify.audit.status, "ok");
69
+ assert.equal(verify.envelope.status, "ok");
70
+ });
71
+ //# sourceMappingURL=sdk.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.test.js","sourceRoot":"","sources":["../src/sdk.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { Authorization, Intent, State, VerificationResult } from "@oxdeai/core";
2
+ export type MaybePromise<T> = T | Promise<T>;
3
+ export type EvaluateDecision = {
4
+ decision: "ALLOW";
5
+ reasons: [];
6
+ authorization: Authorization;
7
+ } | {
8
+ decision: "DENY";
9
+ reasons: string[];
10
+ };
11
+ export type EvaluateAndCommitResult = {
12
+ output: EvaluateDecision;
13
+ state: State;
14
+ auditEvents: unknown[];
15
+ };
16
+ export type VerifyBundleResult = {
17
+ snapshot: VerificationResult;
18
+ audit: VerificationResult;
19
+ envelope: VerificationResult;
20
+ };
21
+ export interface StateAdapter {
22
+ load(): MaybePromise<State>;
23
+ save(state: State): MaybePromise<void>;
24
+ }
25
+ export interface AuditAdapter {
26
+ append(events: readonly unknown[]): MaybePromise<void>;
27
+ }
28
+ export interface ClockAdapter {
29
+ now(): number;
30
+ }
31
+ export type IntentBuilderInput = {
32
+ intent_id: string;
33
+ agent_id: string;
34
+ action_type: Intent["action_type"];
35
+ amount: bigint;
36
+ target: string;
37
+ nonce: bigint;
38
+ timestamp?: number;
39
+ type?: "EXECUTE" | "RELEASE";
40
+ authorization_id?: string;
41
+ asset?: string;
42
+ metadata_hash?: string;
43
+ signature?: string;
44
+ depth?: number;
45
+ tool?: string;
46
+ tool_call?: boolean;
47
+ };
48
+ export type StateBuilderInput = {
49
+ policy_version?: string;
50
+ period_id?: string;
51
+ agent_id: string;
52
+ allow_action_types?: Intent["action_type"][];
53
+ allow_assets?: string[];
54
+ allow_targets?: string[];
55
+ budget_limit?: bigint;
56
+ spent_in_period?: bigint;
57
+ max_amount_per_action?: bigint;
58
+ velocity_window_seconds?: number;
59
+ velocity_max_actions?: number;
60
+ replay_window_seconds?: number;
61
+ replay_max_nonces_per_agent?: number;
62
+ max_concurrent?: number;
63
+ max_depth?: number;
64
+ tool_window_seconds?: number;
65
+ tool_max_calls?: number;
66
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@oxdeai/sdk",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "dependencies": {
8
+ "@oxdeai/core": "^1.0.1"
9
+ },
10
+ "scripts": {
11
+ "typecheck": "tsc -p tsconfig.json --noEmit",
12
+ "build": "rm -rf dist && tsc -p tsconfig.json",
13
+ "test": "pnpm build && node --test \"dist/**/*.test.js\""
14
+ }
15
+ }
@@ -0,0 +1,109 @@
1
+ import { appendFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import { randomUUID } from "node:crypto";
4
+
5
+ import type { State } from "@oxdeai/core";
6
+
7
+ import type { AuditAdapter, StateAdapter } from "./types.js";
8
+
9
+ function canonicalize(value: unknown): unknown {
10
+ if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") return value;
11
+ if (typeof value === "bigint") return `${value.toString()}n`;
12
+ if (Array.isArray(value)) return value.map((v) => canonicalize(v));
13
+ if (typeof value === "object") {
14
+ const obj = value as Record<string, unknown>;
15
+ const out: Record<string, unknown> = {};
16
+ for (const key of Object.keys(obj).sort()) out[key] = canonicalize(obj[key]);
17
+ return out;
18
+ }
19
+ return String(value);
20
+ }
21
+
22
+ function stringifyCanonical(value: unknown): string {
23
+ return JSON.stringify(canonicalize(value));
24
+ }
25
+
26
+ function parseBigIntLike(value: unknown): bigint {
27
+ if (typeof value === "bigint") return value;
28
+ if (typeof value === "number") return BigInt(value);
29
+ if (typeof value === "string") {
30
+ const s = value.endsWith("n") ? value.slice(0, -1) : value;
31
+ return BigInt(s);
32
+ }
33
+ throw new Error("invalid bigint-like value");
34
+ }
35
+
36
+ function mapRecordBigInt(input: unknown): Record<string, bigint> {
37
+ const rec = (input ?? {}) as Record<string, unknown>;
38
+ const out: Record<string, bigint> = {};
39
+ for (const key of Object.keys(rec)) out[key] = parseBigIntLike(rec[key]);
40
+ return out;
41
+ }
42
+
43
+ function normalizeStateBigInts(raw: unknown): State {
44
+ const s = raw as State;
45
+ return {
46
+ ...s,
47
+ budget: {
48
+ ...s.budget,
49
+ budget_limit: mapRecordBigInt((s as any).budget?.budget_limit),
50
+ spent_in_period: mapRecordBigInt((s as any).budget?.spent_in_period)
51
+ },
52
+ max_amount_per_action: mapRecordBigInt((s as any).max_amount_per_action)
53
+ };
54
+ }
55
+
56
+ export class InMemoryStateAdapter implements StateAdapter {
57
+ private state: State;
58
+
59
+ constructor(initial: State) {
60
+ this.state = structuredClone(initial);
61
+ }
62
+
63
+ load(): State {
64
+ return structuredClone(this.state);
65
+ }
66
+
67
+ save(state: State): void {
68
+ this.state = structuredClone(state);
69
+ }
70
+ }
71
+
72
+ export class InMemoryAuditAdapter implements AuditAdapter {
73
+ private readonly events: unknown[] = [];
74
+
75
+ append(events: readonly unknown[]): void {
76
+ for (const event of events) this.events.push(structuredClone(event));
77
+ }
78
+
79
+ snapshot(): unknown[] {
80
+ return structuredClone(this.events);
81
+ }
82
+ }
83
+
84
+ export class JsonFileStateAdapter implements StateAdapter {
85
+ constructor(private readonly path: string) {}
86
+
87
+ async load(): Promise<State> {
88
+ const txt = await readFile(this.path, "utf8");
89
+ return normalizeStateBigInts(JSON.parse(txt));
90
+ }
91
+
92
+ async save(state: State): Promise<void> {
93
+ await mkdir(dirname(this.path), { recursive: true });
94
+ const tmp = `${this.path}.tmp-${randomUUID()}`;
95
+ await writeFile(tmp, `${stringifyCanonical(state)}\n`, "utf8");
96
+ await rename(tmp, this.path);
97
+ }
98
+ }
99
+
100
+ export class NdjsonFileAuditAdapter implements AuditAdapter {
101
+ constructor(private readonly path: string) {}
102
+
103
+ async append(events: readonly unknown[]): Promise<void> {
104
+ if (events.length === 0) return;
105
+ await mkdir(dirname(this.path), { recursive: true });
106
+ const body = events.map((e) => stringifyCanonical(e)).join("\n") + "\n";
107
+ await appendFile(this.path, body, "utf8");
108
+ }
109
+ }
@@ -0,0 +1,80 @@
1
+ import type { Intent, State } from "@oxdeai/core";
2
+ import type { IntentBuilderInput, StateBuilderInput } from "./types.js";
3
+
4
+ export function buildIntent(input: IntentBuilderInput): Intent {
5
+ const base = {
6
+ intent_id: input.intent_id,
7
+ agent_id: input.agent_id,
8
+ action_type: input.action_type,
9
+ amount: input.amount,
10
+ asset: input.asset,
11
+ target: input.target,
12
+ timestamp: input.timestamp ?? 0,
13
+ metadata_hash: input.metadata_hash ?? "0x" + "0".repeat(64),
14
+ nonce: input.nonce,
15
+ signature: input.signature ?? "sdk-signature-placeholder",
16
+ depth: input.depth,
17
+ tool: input.tool,
18
+ tool_call: input.tool_call
19
+ };
20
+
21
+ if (input.type === "RELEASE") {
22
+ if (!input.authorization_id) throw new Error("authorization_id is required for RELEASE intent");
23
+ return {
24
+ ...base,
25
+ type: "RELEASE",
26
+ authorization_id: input.authorization_id
27
+ };
28
+ }
29
+
30
+ return {
31
+ ...base,
32
+ type: "EXECUTE",
33
+ authorization_id: input.authorization_id
34
+ };
35
+ }
36
+
37
+ export function buildState(input: StateBuilderInput): State {
38
+ const agent = input.agent_id;
39
+ return {
40
+ policy_version: input.policy_version ?? "v1",
41
+ period_id: input.period_id ?? "default-period",
42
+ kill_switch: { global: false, agents: {} },
43
+ allowlists: {
44
+ action_types: input.allow_action_types ?? ["PROVISION"],
45
+ assets: input.allow_assets ?? [],
46
+ targets: input.allow_targets ?? []
47
+ },
48
+ budget: {
49
+ budget_limit: { [agent]: input.budget_limit ?? 1_000_000n },
50
+ spent_in_period: { [agent]: input.spent_in_period ?? 0n }
51
+ },
52
+ max_amount_per_action: { [agent]: input.max_amount_per_action ?? 1_000_000n },
53
+ velocity: {
54
+ config: {
55
+ window_seconds: input.velocity_window_seconds ?? 60,
56
+ max_actions: input.velocity_max_actions ?? 1000
57
+ },
58
+ counters: {}
59
+ },
60
+ replay: {
61
+ window_seconds: input.replay_window_seconds ?? 3600,
62
+ max_nonces_per_agent: input.replay_max_nonces_per_agent ?? 256,
63
+ nonces: {}
64
+ },
65
+ concurrency: {
66
+ max_concurrent: { [agent]: input.max_concurrent ?? 16 },
67
+ active: {},
68
+ active_auths: {}
69
+ },
70
+ recursion: {
71
+ max_depth: { [agent]: input.max_depth ?? 8 }
72
+ },
73
+ tool_limits: {
74
+ window_seconds: input.tool_window_seconds ?? 60,
75
+ max_calls: { [agent]: input.tool_max_calls ?? 1000 },
76
+ max_calls_by_tool: {},
77
+ calls: {}
78
+ }
79
+ };
80
+ }
package/src/client.ts ADDED
@@ -0,0 +1,106 @@
1
+ import {
2
+ PolicyEngine,
3
+ encodeCanonicalState,
4
+ encodeEnvelope,
5
+ verifyAuditEvents,
6
+ verifyEnvelope,
7
+ verifySnapshot
8
+ } from "@oxdeai/core";
9
+ import type { Authorization, Intent } from "@oxdeai/core";
10
+
11
+ import type {
12
+ AuditAdapter,
13
+ ClockAdapter,
14
+ EvaluateAndCommitResult,
15
+ EvaluateDecision,
16
+ StateAdapter,
17
+ VerifyBundleResult
18
+ } from "./types.js";
19
+
20
+ type ClientOptions = {
21
+ engine: PolicyEngine;
22
+ stateAdapter: StateAdapter;
23
+ auditAdapter?: AuditAdapter;
24
+ clock?: ClockAdapter;
25
+ };
26
+
27
+ type EvaluateInput = Omit<Intent, "timestamp"> & { timestamp?: number };
28
+
29
+ function collectNewEvents(engine: PolicyEngine, cursor: number): { nextCursor: number; events: unknown[] } {
30
+ const all = engine.audit.snapshot() as unknown[];
31
+ const events = all.slice(cursor);
32
+ return { nextCursor: all.length, events };
33
+ }
34
+
35
+ export class OxDeAIClient {
36
+ private readonly engine: PolicyEngine;
37
+ private readonly stateAdapter: StateAdapter;
38
+ private readonly auditAdapter?: AuditAdapter;
39
+ private readonly clock: ClockAdapter;
40
+ private auditCursor = 0;
41
+
42
+ constructor(opts: ClientOptions) {
43
+ this.engine = opts.engine;
44
+ this.stateAdapter = opts.stateAdapter;
45
+ this.auditAdapter = opts.auditAdapter;
46
+ this.clock = opts.clock ?? { now: () => Math.floor(Date.now() / 1000) };
47
+ }
48
+
49
+ async evaluateAndCommit(input: EvaluateInput): Promise<EvaluateAndCommitResult> {
50
+ const state = await this.stateAdapter.load();
51
+ const resolvedTimestamp = input.timestamp === undefined || input.timestamp === 0
52
+ ? this.clock.now()
53
+ : input.timestamp;
54
+ const intent: Intent = {
55
+ ...input,
56
+ timestamp: resolvedTimestamp
57
+ } as Intent;
58
+
59
+ const out = this.engine.evaluatePure(intent, state, { mode: "fail-fast" });
60
+ const emitted = collectNewEvents(this.engine, this.auditCursor);
61
+ this.auditCursor = emitted.nextCursor;
62
+
63
+ if (this.auditAdapter) await this.auditAdapter.append(emitted.events);
64
+
65
+ if (out.decision === "ALLOW") {
66
+ await this.stateAdapter.save(out.nextState);
67
+ const output: EvaluateDecision = {
68
+ decision: "ALLOW",
69
+ reasons: [],
70
+ authorization: out.authorization
71
+ };
72
+ return { output, state: out.nextState, auditEvents: emitted.events };
73
+ }
74
+
75
+ const output: EvaluateDecision = {
76
+ decision: "DENY",
77
+ reasons: out.reasons
78
+ };
79
+ return { output, state, auditEvents: emitted.events };
80
+ }
81
+
82
+ async verifyAuthorization(intent: Intent, authorization: Authorization): Promise<{ valid: boolean; reason?: string }> {
83
+ const state = await this.stateAdapter.load();
84
+ const result = this.engine.verifyAuthorization(intent, authorization, state, intent.timestamp);
85
+ return result.valid ? { valid: true } : { valid: false, reason: result.reason };
86
+ }
87
+
88
+ async verifyCurrentArtifacts(opts?: { mode?: "strict" | "best-effort" }): Promise<VerifyBundleResult> {
89
+ const state = await this.stateAdapter.load();
90
+ const snapshotBytes = encodeCanonicalState(this.engine.exportState(state));
91
+ const snapshot = verifySnapshot(snapshotBytes);
92
+
93
+ const allEvents = this.engine.audit.snapshot();
94
+ const audit = verifyAuditEvents(allEvents as Parameters<typeof verifyAuditEvents>[0], {
95
+ mode: opts?.mode ?? "best-effort"
96
+ });
97
+
98
+ const envelopeBytes = encodeEnvelope({
99
+ formatVersion: 1,
100
+ snapshot: snapshotBytes,
101
+ events: allEvents as any
102
+ });
103
+ const envelope = verifyEnvelope(envelopeBytes, { mode: opts?.mode ?? "best-effort" });
104
+ return { snapshot, audit, envelope };
105
+ }
106
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ /** OxDeAI SDK — integration layer on top of @oxdeai/core. */
2
+ export * from "@oxdeai/core";
3
+ export * from "./types.js";
4
+ export * from "./builders.js";
5
+ export * from "./adapters.js";
6
+ export * from "./client.js";
@@ -0,0 +1,83 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import { PolicyEngine } from "@oxdeai/core";
5
+
6
+ import { InMemoryAuditAdapter, InMemoryStateAdapter } from "./adapters.js";
7
+ import { buildIntent, buildState } from "./builders.js";
8
+ import { OxDeAIClient } from "./client.js";
9
+
10
+ test("builder helpers create engine-compatible intent/state", () => {
11
+ const state = buildState({
12
+ policy_version: "v1",
13
+ agent_id: "agent-1",
14
+ allow_action_types: ["PROVISION"],
15
+ allow_targets: ["us-east-1"]
16
+ });
17
+ const intent = buildIntent({
18
+ intent_id: "intent-1",
19
+ agent_id: "agent-1",
20
+ action_type: "PROVISION",
21
+ amount: 100n,
22
+ target: "us-east-1",
23
+ nonce: 1n,
24
+ timestamp: 1_770_000_000
25
+ });
26
+
27
+ const engine = new PolicyEngine({
28
+ policy_version: "v1",
29
+ engine_secret: "dev-secret",
30
+ authorization_ttl_seconds: 60
31
+ });
32
+ const out = engine.evaluatePure(intent, state, { mode: "fail-fast" });
33
+ assert.equal(out.decision, "ALLOW");
34
+ });
35
+
36
+ test("OxDeAIClient evaluate+persist+verify flow", async () => {
37
+ const engine = new PolicyEngine({
38
+ policy_version: "v1",
39
+ engine_secret: "dev-secret",
40
+ authorization_ttl_seconds: 60
41
+ });
42
+ const stateAdapter = new InMemoryStateAdapter(
43
+ buildState({
44
+ policy_version: "v1",
45
+ agent_id: "agent-1",
46
+ allow_action_types: ["PROVISION"],
47
+ allow_targets: ["us-east-1"]
48
+ })
49
+ );
50
+ const auditAdapter = new InMemoryAuditAdapter();
51
+ const client = new OxDeAIClient({
52
+ engine,
53
+ stateAdapter,
54
+ auditAdapter,
55
+ clock: { now: () => 1_770_000_000 }
56
+ });
57
+
58
+ const intent = buildIntent({
59
+ intent_id: "intent-2",
60
+ agent_id: "agent-1",
61
+ action_type: "PROVISION",
62
+ amount: 320n,
63
+ target: "us-east-1",
64
+ nonce: 2n
65
+ });
66
+ const res = await client.evaluateAndCommit(intent);
67
+
68
+ assert.equal(res.output.decision, "ALLOW");
69
+ if (res.output.decision !== "ALLOW") return;
70
+ assert.ok(res.auditEvents.length >= 3);
71
+ assert.equal(auditAdapter.snapshot().length, res.auditEvents.length);
72
+
73
+ const auth = await client.verifyAuthorization(
74
+ { ...intent, timestamp: 1_770_000_000 },
75
+ res.output.authorization
76
+ );
77
+ assert.equal(auth.valid, true);
78
+
79
+ const verify = await client.verifyCurrentArtifacts({ mode: "best-effort" });
80
+ assert.equal(verify.snapshot.status, "ok");
81
+ assert.equal(verify.audit.status, "ok");
82
+ assert.equal(verify.envelope.status, "ok");
83
+ });
package/src/types.ts ADDED
@@ -0,0 +1,70 @@
1
+ import type { Authorization, Intent, State, VerificationResult } from "@oxdeai/core";
2
+
3
+ export type MaybePromise<T> = T | Promise<T>;
4
+
5
+ export type EvaluateDecision =
6
+ | { decision: "ALLOW"; reasons: []; authorization: Authorization }
7
+ | { decision: "DENY"; reasons: string[] };
8
+
9
+ export type EvaluateAndCommitResult = {
10
+ output: EvaluateDecision;
11
+ state: State;
12
+ auditEvents: unknown[];
13
+ };
14
+
15
+ export type VerifyBundleResult = {
16
+ snapshot: VerificationResult;
17
+ audit: VerificationResult;
18
+ envelope: VerificationResult;
19
+ };
20
+
21
+ export interface StateAdapter {
22
+ load(): MaybePromise<State>;
23
+ save(state: State): MaybePromise<void>;
24
+ }
25
+
26
+ export interface AuditAdapter {
27
+ append(events: readonly unknown[]): MaybePromise<void>;
28
+ }
29
+
30
+ export interface ClockAdapter {
31
+ now(): number;
32
+ }
33
+
34
+ export type IntentBuilderInput = {
35
+ intent_id: string;
36
+ agent_id: string;
37
+ action_type: Intent["action_type"];
38
+ amount: bigint;
39
+ target: string;
40
+ nonce: bigint;
41
+ timestamp?: number;
42
+ type?: "EXECUTE" | "RELEASE";
43
+ authorization_id?: string;
44
+ asset?: string;
45
+ metadata_hash?: string;
46
+ signature?: string;
47
+ depth?: number;
48
+ tool?: string;
49
+ tool_call?: boolean;
50
+ };
51
+
52
+ export type StateBuilderInput = {
53
+ policy_version?: string;
54
+ period_id?: string;
55
+ agent_id: string;
56
+ allow_action_types?: Intent["action_type"][];
57
+ allow_assets?: string[];
58
+ allow_targets?: string[];
59
+ budget_limit?: bigint;
60
+ spent_in_period?: bigint;
61
+ max_amount_per_action?: bigint;
62
+ velocity_window_seconds?: number;
63
+ velocity_max_actions?: number;
64
+ replay_window_seconds?: number;
65
+ replay_max_nonces_per_agent?: number;
66
+ max_concurrent?: number;
67
+ max_depth?: number;
68
+ tool_window_seconds?: number;
69
+ tool_max_calls?: number;
70
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src"]
9
+ }