@vorim/sdk 3.2.0 → 3.3.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/dist/index.d.cts CHANGED
@@ -75,6 +75,76 @@ interface AuditEventInput {
75
75
  * is `"v1"`; you can also pass it on a per-event basis.
76
76
  */
77
77
  canonical_form?: 'v0' | 'v1';
78
+ /**
79
+ * Per-event delegation context (VAIP -02 § 6). Populated by the
80
+ * server when the emitting agent is acting via a delegation chain;
81
+ * callers may also set these manually for events recorded outside
82
+ * the standard delegation flow. v1 signatures cover these fields.
83
+ */
84
+ on_behalf_of?: string;
85
+ delegator_agent_id?: string;
86
+ delegation_chain_id?: string;
87
+ delegation_depth?: number;
88
+ }
89
+ /**
90
+ * Claims structure for one VAIP -02 § 5 delegation link.
91
+ *
92
+ * Each delegation step in a chain is one of these objects, RFC 8785 JCS
93
+ * canonicalised and Ed25519-signed by the delegator. Mirrors the same
94
+ * type in `@vorim/shared-types`; duplicated here so the SDK ships zero
95
+ * runtime dependencies. Byte-equivalence with the shared definition is
96
+ * enforced by the cross-language parity script.
97
+ */
98
+ interface DelegationLinkClaims {
99
+ v: 0;
100
+ type: 'vaip-delegation-link';
101
+ chain_id: string;
102
+ depth: number;
103
+ delegator: string;
104
+ delegate: string;
105
+ scopes: string[];
106
+ max_chain_depth: number;
107
+ valid_from: string;
108
+ valid_until: string | null;
109
+ parent_link_hash: string | null;
110
+ }
111
+ /**
112
+ * One step in an agent-to-agent identity delegation chain (VAIP -02 § 5).
113
+ *
114
+ * Returned by {@link VorimSDK.delegateToAgent} and the listing endpoints.
115
+ */
116
+ interface AgentDelegationRecord {
117
+ /** Internal row id. */
118
+ id: string;
119
+ /** Public chain identifier (format `chain_<32hex>`). Stamped on
120
+ * every audit event in this chain. */
121
+ publicChainId: string;
122
+ /** Organisation id. */
123
+ orgId: string;
124
+ /** Internal DB id of the delegator agent. */
125
+ delegatorAgentId: string;
126
+ /** Internal DB id of the delegate agent. */
127
+ delegateAgentId: string;
128
+ /** Public agent_id of the delegator (e.g. agid_acme_abc). */
129
+ delegatorAgentPublicId: string;
130
+ /** Public agent_id of the delegate. */
131
+ delegateAgentPublicId: string;
132
+ /** Scopes the delegate may exercise. */
133
+ scopesDelegated: string[];
134
+ /** How many further hops the delegate is permitted. */
135
+ maxChainDepth: number;
136
+ /** This step's depth from the root (root delegator step = 1). */
137
+ currentDepth: number;
138
+ /** Active / suspended / revoked / expired. */
139
+ status: 'active' | 'suspended' | 'revoked' | 'expired';
140
+ /** Parent delegation row (null for the root of an identity chain). */
141
+ parentDelegationId: string | null;
142
+ /** Public agent_id of the root principal of this chain. */
143
+ onBehalfOf: string;
144
+ /** ISO8601 creation timestamp. */
145
+ createdAt: string;
146
+ /** Optional ISO8601 expiry. */
147
+ validUntil: string | null;
78
148
  }
79
149
  interface TrustRecord {
80
150
  agent_id: string;
@@ -302,6 +372,14 @@ declare class VorimSDK {
302
372
  * link to the old chain.
303
373
  */
304
374
  private lastEventHash;
375
+ /**
376
+ * Per-agent monotonic counter incremented by `forgetAgentKey`. An
377
+ * emit that started before `forgetAgentKey` ran will read a stale
378
+ * epoch and refuse to repopulate the chain tail, preventing the
379
+ * revoked agent from inheriting a new lastEventHash from the
380
+ * in-flight emit that the lock was holding.
381
+ */
382
+ private agentForgetEpoch;
305
383
  /**
306
384
  * Per-agent emit promise. Each new emit awaits the previous one so
307
385
  * the chain is constructed in order. Concurrent emits to the same
@@ -392,6 +470,97 @@ declare class VorimSDK {
392
470
  * Revoke a specific permission scope from an agent.
393
471
  */
394
472
  revokePermission(agentId: string, scope: PermissionScope): Promise<any>;
473
+ /**
474
+ * Delegate the right to act on this agent's behalf to another agent
475
+ * in the same org, with a strict subset of this agent's scopes.
476
+ *
477
+ * Multi-hop chains are supported up to depth 32. Scope subset is
478
+ * enforced at every hop. Audit events emitted by the delegate carry
479
+ * the chain context (`on_behalf_of`, `delegator_agent_id`,
480
+ * `delegation_chain_id`, `delegation_depth`) so a verifier walking
481
+ * the bundle can reconstruct the full chain.
482
+ *
483
+ * @example
484
+ * ```ts
485
+ * const chain = await vorim.delegateToAgent('agid_parent_abc', {
486
+ * delegate_agent_id: 'agid_child_xyz',
487
+ * scopes_delegated: ['agent:read', 'agent:execute'],
488
+ * max_chain_depth: 1, // delegate may make ONE further hop
489
+ * valid_until: '2026-12-31T00:00:00Z',
490
+ * });
491
+ * console.log(chain.public_chain_id); // chain_<hex>
492
+ * ```
493
+ */
494
+ delegateToAgent(delegatorAgentId: string, input: {
495
+ delegate_agent_id: string;
496
+ scopes_delegated: PermissionScope[];
497
+ max_chain_depth?: number;
498
+ valid_until?: string;
499
+ /**
500
+ * Optional Ed25519-signed delegation link (VAIP -02 § 5). When
501
+ * provided, the server verifies the signature against the
502
+ * delegator's stored public key, persists alongside the chain
503
+ * row, and exports it in bundle delegation_tokens so the chain
504
+ * is offline-verifiable by `@vorim/verify`. Compute via
505
+ * {@link signDelegationLink} or set manually if signing
506
+ * elsewhere.
507
+ */
508
+ signed_link?: {
509
+ claims: DelegationLinkClaims;
510
+ signature: string;
511
+ };
512
+ }): Promise<AgentDelegationRecord>;
513
+ /**
514
+ * Sign a delegation link with the delegator's private key. Returns
515
+ * the signed link in the shape accepted by `delegateToAgent`'s
516
+ * `signed_link` field.
517
+ *
518
+ * The signature is Ed25519 over the RFC 8785 JCS-canonical bytes of
519
+ * the claims object. Same algorithm the server and `@vorim/verify`
520
+ * use for verification.
521
+ *
522
+ * Use this when the delegator's private key is in this SDK's
523
+ * keyring (i.e. set via `register()` or `useAgentKey()`).
524
+ *
525
+ * @example
526
+ * ```ts
527
+ * const signed = await vorim.signDelegationLink({
528
+ * v: 0,
529
+ * type: 'vaip-delegation-link',
530
+ * chain_id: 'chain_abc', // server returns this on first delegation
531
+ * depth: 1,
532
+ * delegator: 'agid_parent',
533
+ * delegate: 'agid_child',
534
+ * scopes: ['agent:read', 'agent:execute'],
535
+ * max_chain_depth: 1,
536
+ * valid_from: new Date().toISOString(),
537
+ * valid_until: null,
538
+ * parent_link_hash: null,
539
+ * });
540
+ * ```
541
+ */
542
+ signDelegationLink(claims: DelegationLinkClaims): Promise<{
543
+ claims: DelegationLinkClaims;
544
+ signature: string;
545
+ }>;
546
+ /**
547
+ * List active identity-chain entries this agent participates in
548
+ * (either as delegator or delegate). Useful for showing the current
549
+ * delegation graph in an admin UI.
550
+ */
551
+ listAgentDelegations(agentId: string): Promise<AgentDelegationRecord[]>;
552
+ /**
553
+ * Walk a delegation chain by its public id. Returns the full chain
554
+ * in depth order.
555
+ */
556
+ getDelegationChain(agentId: string, chainId: string): Promise<AgentDelegationRecord[]>;
557
+ /**
558
+ * Revoke this agent's delegation in a given chain. Cascades to all
559
+ * descendants beneath this agent's depth in the chain.
560
+ */
561
+ revokeAgentDelegation(agentId: string, chainId: string): Promise<{
562
+ revoked: number;
563
+ }>;
395
564
  /**
396
565
  * Emit an audit event for an agent action.
397
566
  *
@@ -412,12 +581,18 @@ declare class VorimSDK {
412
581
  * Emit a batch of audit events (up to 1,000). Each event is signed
413
582
  * independently using its agent_id to look up the signing key. See
414
583
  * {@link VorimSDK.emit} for signing behaviour and opt-out.
584
+ *
585
+ * When the server returns `ingested < events.length` (e.g. one event
586
+ * referenced an unknown agent_id), a `console.warn` is emitted so
587
+ * operators don't silently lose audit rows. The result is returned
588
+ * unchanged so callers can inspect / act on it.
415
589
  */
416
590
  emitBatch(events: AuditEventInput[], options?: {
417
591
  sign?: boolean;
418
592
  }): Promise<{
419
593
  ingested: number;
420
594
  }>;
595
+ private warnOnPartialIngest;
421
596
  /**
422
597
  * Internal: prepare an event for transmission. Auto-signs if the SDK has a
423
598
  * key for this agent and signing isn't explicitly opted out. Pre-existing
@@ -444,7 +619,15 @@ declare class VorimSDK {
444
619
  private prepareEvent;
445
620
  /** Serialise per-agent and apply chain hash + sign. */
446
621
  private prepareEventChained;
447
- /** Sign an event right now, no chain handling. */
622
+ /** Sign an event right now, no chain handling.
623
+ *
624
+ * On signing failure (malformed key, Web Crypto unavailable, etc.)
625
+ * the event ships with its requested `canonical_form` preserved and
626
+ * a one-line warning is logged via `console.warn`. The previous
627
+ * behaviour silently dropped both the signature AND the
628
+ * canonical_form marker, which caused the event to be classified
629
+ * server-side as a v0 `signature_missing` instead of a v1 attempt
630
+ * that failed — making the breakage invisible to operators. */
448
631
  private signEventNow;
449
632
  /**
450
633
  * Export a signed audit bundle for a date range.
@@ -568,4 +751,4 @@ declare class VorimError extends Error {
568
751
  }
569
752
  declare function createVorim(config: VorimConfig): VorimSDK;
570
753
 
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 };
754
+ export { type Agent, type AgentDelegationRecord, type AgentRegistrationInput, type AgentRegistrationResult, type AgentStatus, type AuditEventInput, type AuditEventType, type AuditResult, CANONICAL_TOOL_CATALOGUE_VERSION, type CatalogueTool, type DelegationLinkClaims, 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.d.ts CHANGED
@@ -75,6 +75,76 @@ interface AuditEventInput {
75
75
  * is `"v1"`; you can also pass it on a per-event basis.
76
76
  */
77
77
  canonical_form?: 'v0' | 'v1';
78
+ /**
79
+ * Per-event delegation context (VAIP -02 § 6). Populated by the
80
+ * server when the emitting agent is acting via a delegation chain;
81
+ * callers may also set these manually for events recorded outside
82
+ * the standard delegation flow. v1 signatures cover these fields.
83
+ */
84
+ on_behalf_of?: string;
85
+ delegator_agent_id?: string;
86
+ delegation_chain_id?: string;
87
+ delegation_depth?: number;
88
+ }
89
+ /**
90
+ * Claims structure for one VAIP -02 § 5 delegation link.
91
+ *
92
+ * Each delegation step in a chain is one of these objects, RFC 8785 JCS
93
+ * canonicalised and Ed25519-signed by the delegator. Mirrors the same
94
+ * type in `@vorim/shared-types`; duplicated here so the SDK ships zero
95
+ * runtime dependencies. Byte-equivalence with the shared definition is
96
+ * enforced by the cross-language parity script.
97
+ */
98
+ interface DelegationLinkClaims {
99
+ v: 0;
100
+ type: 'vaip-delegation-link';
101
+ chain_id: string;
102
+ depth: number;
103
+ delegator: string;
104
+ delegate: string;
105
+ scopes: string[];
106
+ max_chain_depth: number;
107
+ valid_from: string;
108
+ valid_until: string | null;
109
+ parent_link_hash: string | null;
110
+ }
111
+ /**
112
+ * One step in an agent-to-agent identity delegation chain (VAIP -02 § 5).
113
+ *
114
+ * Returned by {@link VorimSDK.delegateToAgent} and the listing endpoints.
115
+ */
116
+ interface AgentDelegationRecord {
117
+ /** Internal row id. */
118
+ id: string;
119
+ /** Public chain identifier (format `chain_<32hex>`). Stamped on
120
+ * every audit event in this chain. */
121
+ publicChainId: string;
122
+ /** Organisation id. */
123
+ orgId: string;
124
+ /** Internal DB id of the delegator agent. */
125
+ delegatorAgentId: string;
126
+ /** Internal DB id of the delegate agent. */
127
+ delegateAgentId: string;
128
+ /** Public agent_id of the delegator (e.g. agid_acme_abc). */
129
+ delegatorAgentPublicId: string;
130
+ /** Public agent_id of the delegate. */
131
+ delegateAgentPublicId: string;
132
+ /** Scopes the delegate may exercise. */
133
+ scopesDelegated: string[];
134
+ /** How many further hops the delegate is permitted. */
135
+ maxChainDepth: number;
136
+ /** This step's depth from the root (root delegator step = 1). */
137
+ currentDepth: number;
138
+ /** Active / suspended / revoked / expired. */
139
+ status: 'active' | 'suspended' | 'revoked' | 'expired';
140
+ /** Parent delegation row (null for the root of an identity chain). */
141
+ parentDelegationId: string | null;
142
+ /** Public agent_id of the root principal of this chain. */
143
+ onBehalfOf: string;
144
+ /** ISO8601 creation timestamp. */
145
+ createdAt: string;
146
+ /** Optional ISO8601 expiry. */
147
+ validUntil: string | null;
78
148
  }
79
149
  interface TrustRecord {
80
150
  agent_id: string;
@@ -302,6 +372,14 @@ declare class VorimSDK {
302
372
  * link to the old chain.
303
373
  */
304
374
  private lastEventHash;
375
+ /**
376
+ * Per-agent monotonic counter incremented by `forgetAgentKey`. An
377
+ * emit that started before `forgetAgentKey` ran will read a stale
378
+ * epoch and refuse to repopulate the chain tail, preventing the
379
+ * revoked agent from inheriting a new lastEventHash from the
380
+ * in-flight emit that the lock was holding.
381
+ */
382
+ private agentForgetEpoch;
305
383
  /**
306
384
  * Per-agent emit promise. Each new emit awaits the previous one so
307
385
  * the chain is constructed in order. Concurrent emits to the same
@@ -392,6 +470,97 @@ declare class VorimSDK {
392
470
  * Revoke a specific permission scope from an agent.
393
471
  */
394
472
  revokePermission(agentId: string, scope: PermissionScope): Promise<any>;
473
+ /**
474
+ * Delegate the right to act on this agent's behalf to another agent
475
+ * in the same org, with a strict subset of this agent's scopes.
476
+ *
477
+ * Multi-hop chains are supported up to depth 32. Scope subset is
478
+ * enforced at every hop. Audit events emitted by the delegate carry
479
+ * the chain context (`on_behalf_of`, `delegator_agent_id`,
480
+ * `delegation_chain_id`, `delegation_depth`) so a verifier walking
481
+ * the bundle can reconstruct the full chain.
482
+ *
483
+ * @example
484
+ * ```ts
485
+ * const chain = await vorim.delegateToAgent('agid_parent_abc', {
486
+ * delegate_agent_id: 'agid_child_xyz',
487
+ * scopes_delegated: ['agent:read', 'agent:execute'],
488
+ * max_chain_depth: 1, // delegate may make ONE further hop
489
+ * valid_until: '2026-12-31T00:00:00Z',
490
+ * });
491
+ * console.log(chain.public_chain_id); // chain_<hex>
492
+ * ```
493
+ */
494
+ delegateToAgent(delegatorAgentId: string, input: {
495
+ delegate_agent_id: string;
496
+ scopes_delegated: PermissionScope[];
497
+ max_chain_depth?: number;
498
+ valid_until?: string;
499
+ /**
500
+ * Optional Ed25519-signed delegation link (VAIP -02 § 5). When
501
+ * provided, the server verifies the signature against the
502
+ * delegator's stored public key, persists alongside the chain
503
+ * row, and exports it in bundle delegation_tokens so the chain
504
+ * is offline-verifiable by `@vorim/verify`. Compute via
505
+ * {@link signDelegationLink} or set manually if signing
506
+ * elsewhere.
507
+ */
508
+ signed_link?: {
509
+ claims: DelegationLinkClaims;
510
+ signature: string;
511
+ };
512
+ }): Promise<AgentDelegationRecord>;
513
+ /**
514
+ * Sign a delegation link with the delegator's private key. Returns
515
+ * the signed link in the shape accepted by `delegateToAgent`'s
516
+ * `signed_link` field.
517
+ *
518
+ * The signature is Ed25519 over the RFC 8785 JCS-canonical bytes of
519
+ * the claims object. Same algorithm the server and `@vorim/verify`
520
+ * use for verification.
521
+ *
522
+ * Use this when the delegator's private key is in this SDK's
523
+ * keyring (i.e. set via `register()` or `useAgentKey()`).
524
+ *
525
+ * @example
526
+ * ```ts
527
+ * const signed = await vorim.signDelegationLink({
528
+ * v: 0,
529
+ * type: 'vaip-delegation-link',
530
+ * chain_id: 'chain_abc', // server returns this on first delegation
531
+ * depth: 1,
532
+ * delegator: 'agid_parent',
533
+ * delegate: 'agid_child',
534
+ * scopes: ['agent:read', 'agent:execute'],
535
+ * max_chain_depth: 1,
536
+ * valid_from: new Date().toISOString(),
537
+ * valid_until: null,
538
+ * parent_link_hash: null,
539
+ * });
540
+ * ```
541
+ */
542
+ signDelegationLink(claims: DelegationLinkClaims): Promise<{
543
+ claims: DelegationLinkClaims;
544
+ signature: string;
545
+ }>;
546
+ /**
547
+ * List active identity-chain entries this agent participates in
548
+ * (either as delegator or delegate). Useful for showing the current
549
+ * delegation graph in an admin UI.
550
+ */
551
+ listAgentDelegations(agentId: string): Promise<AgentDelegationRecord[]>;
552
+ /**
553
+ * Walk a delegation chain by its public id. Returns the full chain
554
+ * in depth order.
555
+ */
556
+ getDelegationChain(agentId: string, chainId: string): Promise<AgentDelegationRecord[]>;
557
+ /**
558
+ * Revoke this agent's delegation in a given chain. Cascades to all
559
+ * descendants beneath this agent's depth in the chain.
560
+ */
561
+ revokeAgentDelegation(agentId: string, chainId: string): Promise<{
562
+ revoked: number;
563
+ }>;
395
564
  /**
396
565
  * Emit an audit event for an agent action.
397
566
  *
@@ -412,12 +581,18 @@ declare class VorimSDK {
412
581
  * Emit a batch of audit events (up to 1,000). Each event is signed
413
582
  * independently using its agent_id to look up the signing key. See
414
583
  * {@link VorimSDK.emit} for signing behaviour and opt-out.
584
+ *
585
+ * When the server returns `ingested < events.length` (e.g. one event
586
+ * referenced an unknown agent_id), a `console.warn` is emitted so
587
+ * operators don't silently lose audit rows. The result is returned
588
+ * unchanged so callers can inspect / act on it.
415
589
  */
416
590
  emitBatch(events: AuditEventInput[], options?: {
417
591
  sign?: boolean;
418
592
  }): Promise<{
419
593
  ingested: number;
420
594
  }>;
595
+ private warnOnPartialIngest;
421
596
  /**
422
597
  * Internal: prepare an event for transmission. Auto-signs if the SDK has a
423
598
  * key for this agent and signing isn't explicitly opted out. Pre-existing
@@ -444,7 +619,15 @@ declare class VorimSDK {
444
619
  private prepareEvent;
445
620
  /** Serialise per-agent and apply chain hash + sign. */
446
621
  private prepareEventChained;
447
- /** Sign an event right now, no chain handling. */
622
+ /** Sign an event right now, no chain handling.
623
+ *
624
+ * On signing failure (malformed key, Web Crypto unavailable, etc.)
625
+ * the event ships with its requested `canonical_form` preserved and
626
+ * a one-line warning is logged via `console.warn`. The previous
627
+ * behaviour silently dropped both the signature AND the
628
+ * canonical_form marker, which caused the event to be classified
629
+ * server-side as a v0 `signature_missing` instead of a v1 attempt
630
+ * that failed — making the breakage invisible to operators. */
448
631
  private signEventNow;
449
632
  /**
450
633
  * Export a signed audit bundle for a date range.
@@ -568,4 +751,4 @@ declare class VorimError extends Error {
568
751
  }
569
752
  declare function createVorim(config: VorimConfig): VorimSDK;
570
753
 
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 };
754
+ export { type Agent, type AgentDelegationRecord, type AgentRegistrationInput, type AgentRegistrationResult, type AgentStatus, type AuditEventInput, type AuditEventType, type AuditResult, CANONICAL_TOOL_CATALOGUE_VERSION, type CatalogueTool, type DelegationLinkClaims, 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
@@ -70,7 +70,7 @@ async function prepareReplayContext(inputs) {
70
70
  }
71
71
 
72
72
  // src/index.ts
73
- var SDK_VERSION = true ? "3.2.0" : "0.0.0";
73
+ var SDK_VERSION = true ? "3.3.1" : "0.0.0";
74
74
  var USER_AGENT = `vorim-sdk/${SDK_VERSION}`;
75
75
  function canonicalPayloadV0(event) {
76
76
  return [
@@ -108,6 +108,14 @@ var VorimSDK = class {
108
108
  * link to the old chain.
109
109
  */
110
110
  lastEventHash = /* @__PURE__ */ new Map();
111
+ /**
112
+ * Per-agent monotonic counter incremented by `forgetAgentKey`. An
113
+ * emit that started before `forgetAgentKey` ran will read a stale
114
+ * epoch and refuse to repopulate the chain tail, preventing the
115
+ * revoked agent from inheriting a new lastEventHash from the
116
+ * in-flight emit that the lock was holding.
117
+ */
118
+ agentForgetEpoch = /* @__PURE__ */ new Map();
111
119
  /**
112
120
  * Per-agent emit promise. Each new emit awaits the previous one so
113
121
  * the chain is constructed in order. Concurrent emits to the same
@@ -142,6 +150,7 @@ var VorimSDK = class {
142
150
  this.agentKeys.delete(agentId);
143
151
  this.lastEventHash.delete(agentId);
144
152
  this.chainLocks.delete(agentId);
153
+ this.agentForgetEpoch.set(agentId, (this.agentForgetEpoch.get(agentId) ?? 0) + 1);
145
154
  }
146
155
  // ─── Health Check ────────────────────────────────────────────────
147
156
  /**
@@ -230,6 +239,95 @@ var VorimSDK = class {
230
239
  async revokePermission(agentId, scope) {
231
240
  return this.delete(`/agents/${agentId}/permissions/${scope}`);
232
241
  }
242
+ // ─── Agent-to-Agent Identity Delegation (VAIP -02 § 5) ──────────────
243
+ /**
244
+ * Delegate the right to act on this agent's behalf to another agent
245
+ * in the same org, with a strict subset of this agent's scopes.
246
+ *
247
+ * Multi-hop chains are supported up to depth 32. Scope subset is
248
+ * enforced at every hop. Audit events emitted by the delegate carry
249
+ * the chain context (`on_behalf_of`, `delegator_agent_id`,
250
+ * `delegation_chain_id`, `delegation_depth`) so a verifier walking
251
+ * the bundle can reconstruct the full chain.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * const chain = await vorim.delegateToAgent('agid_parent_abc', {
256
+ * delegate_agent_id: 'agid_child_xyz',
257
+ * scopes_delegated: ['agent:read', 'agent:execute'],
258
+ * max_chain_depth: 1, // delegate may make ONE further hop
259
+ * valid_until: '2026-12-31T00:00:00Z',
260
+ * });
261
+ * console.log(chain.public_chain_id); // chain_<hex>
262
+ * ```
263
+ */
264
+ async delegateToAgent(delegatorAgentId, input) {
265
+ return this.post(`/agents/${delegatorAgentId}/delegations`, input);
266
+ }
267
+ /**
268
+ * Sign a delegation link with the delegator's private key. Returns
269
+ * the signed link in the shape accepted by `delegateToAgent`'s
270
+ * `signed_link` field.
271
+ *
272
+ * The signature is Ed25519 over the RFC 8785 JCS-canonical bytes of
273
+ * the claims object. Same algorithm the server and `@vorim/verify`
274
+ * use for verification.
275
+ *
276
+ * Use this when the delegator's private key is in this SDK's
277
+ * keyring (i.e. set via `register()` or `useAgentKey()`).
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * const signed = await vorim.signDelegationLink({
282
+ * v: 0,
283
+ * type: 'vaip-delegation-link',
284
+ * chain_id: 'chain_abc', // server returns this on first delegation
285
+ * depth: 1,
286
+ * delegator: 'agid_parent',
287
+ * delegate: 'agid_child',
288
+ * scopes: ['agent:read', 'agent:execute'],
289
+ * max_chain_depth: 1,
290
+ * valid_from: new Date().toISOString(),
291
+ * valid_until: null,
292
+ * parent_link_hash: null,
293
+ * });
294
+ * ```
295
+ */
296
+ async signDelegationLink(claims) {
297
+ const key = this.agentKeys.get(claims.delegator);
298
+ if (!key) {
299
+ throw new VorimError(
300
+ 400,
301
+ "NO_AGENT_KEY",
302
+ `SDK has no private key for delegator ${claims.delegator}`
303
+ );
304
+ }
305
+ const bytes = jcsCanonicalise(claims);
306
+ const signature = await this.sign(bytes, key);
307
+ return { claims, signature };
308
+ }
309
+ /**
310
+ * List active identity-chain entries this agent participates in
311
+ * (either as delegator or delegate). Useful for showing the current
312
+ * delegation graph in an admin UI.
313
+ */
314
+ async listAgentDelegations(agentId) {
315
+ return this.get(`/agents/${agentId}/delegations`);
316
+ }
317
+ /**
318
+ * Walk a delegation chain by its public id. Returns the full chain
319
+ * in depth order.
320
+ */
321
+ async getDelegationChain(agentId, chainId) {
322
+ return this.get(`/agents/${agentId}/delegation-chain/${chainId}`);
323
+ }
324
+ /**
325
+ * Revoke this agent's delegation in a given chain. Cascades to all
326
+ * descendants beneath this agent's depth in the chain.
327
+ */
328
+ async revokeAgentDelegation(agentId, chainId) {
329
+ return this.delete(`/agents/${agentId}/delegations/${chainId}`);
330
+ }
233
331
  // ─── Audit ────────────────────────────────────────────────────────
234
332
  /**
235
333
  * Emit an audit event for an agent action.
@@ -244,16 +342,35 @@ var VorimSDK = class {
244
342
  */
245
343
  async emit(event, options) {
246
344
  const prepared = await this.prepareEvent(event, options?.sign);
247
- return this.post("/audit/events", { events: [prepared] });
345
+ const result = await this.post("/audit/events", { events: [prepared] });
346
+ this.warnOnPartialIngest(1, result?.ingested ?? 0);
347
+ return result;
248
348
  }
249
349
  /**
250
350
  * Emit a batch of audit events (up to 1,000). Each event is signed
251
351
  * independently using its agent_id to look up the signing key. See
252
352
  * {@link VorimSDK.emit} for signing behaviour and opt-out.
353
+ *
354
+ * When the server returns `ingested < events.length` (e.g. one event
355
+ * referenced an unknown agent_id), a `console.warn` is emitted so
356
+ * operators don't silently lose audit rows. The result is returned
357
+ * unchanged so callers can inspect / act on it.
253
358
  */
254
359
  async emitBatch(events, options) {
255
360
  const prepared = await Promise.all(events.map((e) => this.prepareEvent(e, options?.sign)));
256
- return this.post("/audit/events", { events: prepared });
361
+ const result = await this.post("/audit/events", { events: prepared });
362
+ this.warnOnPartialIngest(events.length, result?.ingested ?? 0);
363
+ return result;
364
+ }
365
+ warnOnPartialIngest(submitted, ingested) {
366
+ if (ingested < submitted) {
367
+ try {
368
+ console.warn(
369
+ `[@vorim/sdk] partial ingest: server stored ${ingested}/${submitted} events. Common causes: unknown agent_id (cross-org or typo), invalid signature when strict verification is enabled.`
370
+ );
371
+ } catch {
372
+ }
373
+ }
257
374
  }
258
375
  /**
259
376
  * Internal: prepare an event for transmission. Auto-signs if the SDK has a
@@ -298,6 +415,7 @@ var VorimSDK = class {
298
415
  });
299
416
  this.chainLocks.set(agentId, prior.then(() => next));
300
417
  await prior;
418
+ const startEpoch = this.agentForgetEpoch.get(agentId) ?? 0;
301
419
  try {
302
420
  const prev = this.lastEventHash.get(agentId);
303
421
  const eventWithPrev = prev ? { ...event, prev_event_hash: prev } : event;
@@ -305,7 +423,10 @@ var VorimSDK = class {
305
423
  const wireForm = prepared.canonical_form ?? "v0";
306
424
  const bytes = wireForm === "v1" ? canonicalPayloadV1(prepared) : canonicalPayloadV0(prepared);
307
425
  try {
308
- this.lastEventHash.set(agentId, await hashPreviousEvent(bytes));
426
+ const tail = await hashPreviousEvent(bytes);
427
+ if ((this.agentForgetEpoch.get(agentId) ?? 0) === startEpoch) {
428
+ this.lastEventHash.set(agentId, tail);
429
+ }
309
430
  } catch {
310
431
  this.lastEventHash.delete(agentId);
311
432
  }
@@ -314,18 +435,32 @@ var VorimSDK = class {
314
435
  release();
315
436
  }
316
437
  }
317
- /** Sign an event right now, no chain handling. */
438
+ /** Sign an event right now, no chain handling.
439
+ *
440
+ * On signing failure (malformed key, Web Crypto unavailable, etc.)
441
+ * the event ships with its requested `canonical_form` preserved and
442
+ * a one-line warning is logged via `console.warn`. The previous
443
+ * behaviour silently dropped both the signature AND the
444
+ * canonical_form marker, which caused the event to be classified
445
+ * server-side as a v0 `signature_missing` instead of a v1 attempt
446
+ * that failed — making the breakage invisible to operators. */
318
447
  async signEventNow(event, _shouldSign) {
319
448
  const key = this.agentKeys.get(event.agent_id);
320
449
  if (!key) return event;
321
450
  const form = event.canonical_form ?? this.canonicalForm;
451
+ const withForm = form === "v0" ? event : { ...event, canonical_form: "v1" };
322
452
  try {
323
- const withForm = form === "v0" ? event : { ...event, canonical_form: "v1" };
324
453
  const payload = form === "v1" ? canonicalPayloadV1(withForm) : canonicalPayloadV0(withForm);
325
454
  const signature = await this.sign(payload, key);
326
455
  return { ...withForm, signature };
327
- } catch {
328
- return event;
456
+ } catch (err) {
457
+ try {
458
+ console.warn(
459
+ `[@vorim/sdk] signing failed for agent_id=${event.agent_id} form=${form}: ${err?.message ?? err}. Event will be emitted unsigned with canonical_form retained.`
460
+ );
461
+ } catch {
462
+ }
463
+ return withForm;
329
464
  }
330
465
  }
331
466
  /**