Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/python/src/codespar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
is_tool_not_mocked,
)
from .types import (
AgenticReceipt,
AssistantTextEvent,
AuthConfig,
AuthResult,
Expand All @@ -84,6 +85,8 @@
LedgerAction,
LedgerArgs,
LedgerLeg,
LedgerReceiptResult,
LedgerReceiptsResult,
LedgerResult,
ManageConnections,
MockObject,
Expand All @@ -94,6 +97,11 @@
Preset,
ProxyRequest,
ProxyResult,
ReceiptDelivery,
ReceiptException,
ReceiptMandate,
ReceiptPayment,
ReceiptQuote,
RequiredSecret,
SearchStrategy,
SendResult,
Expand Down Expand Up @@ -141,6 +149,8 @@
"POLICY_DENIED",
"TOOL_NOT_MOCKED",
"TOOL_RESULT_CODES",
# Agentic receipt (the Control Record)
"AgenticReceipt",
"ApiError",
"ApprovalRequiredOutput",
"AssistantTextEvent",
Expand Down Expand Up @@ -182,6 +192,8 @@
"LedgerAction",
"LedgerArgs",
"LedgerLeg",
"LedgerReceiptResult",
"LedgerReceiptsResult",
"LedgerResult",
"ManageConnections",
# Test-mode mocks
Expand All @@ -199,6 +211,11 @@
# Proxy
"ProxyRequest",
"ProxyResult",
"ReceiptDelivery",
"ReceiptException",
"ReceiptMandate",
"ReceiptPayment",
"ReceiptQuote",
"RequiredSecret",
"SearchStrategy",
"SendResult",
Expand Down
99 changes: 98 additions & 1 deletion packages/python/src/codespar/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ class ShipResult:
# ── codespar_ledger wire shape ─────────────────────────────────────


LedgerAction = Literal["entry", "balance", "account"]
LedgerAction = Literal["entry", "balance", "account", "receipt", "receipts"]


@dataclass(slots=True)
Expand Down Expand Up @@ -499,6 +499,9 @@ class LedgerArgs:
alias: str | None = None
name: str | None = None
type: str | None = None
receipt_id: str | None = None
consumer_id: str | None = None
limit: int | None = None
metadata: dict[str, Any] | None = None


Expand All @@ -512,6 +515,100 @@ class LedgerResult:
raw: Any = None


# ── Agentic receipt (the Control Record) ───────────────────────────


@dataclass(slots=True)
class ReceiptException:
"""A settle-time policy divergence recorded on a receipt (OBSERVE mode) β€”
e.g. the settled amount/payee diverging from the bound quote."""

code: str
detail: str
at: str


@dataclass(slots=True)
class ReceiptMandate:
id: str
nonce: str
scope: str
currency: str
sig: str


@dataclass(slots=True)
class ReceiptQuote:
seller: str | None = None
resource: str | None = None
price_minor: int | None = None
payee: str | None = None
session_id: str | None = None
sig: str | None = None
at: str | None = None


@dataclass(slots=True)
class ReceiptPayment:
rail: str
amount_minor: int
attempt_id: str
money_moved: bool
provider: str | None = None
tx_id: str | None = None
at: str | None = None


@dataclass(slots=True)
class ReceiptDelivery:
result: str | None = None
proof: str | None = None
kind: str | None = None
nfe_chave: str | None = None
at: str | None = None


@dataclass(slots=True)
class AgenticReceipt:
"""The canonical agentic receipt β€” the Control Record of one spend.

Binds mandate -> quote -> payment -> delivery into a tamper-evident
chain (SHA-256 over the RFC 8785 canonical JSON of the records
present); ``receipt_sig`` is the consumer HMAC over ``chain``. The
structure is rail-agnostic; only the delivery proof artifact differs
(NF-e + Pix endToEndId for the fiscal path, the x402 resource
response for crypto). Returned by ``codespar_ledger`` action=receipt.
"""

receipt_id: str
state: Literal["paid", "exception", "delivered", "voided"]
mandate: ReceiptMandate
payment: ReceiptPayment
chain: str
receipt_sig: str
quote: ReceiptQuote | None = None
delivery: ReceiptDelivery | None = None
exceptions: list[ReceiptException] = field(default_factory=list)


@dataclass(slots=True)
class LedgerReceiptResult:
"""Result of ``codespar_ledger`` action=receipt. A missing receipt
returns ``found=False`` (not an error)."""

found: bool
receipt: AgenticReceipt | None = None
receipt_id: str | None = None


@dataclass(slots=True)
class LedgerReceiptsResult:
"""Result of ``codespar_ledger`` action=receipts."""

receipts: list[AgenticReceipt] = field(default_factory=list)
count: int = 0


# ── codespar_issue wire shape ──────────────────────────────────────


Expand Down
2 changes: 1 addition & 1 deletion packages/types/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@codespar/types",
"version": "0.10.14",
"version": "0.10.15",
"description": "Shared session interface contract for codespar runtimes",
"type": "module",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/testing/scenario-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* union, and the per-example groups a consumer drives completely. */
export const DEMO_SCENARIO_MANIFEST = {
/** Must equal this package's published version (see the lockstep test). */
version: "0.10.14",
version: "0.10.15",
/** Every shipped scenario `name` β€” the union of all `groups` (see the test). */
scenarios: [
"customer-data-rejection",
Expand Down
105 changes: 103 additions & 2 deletions packages/types/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,8 @@ export interface LedgerLeg {
}

export interface LedgerArgs {
/** entry | balance | account. */
action: "entry" | "balance" | "account";
/** entry | balance | account | receipt | receipts. */
action: "entry" | "balance" | "account" | "receipt" | "receipts";
/** Asset / currency code for entry + account (BRL, USD, USDC, ...). */
asset?: string;
/** Decimal places for the asset. Default 2 (fiat); JPY=0, most
Expand All @@ -400,6 +400,12 @@ export interface LedgerArgs {
/** Midaz account type (deposit, savings, external). Default deposit.
* action=account only. */
type?: string;
/** The agentic receipt id (rcpt_...) to read. Required for action=receipt. */
receipt_id?: string;
/** Whose receipts to list. Defaults to the session user. action=receipts. */
consumer_id?: string;
/** Max receipts to list (action=receipts, default 50). */
limit?: number;
/** Free-form metadata stored on the entry / account. */
metadata?: Record<string, unknown>;
}
Expand All @@ -416,6 +422,101 @@ export interface LedgerResult {
raw?: unknown;
}

/* ── Agentic receipt (the Control Record) ────────────────────── */

/** One settle-time policy divergence recorded on a receipt (OBSERVE mode) β€”
* e.g. the settled amount/payee diverging from the bound quote. */
export interface ReceiptException {
code: string;
detail: string;
at: string;
}

/**
* The canonical agentic receipt β€” the Control Record of one agentic spend.
* Binds four linked records into a tamper-evident chain so the spend proves
* not just that money moved, but that the agent APPROVED X, PAID X, and the
* order/resource was DELIVERED. Returned by codespar_ledger action=receipt.
* The receipt STRUCTURE is rail-agnostic; only the delivery proof artifact
* differs (NF-e + Pix endToEndId for the fiscal path, the x402 resource
* response + on-chain finality for crypto). `chain` is SHA-256 over the RFC
* 8785 canonical JSON of the records present; `receipt_sig` is the consumer
* HMAC over `chain` (the same trust root that signed the mandate).
*/
export interface AgenticReceipt {
receipt_id: string;
/** paid: payment sealed, no delivery yet. exception: a settle-time quote
* divergence was observed (still settled). delivered: a delivery record was
* folded in. voided: reversed. */
state: "paid" | "exception" | "delivered" | "voided";
mandate: {
id: string;
nonce: string;
/** The mandate purpose the spend was bound to. */
scope: string;
currency: string;
/** The consumer HMAC that signed the mandate. */
sig: string;
};
/** The offer the agent approved (seller / resource / price), bound + signed
* into the receipt. Null when no quote was provided. */
quote: {
seller: string | null;
resource: string | null;
price_minor: number | null;
payee: string | null;
session_id: string | null;
sig: string | null;
at: string | null;
} | null;
payment: {
/** pix | card | usdc-onchain | ... */
rail: string;
provider: string | null;
/** PSP tx id / Pix endToEndId / on-chain hash. */
tx_id: string | null;
amount_minor: number;
attempt_id: string;
/** True when real money moved (e.g. a Celcoin cash-out); false for a
* pending charge (e.g. a Mercado Pago QR). */
money_moved: boolean;
at: string | null;
};
/** Proof of what was received. Rail-specific: an order confirmation / NF-e
* for the fiscal (Pix) path, the x402 resource response for crypto. Null
* until a delivery record is folded in. */
delivery: {
/** confirmed | pending | failed. */
result: string | null;
proof: string | null;
/** order_confirmation | nfe | nfse | tracking | resource. */
kind: string | null;
nfe_chave: string | null;
at: string | null;
} | null;
/** SHA-256 over the canonical JSON of the records present:
* hash(mandate -> quote -> payment -> delivery). */
chain: string;
/** Consumer HMAC over `chain`. */
receipt_sig: string;
exceptions: ReceiptException[];
}

/** Result of codespar_ledger action=receipt (read one by receipt_id). A
* missing receipt returns found:false (not an error). */
export interface LedgerReceiptResult {
found: boolean;
receipt?: AgenticReceipt;
/** Echoed back when found is false. */
receipt_id?: string;
}

/** Result of codespar_ledger action=receipts (list a consumer's receipts). */
export interface LedgerReceiptsResult {
receipts: AgenticReceipt[];
count: number;
}

/* ── codespar_issue wire shape ───────────────────────────────── */

/**
Expand Down
Loading