@orangecheck/agent-core 2.6.0 → 2.7.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.
package/dist/types.d.mts CHANGED
@@ -153,7 +153,7 @@ interface RevocationCanonicalInput {
153
153
  reason: string;
154
154
  signed_at: string;
155
155
  }
156
- type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
156
+ type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_STALE' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
157
157
  interface VerifyOk<T> {
158
158
  ok: true;
159
159
  envelope: T;
@@ -187,6 +187,14 @@ interface VerifyActionOkExtra {
187
187
  blockHash: string;
188
188
  verified: true;
189
189
  };
190
+ asOf: {
191
+ basis: 'now';
192
+ now: string;
193
+ } | {
194
+ basis: 'signed_at';
195
+ signedAt: string;
196
+ anchored: boolean;
197
+ };
190
198
  }
191
199
  type VerifyActionResult = (VerifyOk<ActionEnvelope> & VerifyActionOkExtra) | VerifyErr;
192
200
 
package/dist/types.d.ts CHANGED
@@ -153,7 +153,7 @@ interface RevocationCanonicalInput {
153
153
  reason: string;
154
154
  signed_at: string;
155
155
  }
156
- type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
156
+ type AgentErrorCode = 'E_UNSUPPORTED_VERSION' | 'E_MALFORMED' | 'E_BAD_ID' | 'E_BAD_SIG' | 'E_BAD_SCOPE_GRAMMAR' | 'E_NOT_YET_VALID' | 'E_EXPIRED' | 'E_REVOKED' | 'E_STALE' | 'E_DELEGATION_MISMATCH' | 'E_AGENT_MISMATCH' | 'E_OUT_OF_WINDOW' | 'E_SCOPE_DENIED' | 'E_BAD_ACTION_STAMP' | 'E_NO_BOND' | 'E_BOND_UNMET' | 'E_BOND_UNVERIFIED' | 'E_REVOKER_UNAUTHORIZED' | 'E_CALENDAR_UNREACHABLE' | 'E_SUBDELEGATION_DEPTH_EXCEEDED' | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH' | 'E_SUBDELEGATION_EXPIRES_EXTENDED' | 'E_SUBDELEGATION_SCOPE_ESCALATED' | 'E_SCOPES_BOTH_PROVIDED' | 'E_SCOPES_NEITHER_PROVIDED' | 'E_SCOPES_UNREADABLE' | 'E_BAD_LOCK_ENVELOPE' | 'E_BAD_FEDERATION_DESCRIPTOR' | 'E_THRESHOLD_MISMATCH' | 'E_THRESHOLD_NOT_MET' | 'E_UNKNOWN_GUARDIAN' | 'E_DUPLICATE_GUARDIAN';
157
157
  interface VerifyOk<T> {
158
158
  ok: true;
159
159
  envelope: T;
@@ -187,6 +187,14 @@ interface VerifyActionOkExtra {
187
187
  blockHash: string;
188
188
  verified: true;
189
189
  };
190
+ asOf: {
191
+ basis: 'now';
192
+ now: string;
193
+ } | {
194
+ basis: 'signed_at';
195
+ signedAt: string;
196
+ anchored: boolean;
197
+ };
190
198
  }
191
199
  type VerifyActionResult = (VerifyOk<ActionEnvelope> & VerifyActionOkExtra) | VerifyErr;
192
200
 
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB","file":"types.js","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAEO,IAAM,gBAAA,GAAmB","file":"types.js","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_STALE'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n /**\n * The time the verdict holds for. `now`: verifyAction, checked live.\n * `signed_at`: verifyActionAsOfSignedAt, checked as of the time the agent\n * claims; `anchored` is false when only the agent says so.\n */\n asOf:\n | { basis: 'now'; now: string }\n | { basis: 'signed_at'; signedAt: string; anchored: boolean };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAEO,IAAM,gBAAA,GAAmB","file":"types.mjs","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAEO,IAAM,gBAAA,GAAmB","file":"types.mjs","sourcesContent":["// Wire types for OC Agent v1 envelopes. See SPEC.md §4, §5, §9.\n\nexport const ENVELOPE_VERSION = 1 as const;\n\nexport type EnvelopeKind =\n | 'agent-delegation'\n | 'agent-action'\n | 'agent-revocation'\n | 'agent-subdelegation';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActorRef {\n /** mainnet Bitcoin address (P2WPKH, P2TR, or P2PKH). */\n address: string;\n alg: 'bip322';\n}\n\nexport interface Signature {\n alg: 'bip322';\n pubkey: string; // equals the producing actor's address\n value: string; // base64 BIP-322 signature over hex(id)\n}\n\nexport type RevocationHolder = 'principal' | 'agent';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Delegation (SPEC §4)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DelegationBond {\n /** Non-negative sats declared as bonded at issuance time. */\n sats: number;\n /** SHA-256 hex of the OrangeCheck canonical message signed by principal.address. */\n attestation_id: string;\n}\n\nexport interface DelegationRevocationRef {\n /** Who MAY publish a revocation. Default [\"principal\"]. */\n holders: RevocationHolder[];\n /** Optional Nostr-addressable pointer to a published revocation. Non-cryptographic. */\n ref: string | null;\n}\n\n/**\n * v1.2 private-scope mode: a wholesale OC Lock v2 LockEnvelope wrapping the\n * canonical scope list as its payload. We re-import the LockEnvelope type\n * structurally rather than depending on the @orangecheck/lock-core type — agent-\n * core's type surface stays loose so verifiers can be authored in either an\n * agent-only or full-family setup.\n */\nexport interface ScopesEncryptedEnvelope {\n v: 2;\n kind: 'identity';\n id: string;\n alg: { kem: 'x25519'; aead: 'aes-256-gcm'; kdf: 'hkdf-sha256' };\n from: { address: string; attestation_id?: string };\n recipients: Array<{\n address: string;\n device_id: string;\n device_pk: string;\n eph_pk: string;\n wrapped_key: string;\n nonce_kek: string;\n }>;\n ciphertext: string;\n nonce_ct: string;\n hint?: string;\n created_at: string;\n expires_at: string | null;\n payment: unknown | null;\n sig: { alg: 'bip322'; pubkey: string; value: string };\n}\n\nexport interface DelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-delegation';\n id: string; // 64-hex sha256(canonical_message)\n principal: ActorRef;\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: sorted lexicographically in the canonical\n * message; stored in sorted order on the envelope too.\n *\n * v1.2 private mode: this field is OMITTED from the envelope JSON;\n * `scopes_encrypted` is set instead. After decryption, the recovered\n * scope list takes this field's place in the in-memory envelope object\n * for the remainder of verification.\n */\n scopes?: string[];\n /**\n * v1.2 private mode (PRIVATE-SCOPE.md §1.1): an OC Lock v2 envelope\n * wrapping the canonical scope-list bytes as its payload, sealed to one\n * or more recipients (typically the agent ± named verifiers). MUTUALLY\n * EXCLUSIVE with `scopes`.\n */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n bond: DelegationBond | null;\n issued_at: string; // ISO 8601 UTC\n expires_at: string; // ISO 8601 UTC\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface DelegationCanonicalInput {\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n bond_sats: number;\n /** 64-hex attestation id or the literal string \"none\". */\n bond_attestation: string;\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Agent-action (SPEC §5) — strict extension of OC Stamp\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ActionContent {\n hash: string; // \"sha256:<64-hex>\"\n length: number;\n mime: string;\n ref: string | null;\n}\n\nexport interface ActionOts {\n status: 'pending' | 'confirmed';\n proof: string;\n calendars: string[];\n block_height: number | null;\n block_hash: string | null;\n upgraded_at: string | null;\n}\n\nexport interface ActionEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-action';\n id: string;\n content: ActionContent;\n signer: ActorRef; // agent\n signed_at: string;\n delegation_id: string; // 64-hex\n scope_exercised: string; // a sub-scope of some granted scope\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface ActionCanonicalInput {\n address: string; // agent address\n content_hash: string;\n content_length: number;\n content_mime: string;\n signed_at: string;\n delegation_id: string;\n scope_exercised: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Sub-delegation (SUB-DELEGATION.md, v1.1)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface SubdelegationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-subdelegation';\n id: string; // 64-hex sha256(canonical_message)\n parent_id: string; // 64-hex; the immediate parent envelope's id\n /** The sub-principal — equal to parent.agent.address. */\n principal: ActorRef;\n /** The recipient sub-agent. */\n agent: ActorRef;\n /**\n * v1.0 / v1.1 public mode: each scope MUST be a sub-scope of some scope\n * on the parent. v1.2 private mode: omitted; `scopes_encrypted` set.\n */\n scopes?: string[];\n /** v1.2 private mode — same shape as the root delegation field. */\n scopes_encrypted?: ScopesEncryptedEnvelope;\n issued_at: string; // ISO 8601 UTC; >= parent.issued_at\n expires_at: string; // ISO 8601 UTC; <= parent.expires_at\n nonce: string; // 32-hex random\n revocation: DelegationRevocationRef;\n sig: Signature;\n}\n\nexport interface SubdelegationCanonicalInput {\n parent_id: string;\n principal: string;\n agent: string;\n scopes: string[]; // pre-canonicalized, pre-sorted\n issued_at: string;\n expires_at: string;\n nonce: string;\n}\n\n/** Either a root or a sub envelope; chain links walk up to a root delegation. */\nexport type ChainLink = DelegationEnvelope | SubdelegationEnvelope;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Revocation (SPEC §9)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface RevocationEnvelope {\n v: typeof ENVELOPE_VERSION;\n kind: 'agent-revocation';\n id: string;\n delegation_id: string;\n signer: ActorRef;\n /** Short ASCII rationale, <= 128 bytes. Empty string if omitted. */\n reason: string;\n signed_at: string;\n ots: ActionOts | null;\n sig: Signature;\n}\n\nexport interface RevocationCanonicalInput {\n address: string;\n delegation_id: string;\n reason: string;\n signed_at: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error codes (SPEC §11)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type AgentErrorCode =\n | 'E_UNSUPPORTED_VERSION'\n | 'E_MALFORMED'\n | 'E_BAD_ID'\n | 'E_BAD_SIG'\n | 'E_BAD_SCOPE_GRAMMAR'\n | 'E_NOT_YET_VALID'\n | 'E_EXPIRED'\n | 'E_REVOKED'\n | 'E_STALE'\n | 'E_DELEGATION_MISMATCH'\n | 'E_AGENT_MISMATCH'\n | 'E_OUT_OF_WINDOW'\n | 'E_SCOPE_DENIED'\n | 'E_BAD_ACTION_STAMP'\n | 'E_NO_BOND'\n | 'E_BOND_UNMET'\n | 'E_BOND_UNVERIFIED'\n | 'E_REVOKER_UNAUTHORIZED'\n | 'E_CALENDAR_UNREACHABLE'\n | 'E_SUBDELEGATION_DEPTH_EXCEEDED'\n | 'E_SUBDELEGATION_PRINCIPAL_MISMATCH'\n | 'E_SUBDELEGATION_EXPIRES_EXTENDED'\n | 'E_SUBDELEGATION_SCOPE_ESCALATED'\n | 'E_SCOPES_BOTH_PROVIDED'\n | 'E_SCOPES_NEITHER_PROVIDED'\n | 'E_SCOPES_UNREADABLE'\n | 'E_BAD_LOCK_ENVELOPE'\n // v1.2 federation principal (FEDERATION.md §3.3)\n | 'E_BAD_FEDERATION_DESCRIPTOR'\n | 'E_THRESHOLD_MISMATCH'\n | 'E_THRESHOLD_NOT_MET'\n | 'E_UNKNOWN_GUARDIAN'\n | 'E_DUPLICATE_GUARDIAN';\n\nexport interface VerifyOk<T> {\n ok: true;\n envelope: T;\n canonicalMessage: string;\n id: string;\n}\n\nexport interface VerifyErr {\n ok: false;\n code: AgentErrorCode;\n message: string;\n}\n\nexport type VerifyDelegationResult = VerifyOk<DelegationEnvelope> | VerifyErr;\nexport type VerifyRevocationResult = VerifyOk<RevocationEnvelope> | VerifyErr;\nexport type VerifySubdelegationResult = VerifyOk<SubdelegationEnvelope> | VerifyErr;\n\nexport interface VerifyActionOkExtra {\n /** The ROOT delegation rooting the authority chain. */\n delegation: DelegationEnvelope;\n /**\n * The sub-delegation chain `[S_1, …, S_leaf]`. Empty when the action cites\n * the root directly. The action's authority leaf is\n * `chain[chain.length - 1] ?? delegation`.\n */\n chain: SubdelegationEnvelope[];\n scopeExercised: string;\n /**\n * `claimed` is a confirmed `ots` block no verifier walked: the block is the\n * envelope's say-so. Only a passing `verifyOtsAnchor` yields `confirmed`.\n */\n anchor:\n | { status: 'none' }\n | { status: 'pending' }\n | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }\n | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };\n /**\n * The time the verdict holds for. `now`: verifyAction, checked live.\n * `signed_at`: verifyActionAsOfSignedAt, checked as of the time the agent\n * claims; `anchored` is false when only the agent says so.\n */\n asOf:\n | { basis: 'now'; now: string }\n | { basis: 'signed_at'; signedAt: string; anchored: boolean };\n}\n\nexport type VerifyActionResult =\n | (VerifyOk<ActionEnvelope> & VerifyActionOkExtra)\n | VerifyErr;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangecheck/agent-core",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "OC Agent canonical messages, envelope formats (delegation/action/revocation), scope grammar, and verification. See https://github.com/orangecheck/oc-agent-protocol.",
5
5
  "keywords": [
6
6
  "bitcoin",
package/src/index.ts CHANGED
@@ -37,15 +37,19 @@ export type { Scope, ScopeConstraint, ScopeOp, ValidationOptions } from './scope
37
37
  export {
38
38
  verifyDelegation,
39
39
  verifyAction,
40
+ verifyActionAsOfSignedAt,
41
+ createReplayGuard,
40
42
  verifyRevocation,
41
43
  verifySubdelegation,
42
44
  AgentError,
43
45
  DEFAULT_MAX_CHAIN_DEPTH,
46
+ DEFAULT_MAX_SKEW_MS,
44
47
  } from './verify.js';
45
48
  export type {
46
49
  VerifyBase,
47
50
  VerifyDelegationInput,
48
51
  VerifyActionInput,
52
+ VerifyActionAsOfSignedAtInput,
49
53
  VerifyRevocationInput,
50
54
  VerifySubdelegationInput,
51
55
  } from './verify.js';
@@ -0,0 +1,151 @@
1
+ // SPEC §8.1 step 6 checks the delegation at the time of verification, not at
2
+ // the action's signed_at. The agent picks signed_at, so an action that only
3
+ // had to fit the delegation's window could be backdated past its expiry or past
4
+ // a revocation and still verify. A live verifier checks at `now`; an audit of a
5
+ // past action says outright that it relied on the agent's claimed time.
6
+ import { describe, expect, it } from 'vitest';
7
+
8
+ import { computeActionId, computeRevocationId } from './canonical.js';
9
+ import { createReplayGuard, verifyAction, verifyActionAsOfSignedAt } from './verify.js';
10
+ import type { ActionEnvelope, DelegationEnvelope, RevocationEnvelope } from './types.js';
11
+
12
+ const delegation: DelegationEnvelope = {
13
+ v: 1,
14
+ kind: 'agent-delegation',
15
+ id: '36d79600191db871baa3fc9aa3b5e77750a5c423b1f620ec26cf16bd122e19a7',
16
+ principal: { address: 'bc1qprincipal000000000000000000000000000000', alg: 'bip322' },
17
+ agent: { address: 'bc1qagent0000000000000000000000000000000000', alg: 'bip322' },
18
+ scopes: ['lock:seal(recipient=bc1qalice000000000000000000000000000000000)'],
19
+ bond: null,
20
+ issued_at: '2026-04-22T12:00:00Z',
21
+ expires_at: '2026-04-29T12:00:00Z',
22
+ nonce: '0123456789abcdef0123456789abcdef',
23
+ revocation: { holders: ['principal'], ref: null },
24
+ sig: { alg: 'bip322', pubkey: 'bc1qprincipal000000000000000000000000000000', value: 'AAAA' },
25
+ } as DelegationEnvelope;
26
+
27
+ function action(signed_at: string): ActionEnvelope {
28
+ const canon = {
29
+ address: delegation.agent.address,
30
+ content_hash: 'sha256:' + '00'.repeat(32),
31
+ content_length: 0,
32
+ content_mime: 'text/plain',
33
+ signed_at,
34
+ delegation_id: delegation.id,
35
+ scope_exercised: delegation.scopes![0]!,
36
+ };
37
+ return {
38
+ v: 1,
39
+ kind: 'agent-action',
40
+ id: computeActionId(canon),
41
+ content: { hash: canon.content_hash, length: 0, mime: 'text/plain', ref: null },
42
+ signer: { address: canon.address, alg: 'bip322' },
43
+ signed_at,
44
+ delegation_id: delegation.id,
45
+ scope_exercised: canon.scope_exercised,
46
+ ots: null,
47
+ sig: { alg: 'bip322', pubkey: canon.address, value: 'AAAA' },
48
+ } as ActionEnvelope;
49
+ }
50
+
51
+ function revocation(signed_at: string): RevocationEnvelope {
52
+ const canon = { address: delegation.principal.address, delegation_id: delegation.id, reason: '', signed_at };
53
+ return {
54
+ v: 1,
55
+ kind: 'agent-revocation',
56
+ id: computeRevocationId(canon),
57
+ delegation_id: delegation.id,
58
+ signer: { address: canon.address, alg: 'bip322' },
59
+ reason: '',
60
+ signed_at,
61
+ ots: null,
62
+ sig: { alg: 'bip322', pubkey: canon.address, value: 'AAAA' },
63
+ } as RevocationEnvelope;
64
+ }
65
+
66
+ const base = { delegation, skipSignatureVerification: true };
67
+
68
+ describe('live verification checks at now', () => {
69
+ it('refuses an action backdated into the window of a delegation that has expired', async () => {
70
+ const r = await verifyAction({
71
+ ...base,
72
+ action: action('2026-04-28T00:00:00Z'),
73
+ revocations: [],
74
+ now: new Date('2026-05-01T00:00:00Z'),
75
+ });
76
+ expect(r.ok === false && r.code).toBe('E_EXPIRED');
77
+ });
78
+
79
+ it('refuses an action backdated to before a revocation that is already in effect', async () => {
80
+ const r = await verifyAction({
81
+ ...base,
82
+ action: action('2026-04-24T11:59:00Z'),
83
+ revocations: [revocation('2026-04-24T11:59:30Z')],
84
+ now: new Date('2026-04-24T12:00:00Z'),
85
+ });
86
+ expect(r.ok === false && r.code).toBe('E_REVOKED');
87
+ });
88
+
89
+ it('refuses an action signed further from now than the freshness bound', async () => {
90
+ const now = new Date('2026-04-24T12:00:00Z');
91
+ const old = await verifyAction({ ...base, action: action('2026-04-24T11:50:00Z'), revocations: [], now });
92
+ expect(old.ok === false && old.code).toBe('E_STALE');
93
+ const ahead = await verifyAction({ ...base, action: action('2026-04-24T12:05:00Z'), revocations: [], now });
94
+ expect(ahead.ok === false && ahead.code).toBe('E_STALE');
95
+ });
96
+
97
+ it('accepts a fresh action under a clear feed, and says it was checked at now', async () => {
98
+ const now = new Date('2026-04-24T12:00:00Z');
99
+ const r = await verifyAction({ ...base, action: action('2026-04-24T11:59:30Z'), revocations: [], now });
100
+ expect(r.ok).toBe(true);
101
+ expect(r.ok && r.asOf).toEqual({ basis: 'now', now: '2026-04-24T12:00:00.000Z' });
102
+ });
103
+
104
+ it('takes a wider freshness bound when the caller sets one', async () => {
105
+ const r = await verifyAction({
106
+ ...base,
107
+ action: action('2026-04-24T11:50:00Z'),
108
+ revocations: [],
109
+ now: new Date('2026-04-24T12:00:00Z'),
110
+ maxSkewMs: 15 * 60_000,
111
+ });
112
+ expect(r.ok).toBe(true);
113
+ });
114
+
115
+ it('does not let a revocation dated in the future refuse an action today', async () => {
116
+ const r = await verifyAction({
117
+ ...base,
118
+ action: action('2026-04-24T11:59:30Z'),
119
+ revocations: [revocation('2026-04-25T00:00:00Z')],
120
+ now: new Date('2026-04-24T12:00:00Z'),
121
+ });
122
+ expect(r.ok).toBe(true);
123
+ });
124
+ });
125
+
126
+ describe('an audit checks as of signed_at, and says that time is a claim', () => {
127
+ it('accepts a past action inside its window, with an unanchored signed_at', async () => {
128
+ const r = await verifyActionAsOfSignedAt({ ...base, action: action('2026-04-28T00:00:00Z'), revocations: [] });
129
+ expect(r.ok).toBe(true);
130
+ expect(r.ok && r.asOf).toEqual({ basis: 'signed_at', signedAt: '2026-04-28T00:00:00Z', anchored: false });
131
+ });
132
+
133
+ it('still refuses an action that claims a time after the revocation', async () => {
134
+ const r = await verifyActionAsOfSignedAt({
135
+ ...base,
136
+ action: action('2026-04-24T12:00:00Z'),
137
+ revocations: [revocation('2026-04-24T11:59:30Z')],
138
+ });
139
+ expect(r.ok === false && r.code).toBe('E_REVOKED');
140
+ });
141
+ });
142
+
143
+ describe('createReplayGuard', () => {
144
+ it('admits an action once, and forgets it when it could no longer pass as fresh', () => {
145
+ const guard = createReplayGuard(120_000);
146
+ const a = action('2026-04-24T12:00:00Z');
147
+ expect(guard.firstUse(a, new Date('2026-04-24T12:00:05Z'))).toBe(true);
148
+ expect(guard.firstUse(a, new Date('2026-04-24T12:01:00Z'))).toBe(false);
149
+ expect(guard.firstUse(a, new Date('2026-04-24T12:02:01Z'))).toBe(true);
150
+ });
151
+ });
@@ -5,7 +5,7 @@
5
5
  import { describe, expect, it } from 'vitest';
6
6
 
7
7
  import { computeActionId, computeDelegationId, computeRevocationId } from './canonical.js';
8
- import { verifyAction, verifyDelegation, verifyRevocation } from './verify.js';
8
+ import { verifyActionAsOfSignedAt, verifyDelegation, verifyRevocation } from './verify.js';
9
9
  import type { ActionEnvelope, ActionOts, DelegationEnvelope, RevocationEnvelope } from './types.js';
10
10
 
11
11
  const PRINCIPAL = 'bc1qprincipal000000000000000000000000000000';
@@ -91,6 +91,8 @@ function confirmed(height: number): ActionOts {
91
91
 
92
92
  const AT = new Date('2026-04-23T12:00:00Z');
93
93
 
94
+ // Priority between an action and a revocation (SPEC §9.3) is a question about
95
+ // the action's own time, so these run as audits, as of signed_at.
94
96
  describe('revocation authority always includes the principal', () => {
95
97
  for (const holders of [['agent'], [], ['principal'], undefined, 'agent']) {
96
98
  it(`principal revocation is honoured with holders=${JSON.stringify(holders)}`, async () => {
@@ -105,7 +107,7 @@ describe('revocation authority always includes the principal', () => {
105
107
  now: AT,
106
108
  });
107
109
  expect(dr.ok === false && dr.code).toBe('E_REVOKED');
108
- const ar = await verifyAction({
110
+ const ar = await verifyActionAsOfSignedAt({
109
111
  action: action(d, '2026-04-23T06:00:00Z'),
110
112
  delegation: d,
111
113
  revocations: [rev],
@@ -150,7 +152,7 @@ describe('revocation priority uses only verified anchors', () => {
150
152
  const later = '2026-04-23T06:00:00Z';
151
153
 
152
154
  it('an action anchor with no anchor verifier does not outrank a revocation', async () => {
153
- const r = await verifyAction({
155
+ const r = await verifyActionAsOfSignedAt({
154
156
  action: action(d, later, confirmed(1)),
155
157
  delegation: d,
156
158
  revocations: [rev],
@@ -160,7 +162,7 @@ describe('revocation priority uses only verified anchors', () => {
160
162
  });
161
163
 
162
164
  it('an action anchor the verifier rejects does not outrank a revocation', async () => {
163
- const r = await verifyAction({
165
+ const r = await verifyActionAsOfSignedAt({
164
166
  action: action(d, later, confirmed(1)),
165
167
  delegation: d,
166
168
  revocations: [rev],
@@ -168,7 +170,7 @@ describe('revocation priority uses only verified anchors', () => {
168
170
  verifyOtsAnchor: async () => false,
169
171
  });
170
172
  expect(r.ok === false && r.code).toBe('E_REVOKED');
171
- const clean = await verifyAction({
173
+ const clean = await verifyActionAsOfSignedAt({
172
174
  action: action(d, later, confirmed(1)),
173
175
  delegation: d,
174
176
  revocations: [],
@@ -185,14 +187,14 @@ describe('revocation priority uses only verified anchors', () => {
185
187
 
186
188
  // The ots block is outside the signature: its block is a claim until walked.
187
189
  it('reports an unwalked anchor as claimed, and a walked one as confirmed', async () => {
188
- const unwalked = await verifyAction({
190
+ const unwalked = await verifyActionAsOfSignedAt({
189
191
  action: action(d, later, confirmed(1)),
190
192
  delegation: d,
191
193
  revocations: [],
192
194
  skipSignatureVerification: true,
193
195
  });
194
196
  expect(unwalked.ok && unwalked.anchor.status).toBe('claimed');
195
- const walked = await verifyAction({
197
+ const walked = await verifyActionAsOfSignedAt({
196
198
  action: action(d, later, confirmed(1)),
197
199
  delegation: d,
198
200
  revocations: [],
@@ -211,7 +213,7 @@ describe('revocation priority uses only verified anchors', () => {
211
213
  const a = action(d, later, confirmed(100));
212
214
  const revAnchored = revocation(d, PRINCIPAL, '2026-04-23T00:00:00Z', confirmed(200));
213
215
  const seen: string[] = [];
214
- const r = await verifyAction({
216
+ const r = await verifyActionAsOfSignedAt({
215
217
  action: a,
216
218
  delegation: d,
217
219
  revocations: [revAnchored],
@@ -227,7 +229,7 @@ describe('revocation priority uses only verified anchors', () => {
227
229
  });
228
230
 
229
231
  it('a verified action anchor against an unanchored revocation falls back to signed times', async () => {
230
- const r = await verifyAction({
232
+ const r = await verifyActionAsOfSignedAt({
231
233
  action: action(d, later, confirmed(1)),
232
234
  delegation: d,
233
235
  revocations: [rev],
@@ -235,7 +237,7 @@ describe('revocation priority uses only verified anchors', () => {
235
237
  verifyOtsAnchor: async () => true,
236
238
  });
237
239
  expect(r.ok === false && r.code).toBe('E_REVOKED');
238
- const before = await verifyAction({
240
+ const before = await verifyActionAsOfSignedAt({
239
241
  action: action(d, '2026-04-22T18:00:00Z', confirmed(1)),
240
242
  delegation: d,
241
243
  revocations: [rev],
@@ -248,7 +250,7 @@ describe('revocation priority uses only verified anchors', () => {
248
250
  it('a revocation anchor that does not verify cannot move the revocation later', async () => {
249
251
  const revFarFuture = revocation(d, PRINCIPAL, '2026-04-23T00:00:00Z', confirmed(10_000_000));
250
252
  const a = action(d, later, confirmed(100));
251
- const r = await verifyAction({
253
+ const r = await verifyActionAsOfSignedAt({
252
254
  action: a,
253
255
  delegation: d,
254
256
  revocations: [revFarFuture],
@@ -262,7 +264,7 @@ describe('revocation priority uses only verified anchors', () => {
262
264
  const a = action(d, later, confirmed(300));
263
265
  const revAt = (h: number) => revocation(d, PRINCIPAL, '2026-04-23T00:00:00Z', confirmed(h));
264
266
  const verifyOtsAnchor = async () => true;
265
- const earlierRev = await verifyAction({
267
+ const earlierRev = await verifyActionAsOfSignedAt({
266
268
  action: a,
267
269
  delegation: d,
268
270
  revocations: [revAt(300)],
@@ -270,7 +272,7 @@ describe('revocation priority uses only verified anchors', () => {
270
272
  verifyOtsAnchor,
271
273
  });
272
274
  expect(earlierRev.ok === false && earlierRev.code).toBe('E_REVOKED');
273
- const laterRev = await verifyAction({
275
+ const laterRev = await verifyActionAsOfSignedAt({
274
276
  action: a,
275
277
  delegation: d,
276
278
  revocations: [revAt(301)],
@@ -106,7 +106,7 @@ describe('the refusal names a query relays answer', () => {
106
106
  });
107
107
 
108
108
  it('verifyAction', async () => {
109
- const r = await verifyAction({ action, delegation, skipSignatureVerification: true });
109
+ const r = await verifyAction({ action, delegation, skipSignatureVerification: true, now: AT });
110
110
  expect(r.ok === false && r.code).toBe('E_MALFORMED');
111
111
  expect(r.ok === false && r.message).toMatch(/"#t": \[id of every link in the chain\]/);
112
112
  expect(r.ok === false && r.message).not.toMatch(/#delegation/);
@@ -18,7 +18,13 @@ import {
18
18
  subdelegationCanonicalMessage,
19
19
  } from './canonical.js';
20
20
  import { parseScope, ScopeParseError } from './scope.js';
21
- import { verifyAction, verifyDelegation, verifyRevocation, verifySubdelegation } from './verify.js';
21
+ import {
22
+ verifyAction,
23
+ verifyActionAsOfSignedAt,
24
+ verifyDelegation,
25
+ verifyRevocation,
26
+ verifySubdelegation,
27
+ } from './verify.js';
22
28
  import type {
23
29
  ActionEnvelope,
24
30
  AgentErrorCode,
@@ -127,6 +133,12 @@ interface NegativeActionVector extends NegativeVectorBase {
127
133
  scope_exercised: string;
128
134
  content_ref?: string | null;
129
135
  ots?: unknown;
136
+ /** Present on a live vector (§8.1 steps 6–7, §8.3 step 11a): the time of verification. */
137
+ verified_at?: string;
138
+ /** The freshness bound a live vector assumes, in seconds. */
139
+ max_skew_seconds?: number;
140
+ /** Revocations of the cited delegation, as the inputs of each envelope. */
141
+ revocations?: { address: string; reason: string; signed_at: string }[];
130
142
  };
131
143
  }
132
144
 
@@ -279,7 +291,8 @@ describe('oc-agent-protocol test vectors', () => {
279
291
  if (!delegation) {
280
292
  throw new Error(`action vector ${name} references missing delegation ${data.inputs.delegation_id}`);
281
293
  }
282
- const r = await verifyAction({
294
+ // A positive vector is the envelope as of its own signed_at.
295
+ const r = await verifyActionAsOfSignedAt({
283
296
  skipRevocationCheck: true,
284
297
  action: data.expected.envelope,
285
298
  delegation,
@@ -461,12 +474,34 @@ describe('oc-agent-protocol negative test vectors', () => {
461
474
  ots: null,
462
475
  sig: { alg: 'bip322', pubkey: v.inputs.address, value: 'AAAA' },
463
476
  };
464
- const r = await verifyAction({
465
- skipRevocationCheck: true,
466
- action,
467
- delegation,
468
- skipSignatureVerification: true,
469
- });
477
+ const revocations: RevocationEnvelope[] = (v.inputs.revocations ?? []).map((rv) => ({
478
+ v: 1,
479
+ kind: 'agent-revocation',
480
+ id: computeRevocationId({ ...rv, delegation_id: v.inputs.delegation_id }),
481
+ delegation_id: v.inputs.delegation_id,
482
+ signer: { address: rv.address, alg: 'bip322' },
483
+ reason: rv.reason,
484
+ signed_at: rv.signed_at,
485
+ ots: null,
486
+ sig: { alg: 'bip322', pubkey: rv.address, value: 'AAAA' },
487
+ }));
488
+ // A vector with verified_at is checked live at that time; one
489
+ // without is an audit of the envelope as of its signed_at.
490
+ const r = v.inputs.verified_at
491
+ ? await verifyAction({
492
+ action,
493
+ delegation,
494
+ skipSignatureVerification: true,
495
+ revocations,
496
+ now: new Date(v.inputs.verified_at),
497
+ maxSkewMs: (v.inputs.max_skew_seconds ?? 120) * 1000,
498
+ })
499
+ : await verifyActionAsOfSignedAt({
500
+ skipRevocationCheck: true,
501
+ action,
502
+ delegation,
503
+ skipSignatureVerification: true,
504
+ });
470
505
  expect(r.ok).toBe(false);
471
506
  if (!r.ok) {
472
507
  // v07 specifically allows either E_OUT_OF_WINDOW or E_EXPIRED
package/src/types.ts CHANGED
@@ -237,6 +237,7 @@ export type AgentErrorCode =
237
237
  | 'E_NOT_YET_VALID'
238
238
  | 'E_EXPIRED'
239
239
  | 'E_REVOKED'
240
+ | 'E_STALE'
240
241
  | 'E_DELEGATION_MISMATCH'
241
242
  | 'E_AGENT_MISMATCH'
242
243
  | 'E_OUT_OF_WINDOW'
@@ -298,6 +299,14 @@ export interface VerifyActionOkExtra {
298
299
  | { status: 'pending' }
299
300
  | { status: 'claimed'; blockHeight: number; blockHash: string; verified: false }
300
301
  | { status: 'confirmed'; blockHeight: number; blockHash: string; verified: true };
302
+ /**
303
+ * The time the verdict holds for. `now`: verifyAction, checked live.
304
+ * `signed_at`: verifyActionAsOfSignedAt, checked as of the time the agent
305
+ * claims; `anchored` is false when only the agent says so.
306
+ */
307
+ asOf:
308
+ | { basis: 'now'; now: string }
309
+ | { basis: 'signed_at'; signedAt: string; anchored: boolean };
301
310
  }
302
311
 
303
312
  export type VerifyActionResult =