@vorim/sdk 3.0.2 → 3.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 (38) hide show
  1. package/README.md +27 -10
  2. package/dist/index.cjs +268 -10
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +300 -4
  5. package/dist/index.d.ts +300 -4
  6. package/dist/index.js +256 -9
  7. package/dist/index.js.map +1 -1
  8. package/dist/integrations/anthropic.cjs +108 -4
  9. package/dist/integrations/anthropic.cjs.map +1 -1
  10. package/dist/integrations/anthropic.d.cts +13 -2
  11. package/dist/integrations/anthropic.d.ts +13 -2
  12. package/dist/integrations/anthropic.js +96 -4
  13. package/dist/integrations/anthropic.js.map +1 -1
  14. package/dist/integrations/crewai.cjs +8 -2
  15. package/dist/integrations/crewai.cjs.map +1 -1
  16. package/dist/integrations/crewai.d.cts +18 -0
  17. package/dist/integrations/crewai.d.ts +18 -0
  18. package/dist/integrations/crewai.js +8 -2
  19. package/dist/integrations/crewai.js.map +1 -1
  20. package/dist/integrations/langchain.cjs +140 -10
  21. package/dist/integrations/langchain.cjs.map +1 -1
  22. package/dist/integrations/langchain.d.cts +23 -2
  23. package/dist/integrations/langchain.d.ts +23 -2
  24. package/dist/integrations/langchain.js +128 -10
  25. package/dist/integrations/langchain.js.map +1 -1
  26. package/dist/integrations/llamaindex.cjs +96 -4
  27. package/dist/integrations/llamaindex.cjs.map +1 -1
  28. package/dist/integrations/llamaindex.d.cts +7 -1
  29. package/dist/integrations/llamaindex.d.ts +7 -1
  30. package/dist/integrations/llamaindex.js +84 -4
  31. package/dist/integrations/llamaindex.js.map +1 -1
  32. package/dist/integrations/openai.cjs +108 -4
  33. package/dist/integrations/openai.cjs.map +1 -1
  34. package/dist/integrations/openai.d.cts +15 -2
  35. package/dist/integrations/openai.d.ts +15 -2
  36. package/dist/integrations/openai.js +96 -4
  37. package/dist/integrations/openai.js.map +1 -1
  38. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -52,6 +52,29 @@ interface AuditEventInput {
52
52
  error_code?: string;
53
53
  signature?: string;
54
54
  metadata?: Record<string, unknown>;
55
+ /**
56
+ * Replayable agent decision evidence (VAIP -02 schema fields).
57
+ *
58
+ * Stored and exported but NOT covered by the v0 canonical signature
59
+ * form. They will enter canonical bytes in v1 (RFC 8785 JCS) in a
60
+ * follow-up release. Until then, advisory.
61
+ *
62
+ * Use the helpers from this package to compute the hashes:
63
+ * - {@link hashToolCatalogue}
64
+ * - {@link hashSystemPrompt}
65
+ */
66
+ model_version?: string;
67
+ tool_catalogue_hash?: string;
68
+ system_prompt_hash?: string;
69
+ prev_event_hash?: string;
70
+ /**
71
+ * Canonical-form recipe the `signature` was computed over.
72
+ * Absent/null ↔ 'v0' (pipe-joined six-field form). Set to 'v1' for
73
+ * RFC 8785 JCS over the full event minus signature and canonical_form.
74
+ * The SDK sets this automatically when {@link VorimConfig.canonicalForm}
75
+ * is `"v1"`; you can also pass it on a per-event basis.
76
+ */
77
+ canonical_form?: 'v0' | 'v1';
55
78
  }
56
79
  interface TrustRecord {
57
80
  agent_id: string;
@@ -68,16 +91,239 @@ interface TrustRecord {
68
91
  last_active?: string;
69
92
  }
70
93
 
94
+ /**
95
+ * Replayable agent decision evidence helpers.
96
+ *
97
+ * Canonical-form hashing for the VAIP -02 schema fields that the SDK
98
+ * attaches to audit events. The hashes recorded in audit_events.tool_catalogue_hash
99
+ * and audit_events.system_prompt_hash use these functions, so the bytes
100
+ * an auditor or counterparty reconstructs must match what the SDK produced.
101
+ *
102
+ * These helpers are intentionally separate from the signing path. The
103
+ * v0 canonical signature form (event_type|action|resource|input_hash|
104
+ * output_hash|result) does NOT cover model_version, tool_catalogue_hash,
105
+ * or system_prompt_hash. They will enter the canonical bytes in v1
106
+ * (RFC 8785 JCS) in a follow-up release.
107
+ *
108
+ * Stable across SDK versions: the canonical-form version is documented
109
+ * in CANONICAL_TOOL_CATALOGUE_VERSION. Future changes get a v2 etc;
110
+ * never edit the existing v1 logic, or already-recorded hashes lose
111
+ * their meaning.
112
+ */
113
+ /**
114
+ * Canonical-form version for tool catalogue hashes produced by this SDK.
115
+ * Recorded in tool_catalogue_canon_version on the event metadata (when
116
+ * the metadata field is used) so verifiers know which hash recipe to
117
+ * reproduce. Increment ONLY if the algorithm changes in a way that
118
+ * would change the hash for the same logical catalogue.
119
+ */
120
+ declare const CANONICAL_TOOL_CATALOGUE_VERSION: "v1";
121
+ /**
122
+ * Minimum shape a tool needs for catalogue hashing. The framework
123
+ * integrations adapt their native tool objects to this shape before
124
+ * calling hashToolCatalogue.
125
+ */
126
+ interface CatalogueTool {
127
+ /** The name the model sees and calls. Required. */
128
+ name: string;
129
+ /** Human-readable description shown to the model. Optional; absent ↔ empty string. */
130
+ description?: string;
131
+ /**
132
+ * JSON Schema describing the tool's input parameters. Optional;
133
+ * absent ↔ empty object `{}`. The schema gets RFC 8785 JCS-canonicalised
134
+ * before hashing so semantically-equivalent variations (key order,
135
+ * whitespace) produce the same hash.
136
+ */
137
+ schema?: Record<string, unknown> | null;
138
+ }
139
+ /**
140
+ * RFC 8785 JSON Canonicalization Scheme, sufficient subset for tool
141
+ * catalogue values.
142
+ *
143
+ * Rules:
144
+ * - Object keys sorted lexicographically by UTF-16 code units (which
145
+ * is what JS string comparison does naturally).
146
+ * - No whitespace between tokens.
147
+ * - Numbers: integers as integers, finite floats per ECMAScript
148
+ * Number.prototype.toString. JCS forbids NaN and Infinity.
149
+ * - Strings: JSON-escape using minimal set per RFC 8259 § 7.
150
+ * - null, true, false, arrays: as JSON.stringify produces them, since
151
+ * JSON.stringify already produces the canonical form for these.
152
+ *
153
+ * Not vendoring a full library because tool schemas don't carry
154
+ * non-integer numbers in practice and the JS spec for Number.toString
155
+ * happens to coincide with JCS § 3.2.2.2 for the integer case.
156
+ */
157
+ declare function jcsCanonicalise(value: unknown): string;
158
+ /**
159
+ * Hash a single tool definition. Returns `sha256:<hex>`.
160
+ *
161
+ * Canonical form (v1):
162
+ * JCS-canonicalised JSON of `{name, description, schema}` where
163
+ * absent fields substitute `description: ""` and `schema: {}`.
164
+ */
165
+ declare function hashTool(tool: CatalogueTool): Promise<string>;
166
+ /**
167
+ * Hash an entire tool catalogue. Returns `sha256:<hex>`.
168
+ *
169
+ * Reordering tools does NOT change the hash (tool hashes sorted
170
+ * lexicographically before concatenation). Adding, removing, or
171
+ * modifying a tool DOES change the hash.
172
+ *
173
+ * Per-tool hashing first means a verifier comparing two catalogue
174
+ * hashes that differ can also be given the per-tool hashes to
175
+ * identify which specific tool changed.
176
+ */
177
+ declare function hashToolCatalogue(tools: CatalogueTool[]): Promise<string>;
178
+ /**
179
+ * Hash a system prompt. Returns `sha256:<hex>`.
180
+ *
181
+ * The prompt is UTF-8 encoded and hashed verbatim — no normalisation.
182
+ * If a caller wants to ignore whitespace or comment differences, they
183
+ * should normalise before calling. The intent here is deterministic
184
+ * reproducibility, not semantic equivalence.
185
+ */
186
+ declare function hashSystemPrompt(prompt: string): Promise<string>;
187
+ /**
188
+ * Convenience: hash the previous event's canonical bytes for use in
189
+ * the prev_event_hash field of hash-chained ingest. Caller provides
190
+ * the canonical bytes (use canonicalPayloadV0 from the main module).
191
+ */
192
+ declare function hashPreviousEvent(canonicalBytes: string): Promise<string>;
193
+ /**
194
+ * Raw inputs the integration captures from the framework. Set by the
195
+ * integration's config; turned into hashes by {@link prepareReplayContext}.
196
+ */
197
+ interface ReplayInputs {
198
+ /** Stable identifier for the model. E.g. `"anthropic:claude-opus-4-8"`. */
199
+ modelVersion?: string;
200
+ /** Tools available to the agent at call time. Hashed via {@link hashToolCatalogue}. */
201
+ tools?: CatalogueTool[];
202
+ /** System prompt active at call time. Hashed via {@link hashSystemPrompt}. */
203
+ systemPrompt?: string;
204
+ }
205
+ /**
206
+ * Pre-computed hashes ready to attach to audit events. The three keys
207
+ * match the audit_events column names.
208
+ */
209
+ interface ReplayContext {
210
+ model_version?: string;
211
+ tool_catalogue_hash?: string;
212
+ system_prompt_hash?: string;
213
+ }
214
+ /**
215
+ * Compute replay context once from raw inputs. Use at integration
216
+ * setup time so each emit can attach the hashes without re-hashing.
217
+ *
218
+ * Returns an object suitable for spreading into an AuditEventInput:
219
+ * `await vorim.emit({ ...event, ...replayContext })`
220
+ *
221
+ * If a field is absent in the inputs, it is absent in the result
222
+ * (not the empty string). That keeps the event lean.
223
+ */
224
+ declare function prepareReplayContext(inputs: ReplayInputs): Promise<ReplayContext>;
225
+
71
226
  interface VorimConfig {
72
227
  apiKey: string;
73
228
  baseUrl?: string;
74
229
  timeout?: number;
230
+ /**
231
+ * Auto-sign audit events with the agent's private key at emit time.
232
+ * Default true in v3.1+. Set to false to opt out globally.
233
+ * When enabled, the SDK signs the event using whichever private key was
234
+ * stored via {@link VorimSDK.useAgentKey} or returned by
235
+ * {@link VorimSDK.register}. Events for unknown agents pass through unsigned.
236
+ */
237
+ autoSign?: boolean;
238
+ /**
239
+ * Canonical form to use when signing. Default `"v0"` for backward-compat
240
+ * with `@vorim/sdk` 3.1.x. Set to `"v1"` to sign the full event object
241
+ * via RFC 8785 JCS, covering replayable-evidence fields (model_version,
242
+ * tool_catalogue_hash, system_prompt_hash, prev_event_hash). v1 events
243
+ * carry `canonical_form: "v1"` on the wire so the server can dispatch
244
+ * verification correctly.
245
+ */
246
+ canonicalForm?: 'v0' | 'v1';
247
+ /**
248
+ * Hash-chain events per agent so deletion of a single audit row
249
+ * becomes detectable. Default `false` (no chaining). When enabled,
250
+ * the SDK sets `prev_event_hash` on each event to SHA-256 of the
251
+ * previous event's canonical bytes for the same agent. The first
252
+ * event after enable / process restart has `prev_event_hash = null`,
253
+ * which the verifier reports as `chain_restart` (informational, not
254
+ * a failure). Chain integrity is checked by `@vorim/verify`.
255
+ */
256
+ chainEvents?: boolean;
75
257
  }
258
+ /**
259
+ * VAIP v0 canonical bytes used by Vorim's per-event signing.
260
+ *
261
+ * Pipe-joined content fields with empty-string substitution for missing values.
262
+ * This is intentionally duplicated from `@vorim/shared-types` so the published
263
+ * SDK ships with zero runtime dependencies. A parity test in this package
264
+ * imports both definitions and asserts byte-exact equality across a fixture
265
+ * matrix, so they cannot drift silently.
266
+ *
267
+ * To upgrade the format, version the function (`canonicalPayloadV1`) — never
268
+ * edit this one. Old signatures must remain verifiable.
269
+ */
270
+ declare function canonicalPayloadV0(event: AuditEventInput): string;
271
+ /**
272
+ * VAIP v1 canonical bytes for audit-event signing (RFC 8785 JCS).
273
+ *
274
+ * Signs the full event object excluding `signature` (the field being
275
+ * computed) and `canonical_form` (metadata about the recipe). Unlike v0,
276
+ * v1 covers the replayable-evidence fields and the metadata field.
277
+ *
278
+ * Re-exports `jcsCanonicalise` from `./replay.js` which is the byte-exact
279
+ * twin of `@vorim/shared-types`' implementation. The cross-language parity
280
+ * script enforces equivalence with the Python SDK.
281
+ */
282
+ declare function canonicalPayloadV1(event: AuditEventInput): string;
76
283
  declare class VorimSDK {
77
284
  private apiKey;
78
285
  private baseUrl;
79
286
  private timeout;
287
+ private autoSign;
288
+ private canonicalForm;
289
+ private chainEvents;
290
+ /**
291
+ * In-memory keyring mapping agent_id -> PEM-encoded Ed25519 private key.
292
+ * Populated automatically by register() and registerEphemeral(), or
293
+ * explicitly via useAgentKey(). Lost on process restart by design; the
294
+ * caller is responsible for durable key storage.
295
+ */
296
+ private agentKeys;
297
+ /**
298
+ * Per-agent hash of the last emitted event's canonical bytes. Used to
299
+ * populate prev_event_hash on the next emit when chainEvents is on.
300
+ * Empty string ↔ no previous event (chain restart). Reset by
301
+ * forgetAgentKey() so reusing an agent_id after revocation doesn't
302
+ * link to the old chain.
303
+ */
304
+ private lastEventHash;
305
+ /**
306
+ * Per-agent emit promise. Each new emit awaits the previous one so
307
+ * the chain is constructed in order. Concurrent emits to the same
308
+ * agent are serialised (correct); concurrent emits to different
309
+ * agents remain parallel (fast).
310
+ */
311
+ private chainLocks;
80
312
  constructor(config: VorimConfig);
313
+ /**
314
+ * Register a previously-issued agent keypair so this SDK instance can
315
+ * auto-sign events for it on emit. Use this when restoring an agent across
316
+ * process restarts: read the agent's private_key from durable storage,
317
+ * call useAgentKey(agentId, privateKey), and emit() will sign automatically.
318
+ */
319
+ useAgentKey(agentId: string, privateKeyPem: string): void;
320
+ /**
321
+ * Forget an agent's signing key (e.g. after revocation). Subsequent emit()
322
+ * calls for that agent will pass through unsigned unless a key is provided
323
+ * inline. Also clears the per-agent chain state — re-using the same
324
+ * agent_id after revocation does NOT link to the old chain.
325
+ */
326
+ forgetAgentKey(agentId: string): void;
81
327
  /**
82
328
  * Ping the Vorim API to verify connectivity and API key validity.
83
329
  * Returns { status, timestamp } on success, throws VorimError on failure.
@@ -89,6 +335,11 @@ declare class VorimSDK {
89
335
  /**
90
336
  * Register a new agent with Vorim AI.
91
337
  * Returns the agent identity and a private key (shown once).
338
+ *
339
+ * Side effect: the returned private_key is cached in this SDK instance's
340
+ * in-memory keyring so subsequent emit() calls for this agent auto-sign.
341
+ * The keyring is process-local; the caller is responsible for persisting
342
+ * the private_key to durable storage if the agent should survive restarts.
92
343
  */
93
344
  register(input: AgentRegistrationInput): Promise<AgentRegistrationResult>;
94
345
  /**
@@ -143,16 +394,58 @@ declare class VorimSDK {
143
394
  revokePermission(agentId: string, scope: PermissionScope): Promise<any>;
144
395
  /**
145
396
  * Emit an audit event for an agent action.
397
+ *
398
+ * By default (autoSign=true on the SDK), the event is signed with the
399
+ * agent's private key before it leaves the process. Set options.sign=false
400
+ * to skip signing (e.g. for testing). If the SDK has no private key for the
401
+ * event's agent_id, the event is sent unsigned and a warning is not
402
+ * raised — callers that require signing should check event.signature on
403
+ * the returned event after a round-trip, or use a server-side enforcement
404
+ * flag (VORIM_VERIFY_AUDIT_SIGNATURES).
146
405
  */
147
- emit(event: AuditEventInput): Promise<{
406
+ emit(event: AuditEventInput, options?: {
407
+ sign?: boolean;
408
+ }): Promise<{
148
409
  ingested: number;
149
410
  }>;
150
411
  /**
151
- * Emit a batch of audit events (up to 1,000).
412
+ * Emit a batch of audit events (up to 1,000). Each event is signed
413
+ * independently using its agent_id to look up the signing key. See
414
+ * {@link VorimSDK.emit} for signing behaviour and opt-out.
152
415
  */
153
- emitBatch(events: AuditEventInput[]): Promise<{
416
+ emitBatch(events: AuditEventInput[], options?: {
417
+ sign?: boolean;
418
+ }): Promise<{
154
419
  ingested: number;
155
420
  }>;
421
+ /**
422
+ * Internal: prepare an event for transmission. Auto-signs if the SDK has a
423
+ * key for this agent and signing isn't explicitly opted out. Pre-existing
424
+ * signatures on the event are respected (caller-signed events are not
425
+ * re-signed). Per-agent failure is non-fatal: if signing throws, the event
426
+ * still sends unsigned so a single bad key doesn't break a batch.
427
+ *
428
+ * Canonical-form dispatch:
429
+ * - If the event already carries `canonical_form`, that wins (caller
430
+ * opted in/out for this specific event).
431
+ * - Otherwise the SDK-level `canonicalForm` (config) applies.
432
+ * - v0 signs the pipe-joined 6 fields. v1 signs the RFC 8785 JCS
433
+ * bytes over the full event minus signature and canonical_form,
434
+ * and the event goes on the wire with `canonical_form: "v1"`.
435
+ *
436
+ * Chain construction:
437
+ * - When chainEvents is on, the event gets `prev_event_hash` set to
438
+ * the SHA-256 of the previous event's canonical bytes for the
439
+ * same agent, set BEFORE the signature is computed (so v1
440
+ * signatures cover the chain link).
441
+ * - Chain ops are serialised per-agent via `chainLocks` so
442
+ * concurrent emits to the same agent build a deterministic chain.
443
+ */
444
+ private prepareEvent;
445
+ /** Serialise per-agent and apply chain hash + sign. */
446
+ private prepareEventChained;
447
+ /** Sign an event right now, no chain handling. */
448
+ private signEventNow;
156
449
  /**
157
450
  * Export a signed audit bundle for a date range.
158
451
  */
@@ -177,6 +470,9 @@ declare class VorimSDK {
177
470
  /**
178
471
  * Register an ephemeral agent with W3C did:key identity.
179
472
  * The agent auto-expires after the specified TTL.
473
+ *
474
+ * Side effect: the returned private_key is cached in this SDK instance's
475
+ * in-memory keyring (matching register()).
180
476
  */
181
477
  registerEphemeral(input: {
182
478
  capabilities: string[];
@@ -272,4 +568,4 @@ declare class VorimError extends Error {
272
568
  }
273
569
  declare function createVorim(config: VorimConfig): VorimSDK;
274
570
 
275
- export { type Agent, type AgentRegistrationInput, type AgentRegistrationResult, type AgentStatus, type AuditEventInput, type AuditEventType, type AuditResult, type PermissionCheckResult, type PermissionScope, type TrustRecord, type VorimConfig, VorimError, VorimSDK, createVorim as default };
571
+ export { type Agent, type AgentRegistrationInput, type AgentRegistrationResult, type AgentStatus, type AuditEventInput, type AuditEventType, type AuditResult, CANONICAL_TOOL_CATALOGUE_VERSION, type CatalogueTool, type PermissionCheckResult, type PermissionScope, type ReplayContext, type ReplayInputs, type TrustRecord, type VorimConfig, VorimError, VorimSDK, canonicalPayloadV0, canonicalPayloadV1, createVorim as default, hashPreviousEvent, hashSystemPrompt, hashTool, hashToolCatalogue, jcsCanonicalise, prepareReplayContext };
package/dist/index.js CHANGED
@@ -1,14 +1,147 @@
1
+ // src/replay.ts
2
+ var CANONICAL_TOOL_CATALOGUE_VERSION = "v1";
3
+ function jcsCanonicalise(value) {
4
+ if (value === null) return "null";
5
+ if (value === true) return "true";
6
+ if (value === false) return "false";
7
+ if (typeof value === "number") {
8
+ if (!Number.isFinite(value)) {
9
+ throw new Error("jcsCanonicalise: NaN and Infinity are not JCS-valid");
10
+ }
11
+ return value.toString();
12
+ }
13
+ if (typeof value === "string") {
14
+ return JSON.stringify(value);
15
+ }
16
+ if (Array.isArray(value)) {
17
+ return "[" + value.map(jcsCanonicalise).join(",") + "]";
18
+ }
19
+ if (typeof value === "object") {
20
+ const keys = Object.keys(value).sort();
21
+ const parts = keys.map((k) => {
22
+ return JSON.stringify(k) + ":" + jcsCanonicalise(value[k]);
23
+ });
24
+ return "{" + parts.join(",") + "}";
25
+ }
26
+ throw new Error(`jcsCanonicalise: unsupported value type: ${typeof value}`);
27
+ }
28
+ async function sha256Hex(input) {
29
+ const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
30
+ const subtle = globalThis.crypto?.subtle;
31
+ if (subtle) {
32
+ const buf = await subtle.digest("SHA-256", bytes);
33
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
34
+ }
35
+ const nodeCrypto = await import("crypto");
36
+ return nodeCrypto.createHash("sha256").update(bytes).digest("hex");
37
+ }
38
+ async function hashTool(tool) {
39
+ const normalised = {
40
+ name: tool.name,
41
+ description: tool.description ?? "",
42
+ schema: tool.schema ?? {}
43
+ };
44
+ const hex = await sha256Hex(jcsCanonicalise(normalised));
45
+ return `sha256:${hex}`;
46
+ }
47
+ async function hashToolCatalogue(tools) {
48
+ if (tools.length === 0) {
49
+ return `sha256:${await sha256Hex("[]")}`;
50
+ }
51
+ const perTool = await Promise.all(tools.map(hashTool));
52
+ perTool.sort();
53
+ const hex = await sha256Hex(perTool.join(""));
54
+ return `sha256:${hex}`;
55
+ }
56
+ async function hashSystemPrompt(prompt) {
57
+ const hex = await sha256Hex(prompt);
58
+ return `sha256:${hex}`;
59
+ }
60
+ async function hashPreviousEvent(canonicalBytes) {
61
+ const hex = await sha256Hex(canonicalBytes);
62
+ return `sha256:${hex}`;
63
+ }
64
+ async function prepareReplayContext(inputs) {
65
+ const ctx = {};
66
+ if (inputs.modelVersion) ctx.model_version = inputs.modelVersion;
67
+ if (inputs.tools) ctx.tool_catalogue_hash = await hashToolCatalogue(inputs.tools);
68
+ if (inputs.systemPrompt) ctx.system_prompt_hash = await hashSystemPrompt(inputs.systemPrompt);
69
+ return ctx;
70
+ }
71
+
1
72
  // src/index.ts
2
- var SDK_VERSION = "3.0.2";
73
+ var SDK_VERSION = true ? "3.2.0" : "0.0.0";
3
74
  var USER_AGENT = `vorim-sdk/${SDK_VERSION}`;
75
+ function canonicalPayloadV0(event) {
76
+ return [
77
+ event.event_type,
78
+ event.action,
79
+ event.resource ?? "",
80
+ event.input_hash ?? "",
81
+ event.output_hash ?? "",
82
+ event.result
83
+ ].join("|");
84
+ }
85
+ function canonicalPayloadV1(event) {
86
+ const { signature: _sig, canonical_form: _cf, ...rest } = event;
87
+ return jcsCanonicalise(rest);
88
+ }
4
89
  var VorimSDK = class {
5
90
  apiKey;
6
91
  baseUrl;
7
92
  timeout;
93
+ autoSign;
94
+ canonicalForm;
95
+ chainEvents;
96
+ /**
97
+ * In-memory keyring mapping agent_id -> PEM-encoded Ed25519 private key.
98
+ * Populated automatically by register() and registerEphemeral(), or
99
+ * explicitly via useAgentKey(). Lost on process restart by design; the
100
+ * caller is responsible for durable key storage.
101
+ */
102
+ agentKeys = /* @__PURE__ */ new Map();
103
+ /**
104
+ * Per-agent hash of the last emitted event's canonical bytes. Used to
105
+ * populate prev_event_hash on the next emit when chainEvents is on.
106
+ * Empty string ↔ no previous event (chain restart). Reset by
107
+ * forgetAgentKey() so reusing an agent_id after revocation doesn't
108
+ * link to the old chain.
109
+ */
110
+ lastEventHash = /* @__PURE__ */ new Map();
111
+ /**
112
+ * Per-agent emit promise. Each new emit awaits the previous one so
113
+ * the chain is constructed in order. Concurrent emits to the same
114
+ * agent are serialised (correct); concurrent emits to different
115
+ * agents remain parallel (fast).
116
+ */
117
+ chainLocks = /* @__PURE__ */ new Map();
8
118
  constructor(config) {
9
119
  this.apiKey = config.apiKey;
10
120
  this.baseUrl = (config.baseUrl || "https://api.vorim.ai").replace(/\/$/, "") + "/v1";
11
121
  this.timeout = config.timeout || 1e4;
122
+ this.autoSign = config.autoSign !== false;
123
+ this.canonicalForm = config.canonicalForm ?? "v0";
124
+ this.chainEvents = config.chainEvents ?? false;
125
+ }
126
+ /**
127
+ * Register a previously-issued agent keypair so this SDK instance can
128
+ * auto-sign events for it on emit. Use this when restoring an agent across
129
+ * process restarts: read the agent's private_key from durable storage,
130
+ * call useAgentKey(agentId, privateKey), and emit() will sign automatically.
131
+ */
132
+ useAgentKey(agentId, privateKeyPem) {
133
+ this.agentKeys.set(agentId, privateKeyPem);
134
+ }
135
+ /**
136
+ * Forget an agent's signing key (e.g. after revocation). Subsequent emit()
137
+ * calls for that agent will pass through unsigned unless a key is provided
138
+ * inline. Also clears the per-agent chain state — re-using the same
139
+ * agent_id after revocation does NOT link to the old chain.
140
+ */
141
+ forgetAgentKey(agentId) {
142
+ this.agentKeys.delete(agentId);
143
+ this.lastEventHash.delete(agentId);
144
+ this.chainLocks.delete(agentId);
12
145
  }
13
146
  // ─── Health Check ────────────────────────────────────────────────
14
147
  /**
@@ -27,9 +160,18 @@ var VorimSDK = class {
27
160
  /**
28
161
  * Register a new agent with Vorim AI.
29
162
  * Returns the agent identity and a private key (shown once).
163
+ *
164
+ * Side effect: the returned private_key is cached in this SDK instance's
165
+ * in-memory keyring so subsequent emit() calls for this agent auto-sign.
166
+ * The keyring is process-local; the caller is responsible for persisting
167
+ * the private_key to durable storage if the agent should survive restarts.
30
168
  */
31
169
  async register(input) {
32
- return this.post("/agents", input);
170
+ const result = await this.post("/agents", input);
171
+ if (result?.agent?.agent_id && result?.private_key) {
172
+ this.agentKeys.set(result.agent.agent_id, result.private_key);
173
+ }
174
+ return result;
33
175
  }
34
176
  /**
35
177
  * Verify an agent's identity via the public Trust API.
@@ -91,15 +233,100 @@ var VorimSDK = class {
91
233
  // ─── Audit ────────────────────────────────────────────────────────
92
234
  /**
93
235
  * Emit an audit event for an agent action.
236
+ *
237
+ * By default (autoSign=true on the SDK), the event is signed with the
238
+ * agent's private key before it leaves the process. Set options.sign=false
239
+ * to skip signing (e.g. for testing). If the SDK has no private key for the
240
+ * event's agent_id, the event is sent unsigned and a warning is not
241
+ * raised — callers that require signing should check event.signature on
242
+ * the returned event after a round-trip, or use a server-side enforcement
243
+ * flag (VORIM_VERIFY_AUDIT_SIGNATURES).
244
+ */
245
+ async emit(event, options) {
246
+ const prepared = await this.prepareEvent(event, options?.sign);
247
+ return this.post("/audit/events", { events: [prepared] });
248
+ }
249
+ /**
250
+ * Emit a batch of audit events (up to 1,000). Each event is signed
251
+ * independently using its agent_id to look up the signing key. See
252
+ * {@link VorimSDK.emit} for signing behaviour and opt-out.
94
253
  */
95
- async emit(event) {
96
- return this.post("/audit/events", { events: [event] });
254
+ async emitBatch(events, options) {
255
+ const prepared = await Promise.all(events.map((e) => this.prepareEvent(e, options?.sign)));
256
+ return this.post("/audit/events", { events: prepared });
97
257
  }
98
258
  /**
99
- * Emit a batch of audit events (up to 1,000).
259
+ * Internal: prepare an event for transmission. Auto-signs if the SDK has a
260
+ * key for this agent and signing isn't explicitly opted out. Pre-existing
261
+ * signatures on the event are respected (caller-signed events are not
262
+ * re-signed). Per-agent failure is non-fatal: if signing throws, the event
263
+ * still sends unsigned so a single bad key doesn't break a batch.
264
+ *
265
+ * Canonical-form dispatch:
266
+ * - If the event already carries `canonical_form`, that wins (caller
267
+ * opted in/out for this specific event).
268
+ * - Otherwise the SDK-level `canonicalForm` (config) applies.
269
+ * - v0 signs the pipe-joined 6 fields. v1 signs the RFC 8785 JCS
270
+ * bytes over the full event minus signature and canonical_form,
271
+ * and the event goes on the wire with `canonical_form: "v1"`.
272
+ *
273
+ * Chain construction:
274
+ * - When chainEvents is on, the event gets `prev_event_hash` set to
275
+ * the SHA-256 of the previous event's canonical bytes for the
276
+ * same agent, set BEFORE the signature is computed (so v1
277
+ * signatures cover the chain link).
278
+ * - Chain ops are serialised per-agent via `chainLocks` so
279
+ * concurrent emits to the same agent build a deterministic chain.
100
280
  */
101
- async emitBatch(events) {
102
- return this.post("/audit/events", { events });
281
+ async prepareEvent(event, signOverride) {
282
+ const shouldSign = signOverride ?? this.autoSign;
283
+ if (!shouldSign && !this.chainEvents) return event;
284
+ if (this.chainEvents) {
285
+ return this.prepareEventChained(event, shouldSign);
286
+ }
287
+ if (!shouldSign) return event;
288
+ if (event.signature) return event;
289
+ return this.signEventNow(event, shouldSign);
290
+ }
291
+ /** Serialise per-agent and apply chain hash + sign. */
292
+ async prepareEventChained(event, shouldSign) {
293
+ const agentId = event.agent_id;
294
+ const prior = this.chainLocks.get(agentId) ?? Promise.resolve();
295
+ let release;
296
+ const next = new Promise((r) => {
297
+ release = r;
298
+ });
299
+ this.chainLocks.set(agentId, prior.then(() => next));
300
+ await prior;
301
+ try {
302
+ const prev = this.lastEventHash.get(agentId);
303
+ const eventWithPrev = prev ? { ...event, prev_event_hash: prev } : event;
304
+ const prepared = shouldSign && !eventWithPrev.signature ? await this.signEventNow(eventWithPrev, shouldSign) : eventWithPrev;
305
+ const wireForm = prepared.canonical_form ?? "v0";
306
+ const bytes = wireForm === "v1" ? canonicalPayloadV1(prepared) : canonicalPayloadV0(prepared);
307
+ try {
308
+ this.lastEventHash.set(agentId, await hashPreviousEvent(bytes));
309
+ } catch {
310
+ this.lastEventHash.delete(agentId);
311
+ }
312
+ return prepared;
313
+ } finally {
314
+ release();
315
+ }
316
+ }
317
+ /** Sign an event right now, no chain handling. */
318
+ async signEventNow(event, _shouldSign) {
319
+ const key = this.agentKeys.get(event.agent_id);
320
+ if (!key) return event;
321
+ const form = event.canonical_form ?? this.canonicalForm;
322
+ try {
323
+ const withForm = form === "v0" ? event : { ...event, canonical_form: "v1" };
324
+ const payload = form === "v1" ? canonicalPayloadV1(withForm) : canonicalPayloadV0(withForm);
325
+ const signature = await this.sign(payload, key);
326
+ return { ...withForm, signature };
327
+ } catch {
328
+ return event;
329
+ }
103
330
  }
104
331
  /**
105
332
  * Export a signed audit bundle for a date range.
@@ -130,9 +357,17 @@ var VorimSDK = class {
130
357
  /**
131
358
  * Register an ephemeral agent with W3C did:key identity.
132
359
  * The agent auto-expires after the specified TTL.
360
+ *
361
+ * Side effect: the returned private_key is cached in this SDK instance's
362
+ * in-memory keyring (matching register()).
133
363
  */
134
364
  async registerEphemeral(input) {
135
- return this.post("/agents/ephemeral", input);
365
+ const result = await this.post("/agents/ephemeral", input);
366
+ const agentId = result?.agent?.agent_id ?? result?.did_key;
367
+ if (agentId && result?.private_key) {
368
+ this.agentKeys.set(agentId, result.private_key);
369
+ }
370
+ return result;
136
371
  }
137
372
  // ─── Credential Delegation ──────────────────────────────────────────
138
373
  /**
@@ -283,13 +518,25 @@ var VorimError = class extends Error {
283
518
  this.details = details;
284
519
  this.name = "VorimError";
285
520
  }
521
+ status;
522
+ code;
523
+ details;
286
524
  };
287
525
  function createVorim(config) {
288
526
  return new VorimSDK(config);
289
527
  }
290
528
  export {
529
+ CANONICAL_TOOL_CATALOGUE_VERSION,
291
530
  VorimError,
292
531
  VorimSDK,
293
- createVorim as default
532
+ canonicalPayloadV0,
533
+ canonicalPayloadV1,
534
+ createVorim as default,
535
+ hashPreviousEvent,
536
+ hashSystemPrompt,
537
+ hashTool,
538
+ hashToolCatalogue,
539
+ jcsCanonicalise,
540
+ prepareReplayContext
294
541
  };
295
542
  //# sourceMappingURL=index.js.map