diff --git a/python-sdk/commandlayer/types.py b/python-sdk/commandlayer/types.py index 0c31330..db871ff 100644 --- a/python-sdk/commandlayer/types.py +++ b/python-sdk/commandlayer/types.py @@ -62,6 +62,7 @@ class VerifyOptions(TypedDict, total=False): class VerifyChecks(TypedDict): hash_matches: bool signature_valid: bool + receipt_id_present: bool receipt_id_matches: bool alg_matches: bool canonical_matches: bool diff --git a/python-sdk/commandlayer/verify.py b/python-sdk/commandlayer/verify.py index bd480ab..5f5db44 100644 --- a/python-sdk/commandlayer/verify.py +++ b/python-sdk/commandlayer/verify.py @@ -199,6 +199,7 @@ def verify_receipt(receipt: CanonicalReceipt | CommandResponse, public_key: str metadata = target.get("metadata") if isinstance(target, dict) else None receipt_id_value = metadata.get("receipt_id") if isinstance(metadata, dict) else None receipt_id = receipt_id_value if isinstance(receipt_id_value, str) else None + receipt_id_present = receipt_id is not None receipt_id_matches = not receipt_id or not claimed_hash or receipt_id == claimed_hash pubkey: bytes | None = None @@ -239,10 +240,11 @@ def verify_receipt(receipt: CanonicalReceipt | CommandResponse, public_key: str signature_error = str(err) return { - "ok": alg_matches and canonical_matches and hash_matches and receipt_id_matches and signature_valid, + "ok": alg_matches and canonical_matches and hash_matches and signature_valid, "checks": { "hash_matches": hash_matches, "signature_valid": signature_valid, + "receipt_id_present": receipt_id_present, "receipt_id_matches": receipt_id_matches, "alg_matches": alg_matches, "canonical_matches": canonical_matches, @@ -272,6 +274,7 @@ def verify_receipt(receipt: CanonicalReceipt | CommandResponse, public_key: str "checks": { "hash_matches": False, "signature_valid": False, + "receipt_id_present": isinstance(metadata, dict) and isinstance(metadata.get("receipt_id"), str), "receipt_id_matches": False, "alg_matches": False, "canonical_matches": False, diff --git a/typescript-sdk/scripts/unit-tests.mjs b/typescript-sdk/scripts/unit-tests.mjs index 1f2c509..735d559 100644 --- a/typescript-sdk/scripts/unit-tests.mjs +++ b/typescript-sdk/scripts/unit-tests.mjs @@ -198,6 +198,7 @@ await assertRejects( const receipt = { status: "success", + verb: "summarize", result: { summary: "test" }, metadata: { proof: { diff --git a/typescript-sdk/src/index.ts b/typescript-sdk/src/index.ts index ec80d4e..1fc5ee6 100644 --- a/typescript-sdk/src/index.ts +++ b/typescript-sdk/src/index.ts @@ -26,6 +26,11 @@ const VERBS = [ export type Verb = (typeof VERBS)[number]; export type ReceiptStatus = "success" | "error" | string; +export type ReceiptProtocolMetadata = { + verb?: string; + version?: string; + [k: string]: unknown; +}; export type ReceiptProof = { alg: typeof CANONICAL_ALG; @@ -76,12 +81,12 @@ export type RuntimeMetadata = { [k: string]: unknown; }; -export type CommandResponse = { - receipt: CanonicalReceipt; +export type CommandResponse = { + receipt: CanonicalReceipt; runtime_metadata?: RuntimeMetadata; }; -export type LegacyBlendedReceipt = CanonicalReceipt & { +export type LegacyBlendedReceipt = CanonicalReceipt & { trace?: RuntimeMetadata; }; @@ -295,7 +300,7 @@ function extractReceipt(subject: CanonicalReceipt | CommandResponse | LegacyBlen export function extractReceiptVerb(subject: CanonicalReceipt | CommandResponse | LegacyBlendedReceipt): string | null { const receipt = extractReceipt(subject); - return isRecord(receipt.x402) && typeof receipt.x402.verb === "string" ? receipt.x402.verb : null; + return getReceiptVerb(receipt); } export function normalizeCommandResponse(payload: unknown): CommandResponse { @@ -349,6 +354,7 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo const { hash_sha256: recomputedHash } = recomputeReceiptHashSha256(receipt); const hashMatches = claimedHash === recomputedHash; const receiptId = typeof receipt.metadata?.receipt_id === "string" ? receipt.metadata.receipt_id : null; + const receiptIdPresent = typeof receipt.metadata?.receipt_id === "string"; const receiptIdMatches = !receiptId || !claimedHash ? true : receiptId === claimedHash; let pubkey: Uint8Array | null = null; @@ -396,7 +402,7 @@ export async function verifyReceipt(receiptLike: CanonicalReceipt | CommandRespo }, values: { verb: getReceiptVerb(receipt), - signer_id, + signer_id: signerId, alg, canonical, claimed_hash: claimedHash,