@nexart/ai-execution 0.1.0 → 0.2.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 (49) hide show
  1. package/README.md +270 -112
  2. package/dist/__tests__/v020.test.d.ts +2 -0
  3. package/dist/__tests__/v020.test.d.ts.map +1 -0
  4. package/dist/__tests__/v020.test.js +408 -0
  5. package/dist/__tests__/v020.test.js.map +1 -0
  6. package/dist/archive.d.ts +4 -0
  7. package/dist/archive.d.ts.map +1 -0
  8. package/dist/archive.js +28 -0
  9. package/dist/archive.js.map +1 -0
  10. package/dist/attest.d.ts +3 -0
  11. package/dist/attest.d.ts.map +1 -0
  12. package/dist/attest.js +42 -0
  13. package/dist/attest.js.map +1 -0
  14. package/dist/certify.d.ts +3 -0
  15. package/dist/certify.d.ts.map +1 -0
  16. package/dist/certify.js +27 -0
  17. package/dist/certify.js.map +1 -0
  18. package/dist/errors.d.ts +10 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +19 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/index.d.ts +7 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +6 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/providers/anthropic.d.ts +22 -0
  27. package/dist/providers/anthropic.d.ts.map +1 -0
  28. package/dist/providers/anthropic.js +61 -0
  29. package/dist/providers/anthropic.js.map +1 -0
  30. package/dist/providers/wrap.d.ts +7 -0
  31. package/dist/providers/wrap.d.ts.map +1 -0
  32. package/dist/providers/wrap.js +28 -0
  33. package/dist/providers/wrap.js.map +1 -0
  34. package/dist/run.d.ts +14 -0
  35. package/dist/run.d.ts.map +1 -0
  36. package/dist/run.js +62 -0
  37. package/dist/run.js.map +1 -0
  38. package/dist/snapshot.d.ts.map +1 -1
  39. package/dist/snapshot.js +15 -2
  40. package/dist/snapshot.js.map +1 -1
  41. package/dist/types.d.ts +99 -0
  42. package/dist/types.d.ts.map +1 -1
  43. package/fixtures/golden/golden-001.json +29 -0
  44. package/fixtures/golden/golden-002.json +38 -0
  45. package/fixtures/golden/golden-003.json +36 -0
  46. package/fixtures/golden/golden-004.json +29 -0
  47. package/fixtures/golden/golden-005.json +29 -0
  48. package/fixtures/vectors/vector-002.chain.json +101 -0
  49. package/package.json +13 -3
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @nexart/ai-execution v0.1.0
1
+ # @nexart/ai-execution v0.2.0
2
2
 
3
3
  Tamper-evident records and Certified Execution Records (CER) for AI operations.
4
4
 
@@ -15,6 +15,13 @@ These records can be verified later to prove the execution happened as recorded.
15
15
 
16
16
  **Important:** This does NOT promise that an AI model will produce the same output twice. LLMs are not deterministic. This package provides **integrity and auditability** — proof that a specific input produced a specific output at a specific time with specific parameters.
17
17
 
18
+ ## Compatibility Guarantees
19
+
20
+ - **v0.1.0 bundles verify forever.** Any CER bundle produced by v0.1.0 will pass `verifyCer()` in v0.2.0 and all future versions.
21
+ - **Hashing rules are frozen for `cer.ai.execution.v1`.** The canonicalization, SHA-256 computation, and certificate hash inputs (bundleType, version, createdAt, snapshot) are unchanged.
22
+ - **New optional snapshot fields** (runId, stepId, stepIndex, etc.) default to undefined and are excluded from legacy snapshots. They participate in the certificate hash only when present.
23
+ - **If stricter canonicalization is needed in the future**, it will ship as a new bundle type (e.g. `cer.ai.execution.v2`), not as a change to v1.
24
+
18
25
  ## Installation
19
26
 
20
27
  ```bash
@@ -23,12 +30,27 @@ npm install @nexart/ai-execution
23
30
 
24
31
  ## Quick Start
25
32
 
26
- ### Create a Snapshot Manually
33
+ ### Single Decision (3 lines)
34
+
35
+ ```typescript
36
+ import { certifyDecision } from '@nexart/ai-execution';
37
+
38
+ const cer = certifyDecision({
39
+ provider: 'openai',
40
+ model: 'gpt-4o',
41
+ prompt: 'Summarize.',
42
+ input: userQuery,
43
+ output: llmResponse,
44
+ parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null },
45
+ });
46
+ console.log(cer.certificateHash); // "sha256:..."
47
+ ```
48
+
49
+ ### Manual Snapshot + Seal (v0.1.0 style, still supported)
27
50
 
28
51
  ```typescript
29
52
  import { createSnapshot, verifySnapshot, sealCer, verifyCer } from '@nexart/ai-execution';
30
53
 
31
- // Create a snapshot of an AI execution
32
54
  const snapshot = createSnapshot({
33
55
  executionId: 'exec-001',
34
56
  provider: 'openai',
@@ -36,28 +58,79 @@ const snapshot = createSnapshot({
36
58
  modelVersion: '2026-01-01',
37
59
  prompt: 'You are a helpful assistant.',
38
60
  input: 'What is 2+2?',
39
- parameters: {
40
- temperature: 0.7,
41
- maxTokens: 1024,
42
- topP: null,
43
- seed: null,
44
- },
61
+ parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null },
45
62
  output: 'The answer is 4.',
46
63
  });
47
64
 
48
- // Verify the snapshot hashes are correct
49
65
  const result = verifySnapshot(snapshot);
50
66
  console.log(result.ok); // true
51
67
 
52
- // Seal into a Certified Execution Record
53
68
  const bundle = sealCer(snapshot);
54
69
  console.log(bundle.certificateHash); // "sha256:..."
55
70
 
56
- // Verify the entire bundle
57
71
  const cerResult = verifyCer(bundle);
58
72
  console.log(cerResult.ok); // true
59
73
  ```
60
74
 
75
+ ### Agentic Multi-Step Workflow
76
+
77
+ ```typescript
78
+ import { RunBuilder } from '@nexart/ai-execution';
79
+
80
+ const run = new RunBuilder({ runId: 'analysis-run', workflowId: 'data-pipeline' });
81
+
82
+ const step0 = run.step({
83
+ provider: 'openai',
84
+ model: 'gpt-4o',
85
+ prompt: 'Plan the analysis.',
86
+ input: 'Analyze Q1 sales data.',
87
+ output: 'I will: 1) load data, 2) compute totals, 3) summarize.',
88
+ parameters: { temperature: 0.3, maxTokens: 512, topP: null, seed: null },
89
+ });
90
+ // step0.snapshot.stepIndex === 0, prevStepHash === null
91
+
92
+ const step1 = run.step({
93
+ provider: 'openai',
94
+ model: 'gpt-4o',
95
+ prompt: 'Execute step 1.',
96
+ input: 'Load and total Q1 data.',
97
+ output: 'Total revenue: $1.2M.',
98
+ parameters: { temperature: 0.3, maxTokens: 512, topP: null, seed: null },
99
+ });
100
+ // step1.snapshot.stepIndex === 1, prevStepHash === step0.certificateHash
101
+
102
+ const summary = run.finalize();
103
+ // { runId, stepCount: 2, steps: [...], finalStepHash: step1.certificateHash }
104
+ ```
105
+
106
+ ### Attest to Canonical Node
107
+
108
+ ```typescript
109
+ import { certifyDecision, attest } from '@nexart/ai-execution';
110
+
111
+ const cer = certifyDecision({ /* ... */ });
112
+ const proof = await attest(cer, {
113
+ nodeUrl: 'https://node.nexart.io',
114
+ apiKey: process.env.NEXART_API_KEY!,
115
+ });
116
+ console.log(proof.attestationId); // ledger proof reference
117
+ ```
118
+
119
+ ### Archive (Export / Import)
120
+
121
+ ```typescript
122
+ import { exportCer, importCer, sealCer, createSnapshot } from '@nexart/ai-execution';
123
+
124
+ const bundle = sealCer(createSnapshot({ /* ... */ }));
125
+
126
+ // Export as canonical JSON string (deterministic, archive-safe)
127
+ const json = exportCer(bundle);
128
+ await fs.writeFile('audit/cer-001.json', json);
129
+
130
+ // Import and verify in one step (throws CerVerificationError if invalid)
131
+ const restored = importCer(await fs.readFile('audit/cer-001.json', 'utf-8'));
132
+ ```
133
+
61
134
  ### With JSON Input/Output
62
135
 
63
136
  ```typescript
@@ -67,12 +140,7 @@ const snapshot = createSnapshot({
67
140
  model: 'gpt-4o',
68
141
  prompt: 'Extract entities',
69
142
  input: { text: 'John lives in Paris', lang: 'en' },
70
- parameters: {
71
- temperature: 0,
72
- maxTokens: 512,
73
- topP: null,
74
- seed: 42,
75
- },
143
+ parameters: { temperature: 0, maxTokens: 512, topP: null, seed: 42 },
76
144
  output: { entities: ['John', 'Paris'], count: 2 },
77
145
  });
78
146
  ```
@@ -83,8 +151,6 @@ Convenience adapter only; the core value of this package is the snapshot + hashi
83
151
  The OpenAI adapter wraps a single `chat/completions` call and returns a sealed CER bundle.
84
152
  **Provider determinism is not guaranteed** — identical inputs may produce different outputs across calls.
85
153
 
86
- If you have an OpenAI API key, the package includes a convenience adapter:
87
-
88
154
  ```typescript
89
155
  import { runOpenAIChatExecution } from '@nexart/ai-execution/providers/openai';
90
156
 
@@ -92,19 +158,65 @@ const result = await runOpenAIChatExecution({
92
158
  prompt: 'You are a helpful assistant.',
93
159
  input: 'What is the capital of France?',
94
160
  model: 'gpt-4o',
95
- parameters: {
96
- temperature: 0.7,
97
- maxTokens: 256,
98
- },
161
+ parameters: { temperature: 0.7, maxTokens: 256 },
99
162
  });
100
163
 
101
- console.log(result.output); // "The capital of France is Paris."
102
- console.log(result.snapshot.inputHash); // "sha256:..."
164
+ console.log(result.output); // "The capital of France is Paris."
165
+ console.log(result.snapshot.inputHash); // "sha256:..."
103
166
  console.log(result.bundle.certificateHash); // "sha256:..."
104
167
  ```
105
168
 
106
169
  Requires `OPENAI_API_KEY` environment variable or `apiKey` parameter.
107
170
 
171
+ ### Anthropic Provider (Optional)
172
+
173
+ ```typescript
174
+ import { runAnthropicExecution } from '@nexart/ai-execution/providers/anthropic';
175
+
176
+ const result = await runAnthropicExecution({
177
+ prompt: 'You are a helpful assistant.',
178
+ input: 'Explain quantum computing briefly.',
179
+ model: 'claude-sonnet-4-20250514',
180
+ parameters: { temperature: 0.5, maxTokens: 512 },
181
+ });
182
+ ```
183
+
184
+ Requires `ANTHROPIC_API_KEY` environment variable or `apiKey` parameter.
185
+
186
+ ### Generic Provider Wrapper
187
+
188
+ ```typescript
189
+ import { wrapProvider } from '@nexart/ai-execution/providers/wrap';
190
+
191
+ const myProvider = wrapProvider({
192
+ provider: 'my-llm',
193
+ callFn: async (input) => await myLlmClient.chat(input),
194
+ extractOutput: (raw) => raw.message,
195
+ extractModelVersion: (raw) => raw.modelVersion,
196
+ });
197
+
198
+ const result = await myProvider.execute({
199
+ providerInput: { messages: [{ role: 'user', content: 'Hello' }] },
200
+ prompt: 'System prompt',
201
+ input: 'Hello',
202
+ model: 'my-model-v2',
203
+ parameters: { temperature: 0.7, maxTokens: 1024, topP: null, seed: null },
204
+ });
205
+ ```
206
+
207
+ ## v0.2.0 Additions
208
+
209
+ | Feature | Description |
210
+ |---|---|
211
+ | `certifyDecision()` | One-call convenience: createSnapshot + sealCer combined |
212
+ | `RunBuilder` | Agentic workflow support with step chaining and prevStepHash |
213
+ | `attest()` | Post CER bundle to canonical node for ledger attestation |
214
+ | `exportCer()` / `importCer()` | Archive-safe canonical JSON serialization with verification |
215
+ | `wrapProvider()` | Generic provider wrapper factory |
216
+ | Anthropic adapter | `@nexart/ai-execution/providers/anthropic` |
217
+ | Typed errors | `CerVerificationError`, `CerAttestationError` |
218
+ | Workflow fields | `runId`, `stepId`, `stepIndex`, `workflowId`, `conversationId`, `prevStepHash` |
219
+
108
220
  ## Snapshot Format (ai.execution.v1)
109
221
 
110
222
  ### Required vs Optional Fields
@@ -123,8 +235,14 @@ Requires `OPENAI_API_KEY` environment variable or `apiKey` parameter.
123
235
  | `modelVersion` | Optional | `string \| null` | Defaults to `null` |
124
236
  | `parameters.topP` | Optional | `number \| null` | Defaults to `null` |
125
237
  | `parameters.seed` | Optional | `number \| null` | Defaults to `null` |
126
- | `sdkVersion` | Optional | `string \| null` | Defaults to package version (`"0.1.0"`) |
238
+ | `sdkVersion` | Optional | `string \| null` | Defaults to package version (`"0.2.0"`) |
127
239
  | `appId` | Optional | `string \| null` | Defaults to `null` |
240
+ | `runId` | Optional | `string \| null` | Workflow run ID (v0.2.0+) |
241
+ | `stepId` | Optional | `string \| null` | Step identifier within a run (v0.2.0+) |
242
+ | `stepIndex` | Optional | `number \| null` | 0-based step position (v0.2.0+) |
243
+ | `workflowId` | Optional | `string \| null` | Workflow template ID (v0.2.0+) |
244
+ | `conversationId` | Optional | `string \| null` | Conversation/session ID (v0.2.0+) |
245
+ | `prevStepHash` | Optional | `string \| null` | certificateHash of previous step (v0.2.0+) |
128
246
 
129
247
  The following fields are **auto-generated** by `createSnapshot` and must not be set manually:
130
248
 
@@ -136,34 +254,6 @@ The following fields are **auto-generated** by `createSnapshot` and must not be
136
254
  | `inputHash` | SHA-256 of input |
137
255
  | `outputHash` | SHA-256 of output |
138
256
 
139
- ### Example Snapshot
140
-
141
- ```json
142
- {
143
- "type": "ai.execution.v1",
144
- "protocolVersion": "1.2.0",
145
- "executionSurface": "ai",
146
- "executionId": "exec-001",
147
- "timestamp": "2026-02-12T00:00:00.000Z",
148
- "provider": "openai",
149
- "model": "gpt-4o",
150
- "modelVersion": "2026-01-01",
151
- "prompt": "You are a helpful assistant.",
152
- "input": "What is 2+2?",
153
- "inputHash": "sha256:...",
154
- "parameters": {
155
- "temperature": 0.7,
156
- "maxTokens": 1024,
157
- "topP": null,
158
- "seed": null
159
- },
160
- "output": "The answer is 4.",
161
- "outputHash": "sha256:...",
162
- "sdkVersion": "0.1.0",
163
- "appId": null
164
- }
165
- ```
166
-
167
257
  ## CER Bundle Format
168
258
 
169
259
  ```json
@@ -191,6 +281,7 @@ The `certificateHash` is SHA-256 of the UTF-8 bytes of the canonical JSON of **e
191
281
  - `meta` is **excluded** from the certificate hash. It is an informational envelope field that does not affect integrity verification.
192
282
  - Key-ordering rules apply **recursively** — every nested object within `snapshot` is also sorted lexicographically by key.
193
283
  - The bytes being hashed are the UTF-8 encoding of the canonical JSON string (no BOM, no trailing newline).
284
+ - **This computation is identical in v0.1.0 and v0.2.0.** New optional snapshot fields (runId, stepIndex, etc.) participate in the hash only when present in the snapshot object.
194
285
 
195
286
  ## Hashing Rules
196
287
 
@@ -213,102 +304,117 @@ Canonical JSON is a deterministic subset of JSON. The following rules apply:
213
304
 
214
305
  These constraints ensure that the same logical value always produces the same byte sequence, which is essential for hash stability across implementations and languages.
215
306
 
307
+ **Note on number formatting:** v1 bundles use `JSON.stringify()` for number-to-string conversion, which is consistent across JavaScript engines but does not implement RFC 8785 (JCS) number formatting for edge cases like `-0`. If stricter canonicalization is required in the future, it will be introduced via a new bundle type (`cer.ai.execution.v2`), not by modifying v1 behavior.
308
+
309
+ ## RunBuilder: Agentic Workflow Chaining
310
+
311
+ The `RunBuilder` class enables multi-step AI workflows with cryptographic chaining:
312
+
313
+ - **Auto-incrementing `stepIndex`**: starts at 0, increments with each `.step()` call.
314
+ - **`prevStepHash` chaining**: each step's `prevStepHash` is set to the `certificateHash` of the previous step (null for step 0).
315
+ - **`executionId` generation**: automatically set to `{runId}-step-{stepIndex}`.
316
+ - Each step produces a full, independently verifiable CER bundle.
317
+ - Call `.finalize()` to get a `RunSummary` with all step hashes and chain metadata.
318
+
319
+ ## Attestation
320
+
321
+ The `attest()` function posts a sealed CER bundle to a NexArt canonical node:
322
+
323
+ ```typescript
324
+ const proof = await attest(bundle, {
325
+ nodeUrl: 'https://node.nexart.io',
326
+ apiKey: 'your-api-key',
327
+ });
328
+ ```
329
+
330
+ - Endpoint: `POST {nodeUrl}/api/attest`
331
+ - Authorization: `Bearer {apiKey}`
332
+ - Body: the full CER bundle as JSON
333
+ - Returns: `AttestationResult` with `attestationId`, `nodeRuntimeHash`, `certificateHash`, `protocolVersion`
334
+ - Throws: `CerAttestationError` on network or HTTP errors
335
+
336
+ ## Error Types
337
+
338
+ | Error | When thrown |
339
+ |---|---|
340
+ | `CerVerificationError` | `importCer()` receives invalid or tampered JSON |
341
+ | `CerAttestationError` | `attest()` fails (network error, HTTP error, invalid response) |
342
+
343
+ Both extend `Error` and include structured data:
344
+ - `CerVerificationError.errors`: `string[]` of specific verification failures
345
+ - `CerAttestationError.statusCode`: HTTP status code (if available)
346
+ - `CerAttestationError.responseBody`: raw response body (if available)
347
+
216
348
  ## Interoperability (Test Vectors)
217
349
 
218
- Cross-language implementations **must** match the test vectors exactly to be considered compatible. Vectors are committed as JSON fixtures at:
350
+ Cross-language implementations **must** match the test vectors exactly. Vectors are committed as JSON fixtures at:
219
351
 
220
352
  ```
221
353
  packages/ai-execution/fixtures/vectors/
222
354
  ```
223
355
 
224
- ### Vector 001
356
+ ### Vector 001 (single decision)
225
357
 
226
- **Input snapshot** (`vector-001.snapshot.json`):
358
+ See `vector-001.snapshot.json` and `vector-001.expected.json` for a single text-in/text-out CER with committed hash values.
227
359
 
228
- ```json
229
- {
230
- "type": "ai.execution.v1",
231
- "protocolVersion": "1.2.0",
232
- "executionSurface": "ai",
233
- "executionId": "vec-001",
234
- "timestamp": "2026-02-12T00:00:00.000Z",
235
- "provider": "openai",
236
- "model": "gpt-4o",
237
- "modelVersion": "2026-01-01",
238
- "prompt": "You are a helpful assistant.",
239
- "input": "What is 2+2?",
240
- "inputHash": "sha256:52cb6b5e4a038af1756708f98afb718a08c75b87b2f03dbee4dd9c8139c15c5e",
241
- "parameters": {
242
- "temperature": 0.7,
243
- "maxTokens": 1024,
244
- "topP": null,
245
- "seed": null
246
- },
247
- "output": "The answer is 4.",
248
- "outputHash": "sha256:ae758477f843049bd252ceb5498aa33f190326589ee92cbe5a1ab563f54bc05b",
249
- "sdkVersion": "0.1.0",
250
- "appId": "vector-test"
251
- }
252
- ```
360
+ ### Vector 002 (3-step agentic chain)
253
361
 
254
- **Expected hashes** (`vector-001.expected.json`):
362
+ See `vector-002.chain.json` for a 3-step workflow chain with:
363
+ - Step 0: `prevStepHash: null`, `stepIndex: 0`
364
+ - Step 1: `prevStepHash: step0.certificateHash`, `stepIndex: 1`
365
+ - Step 2: `prevStepHash: step1.certificateHash`, `stepIndex: 2`
255
366
 
256
- | Hash | Value |
257
- |---|---|
258
- | `inputHash` | `sha256:52cb6b5e4a038af1756708f98afb718a08c75b87b2f03dbee4dd9c8139c15c5e` |
259
- | `outputHash` | `sha256:ae758477f843049bd252ceb5498aa33f190326589ee92cbe5a1ab563f54bc05b` |
260
- | `certificateHash` | `sha256:86275d60d088483eefaf0bd31d79629b11342315816f3a1da26980e4a05352f4` |
367
+ Each entry includes `snapshot`, `expectedCertificateHash`, and `cerCreatedAt` for deterministic verification.
368
+
369
+ ### Golden Fixtures (backward compatibility)
261
370
 
262
- The `certificateHash` is computed with `cerCreatedAt: "2026-02-12T00:00:00.000Z"`.
371
+ 5 CER bundles produced with v0.1.0 semantics are committed at:
263
372
 
264
- **Canonicalization rules for verification:**
265
- 1. Object keys sorted lexicographically at every level.
266
- 2. No whitespace between tokens.
267
- 3. Arrays preserve insertion order.
268
- 4. Hashes are SHA-256 of the UTF-8 bytes of the resulting string.
373
+ ```
374
+ packages/ai-execution/fixtures/golden/
375
+ ```
269
376
 
270
- To verify compatibility, a cross-language implementation should:
271
- 1. Load `vector-001.snapshot.json`.
272
- 2. Recompute `inputHash` from `snapshot.input` and assert it matches `expected.inputHash`.
273
- 3. Recompute `outputHash` from `snapshot.output` and assert it matches `expected.outputHash`.
274
- 4. Construct a CER payload `{ bundleType: "cer.ai.execution.v1", version: "0.1", createdAt: "2026-02-12T00:00:00.000Z", snapshot }`, canonicalize it, hash it, and assert it matches `expected.certificateHash`.
377
+ These bundles must verify with every future version of the library.
275
378
 
276
379
  ## Threat Model
277
380
 
278
- This package provides **integrity and auditability** for AI execution records. It is an auditing tool, not a security boundary.
381
+ This package provides **integrity and auditability** for AI execution records.
279
382
 
280
383
  ### What it prevents
281
384
 
282
385
  - **Output tampering** — any modification to the recorded output invalidates the `outputHash` and `certificateHash`.
283
- - **Parameter laundering** — changing temperature, model, seed, or other parameters after the fact is detectable because they are included in the certificate hash.
386
+ - **Parameter laundering** — changing temperature, model, seed, or other parameters after the fact is detectable.
284
387
  - **Silent record edits** — any modification to a sealed CER bundle is detectable via `verifyCer()`.
388
+ - **Chain forgery** — in multi-step workflows, `prevStepHash` links each step to the previous, making it impossible to insert, remove, or reorder steps without detection.
285
389
 
286
390
  ### What it does NOT prevent
287
391
 
288
- - **Provider lying** — if the AI provider returns fabricated output, this package faithfully records it. The record proves what the provider returned, not that the provider was honest.
289
- - **Model drift** — models change over time. The same input may produce different output with the same model name on different dates. `modelVersion` helps track this but cannot prevent it.
392
+ - **Provider lying** — if the AI provider returns fabricated output, this package faithfully records it.
393
+ - **Model drift** — models change over time. `modelVersion` helps track this but cannot prevent it.
290
394
  - **Correctness of the answer** — this package records what was said, not whether it was right.
291
395
 
292
396
  ## Verification Best Practice
293
397
 
294
398
  **Recommended flow:**
295
399
 
296
- 1. Call `createSnapshot(...)` immediately after receiving the AI response.
297
- 2. Call `sealCer(snapshot)` to produce the CER bundle.
298
- 3. Call `verifyCer(bundle)` **before** storing or transmitting the bundle this catches any in-memory corruption.
299
- 4. Store the full bundle as an audit artifact. Treat it as immutable.
400
+ 1. Call `certifyDecision(...)` (or `createSnapshot` + `sealCer`).
401
+ 2. Call `verifyCer(bundle)` **before** storing catches any in-memory corruption.
402
+ 3. Store the full bundle as an immutable audit artifact.
403
+ 4. Optionally call `attest(bundle, ...)` to register on the canonical node ledger.
300
404
  5. On retrieval, call `verifyCer(bundle)` again to confirm integrity.
301
405
 
302
406
  ```typescript
303
- const snapshot = createSnapshot({ ... });
304
- const bundle = sealCer(snapshot);
407
+ const bundle = certifyDecision({ ... });
305
408
 
306
409
  const check = verifyCer(bundle);
307
410
  if (!check.ok) {
308
411
  throw new Error(`CER integrity failure: ${check.errors.join('; ')}`);
309
412
  }
310
413
 
311
- await store(bundle); // persist as audit artifact
414
+ await store(bundle);
415
+
416
+ // Optional: attest to canonical node
417
+ const proof = await attest(bundle, { nodeUrl, apiKey });
312
418
  ```
313
419
 
314
420
  ## Privacy & Data Handling
@@ -321,17 +427,46 @@ Snapshots include **raw prompt, input, and output by default**. This means:
321
427
  **Recommendations:**
322
428
 
323
429
  - For sensitive workloads, consider redacting input/output before calling `createSnapshot()`, then verify the redacted versions.
324
- - A hash-only mode (where only hashes are stored, not raw content) is **not implemented in v0.1.0**. It is planned for a future release.
430
+ - A dedicated redacted bundle type (`cer.ai.execution.redacted.v1`) is planned for a future release.
325
431
  - Treat CER bundles with the same access controls as the underlying data they contain.
326
432
 
327
433
  ## API Reference
328
434
 
435
+ ### Core
436
+
329
437
  | Function | Description |
330
438
  |----------|-------------|
331
439
  | `createSnapshot(params)` | Create an AI execution snapshot with computed hashes |
332
440
  | `verifySnapshot(snapshot)` | Verify snapshot hashes and structure |
333
441
  | `sealCer(snapshot, options?)` | Seal a snapshot into a CER bundle |
334
442
  | `verifyCer(bundle)` | Verify a CER bundle (snapshot + certificate hash) |
443
+ | `certifyDecision(params)` | One-call: createSnapshot + sealCer combined **(v0.2.0)** |
444
+
445
+ ### Workflow
446
+
447
+ | Function/Class | Description |
448
+ |----------|-------------|
449
+ | `RunBuilder` | Multi-step workflow builder with step chaining **(v0.2.0)** |
450
+ | `RunBuilder.step(params)` | Add a step, returns sealed CER bundle |
451
+ | `RunBuilder.finalize()` | Returns RunSummary with all step hashes |
452
+
453
+ ### Attestation
454
+
455
+ | Function | Description |
456
+ |----------|-------------|
457
+ | `attest(bundle, options)` | Post CER bundle to canonical node **(v0.2.0)** |
458
+
459
+ ### Archive
460
+
461
+ | Function | Description |
462
+ |----------|-------------|
463
+ | `exportCer(bundle)` | Serialize bundle to canonical JSON string **(v0.2.0)** |
464
+ | `importCer(json)` | Parse + verify bundle from JSON string **(v0.2.0)** |
465
+
466
+ ### Hashing
467
+
468
+ | Function | Description |
469
+ |----------|-------------|
335
470
  | `computeInputHash(input)` | Compute hash for input (string or object) |
336
471
  | `computeOutputHash(output)` | Compute hash for output (string or object) |
337
472
  | `toCanonicalJson(value)` | Deterministic JSON serialization |
@@ -339,6 +474,29 @@ Snapshots include **raw prompt, input, and output by default**. This means:
339
474
  | `hashUtf8(value)` | Hash a UTF-8 string |
340
475
  | `hashCanonicalJson(value)` | Hash canonical JSON of a value |
341
476
 
477
+ ### Providers
478
+
479
+ | Function | Description |
480
+ |----------|-------------|
481
+ | `runOpenAIChatExecution(params)` | OpenAI chat wrapper (sub-export) |
482
+ | `runAnthropicExecution(params)` | Anthropic Messages wrapper **(v0.2.0)** |
483
+ | `wrapProvider(config)` | Generic provider wrapper factory **(v0.2.0)** |
484
+
485
+ ### Errors
486
+
487
+ | Class | Description |
488
+ |----------|-------------|
489
+ | `CerVerificationError` | Thrown by `importCer()` on invalid/tampered data **(v0.2.0)** |
490
+ | `CerAttestationError` | Thrown by `attest()` on failure **(v0.2.0)** |
491
+
492
+ ## Version Plan
493
+
494
+ | Version | Status | Description |
495
+ |---------|--------|-------------|
496
+ | v0.1.0 | Released | Core snapshot + CER + verify + OpenAI adapter |
497
+ | **v0.2.0** | **Current** | certifyDecision, RunBuilder, attest, archive, Anthropic, wrapProvider, typed errors, workflow fields |
498
+ | v1.0.0 | Planned | API stabilization. Rename `sealCer`→`seal`, `verifyCer`→`verify` (old names kept as deprecated aliases). Freeze public API surface. |
499
+
342
500
  ## How to Publish
343
501
 
344
502
  ```bash
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=v020.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v020.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/v020.test.ts"],"names":[],"mappings":""}