@xagent/xpense— Agentic Payment Stack. Inject payment capabilities into agents; emit structured, policy-gated, auditable Payment Intents (x402 / mpp), settled through the xerpaai-go on-chain API.
Status: v0.1.0 · ESM library + xpense CLI · published @xagent scope.
Turn "an agent wants to spend money" into a validated, budget-checked, auditable Payment Intent before anything settles.
- Target users: developers building autonomous/agentic systems that must spend money (API calls, SaaS, on-chain) under human-defined guardrails.
- Core value: money is never a JS float; every spend passes a policy engine; every intent carries an audit trail; nothing settles unless
mode = live. - Out of scope (today): custody / key management, fiat rails, multi-tenant accounting, a hosted dashboard.
| Command | Purpose |
|---|---|
npm run build |
tsc -p tsconfig.build.json → dist/ |
npm run lint |
tsc --noEmit type-check (no separate linter) |
npm test |
vitest run — full suite |
npx vitest run <file> |
single test file |
npm run example:governance |
governance limits scenario (reject / approval) |
npm run example:settlement |
settlement payloads to xerpaai-go (mock) |
npm run login:smoke |
dist/examples/auth-login.js auth smoke |
xpense login |
CLI OAuth (loopback | device | paste) |
Layers narrate the spend lifecycle: agent → intent → governance → settlement, with access/ supplying the JWT that settlement needs.
src/
agent/ Xpense facade · capabilities (tool registry) · inject · ledger · default-store · tooling
intent/ types · builder (fluent) · validate · lifecycle (state machine)
governance/ policy (per-currency bigint budget engine) · gate (approval + revoke) · approval
settlement/ onchainos (typed client over xerpaai-go /user/onchainos/*) · submit (emit) · pay-fetch (402)
access/ login · loopback/device/paste OAuth · credentials · token refresh · exchange
money.ts viem parseUnits/formatUnits — exact bigint money
http.ts thin fetch wrapper (envelope, timeout, bearer)
config.ts env-overridable config resolver
cli.ts bin entry
examples/ runnable end-to-end examples
test/ vitest suite — mirrors src/ (source tree stays free of test files)
e2e/ playwright smoke
Business-layered, not fragmented: one directory per domain concern. Classes only for stateful units (Xpense, PolicyEngine, GovernanceGate, PaymentIntentBuilder, PaymentSession, OnchainosGateway); everything else is small free functions. xpense calls xerpaai-go — it never re-implements on-chain logic, and endpoints xerpaai-go has not built are not stubbed here.
- ESM only,
type: module, Node>=18.17. Source uses.tsimport specifiers; TypeScript rewrites them to.json emit (rewriteRelativeImportExtensions+allowImportingTsExtensions). - No comments — names carry intent; if a line needs a comment, rename or extract.
- Money is exact: amounts are decimal strings (
"12.50") at the boundary; all arithmetic viaviemparseUnits→bigint.numberis forbidden for money. - IDs, not slugs for any identifier (
pi_…,sess_…). - Builder/fluent for construction; pure validators returning
string[]errors. - Inject config & clocks (
now: () => number) rather than reading globals — keeps logic testable. - No proxy/networking shims in code — proxying is the operator's environment concern.
- Runner: vitest. Tests live under top-level
test/, mirroringsrc/— the source tree stays free of*.test.ts(include: ["test/**/*.test.ts"]). - TDD is mandatory for money & auth — write the failing test that reproduces the defect first (red), then fix (green). No "ship first, fix later" on payment/auth paths.
- Money tests must prove precision: e.g.
0.1 + 0.2fits a0.3budget; per-currency isolation; daily reset across a day boundary;reserveatomicity vs TOCTOU. - Network is mocked at the
http.tsboundary; no live calls in tests. - Gate before any publish:
lint+test+buildall green (prepublishOnly).
Always
- Keep money in
bigintvia viem; validate Payment Intents before submit. This includes the session ledger (PaymentSession.total) — neverNumber()an amount. - Enforce budgets per-currency; default
mode = dry-run. All modes run the policy engine;dry-run/mockaccumulate spend within the instance so a simulation surfaces the limits it would hit. Onlymode = livesettles real money. PolicyEngineis in-process, single-runtime — itsreserve/releaseMaps do not coordinate across workers/processes. Use exactly one budget flow per spend:reserve→release(atomic, preferred) orevaluate→commit(legacy). Never pairreserve+commit— that double-counts.- Store credentials
0600under~/.config/xagent; auto-refresh access tokens. - Test-first on anything touching auth, payments, or on-chain.
Ask first
- Changing any public API (exports in
index.ts) or the Payment IntentschemaVersion. - Switching default
modetolive, or adding a runtime dependency. - Behavior changes to
pay-fetch(e.g. auto-retry after settlement — currently a side-effect-only callback, open decision).
Never
- Float math for money; secrets committed to the repo;
git commit/pushwithout an explicit instruction. - Network/proxy logic baked into library code.
- Couple xpense to a specific agent framework — it emits intents, it does not own the agent loop.