@lucid-agents/pi-xgate
Wallet-paid OpenAI and Anthropic inference through xgate for the Pi coding agent
Package details
Install @lucid-agents/pi-xgate from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@lucid-agents/pi-xgate- Package
@lucid-agents/pi-xgate- Version
0.1.7- Published
- Oct 21, 2026
- Downloads
- 2,607/mo · 1,411/wk
- Author
- realm_lord
- License
- MIT
- Types
- extension
- Size
- 175.2 KB
- Dependencies
- 1 dependency · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/extension.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
xgate provider for Pi
A Pi provider extension for wallet-paid inference, supported on Pi 1.1.0 and 0.85.1. It uses native Anthropic Messages and structured OpenAI Responses through xgate. Pi executes its own tools; xgate never executes local tools. Text, tool calls/results and supported reasoning signatures survive the full agent loop.
Install
The release package is @lucid-agents/pi-xgate. It includes the client dependency
and the reviewed model-capability snapshot; the internal router contracts package
is not required. With Node 24.19 or later and Pi 1.1.0:
pi install npm:@lucid-agents/pi-xgate@0.1.7
For repository development before a release is available:
pnpm --filter @lucid-agents/xgate-client build
pi install ./packages/pi-xgate
Pi supplies @earendil-works/pi-ai and @earendil-works/pi-coding-agent as peers.
This release supports exactly 1.1.0 and 0.85.1 (the version TaskMarket's agent
runtime pins); other versions need their own check.
Connect a wallet broker
Choose Tempo MPP, Base x402 batch settlement, or Solana x402 exact in the wallet service policy. Pi's provider and tool loop are the same for each; payment selection and keys stay in the broker. The new x402 client adapters have fixture coverage and still require separately budgeted signed live qualification before production use.
Run the wallet service outside the filesystem/process access available to Pi tools. The extension reads a broker token, not a wallet key. A broker running under the same user as unrestricted Pi shell tools does not provide key isolation.
Use /xgate setup in Pi to set the broker URL and an absolute path to its token
file. Setup writes ~/.pi/agent/xgate.json only if it does not already exist. To
change an existing setup, edit that owner-only file and use /xgate refresh.
For headless runs, set XGATE_CONFIG to an absolute owner-only JSON file:
{
"url": "http://127.0.0.1:4021",
"tokenFile": "/absolute/private/broker-token"
}
Both files must be regular, nonsymlink files with no group/other permissions.
Select a model through /model. Only the broker policy's models with compatible
live qualifications are offered. OpenAI requires the new
openai-anonymous-tools-v2 router profile; an older server will expose no OpenAI
coding models. Anthropic requires anthropic-anonymous-usage-v3 native Messages
scopes qualified as anthropic_no_cache, anthropic_ephemeral_5m or
anthropic_ephemeral_1h.
Anthropic caches a prompt only at explicit cache_control breakpoints. On a
cache-qualified scope the provider marks, like Pi's direct Anthropic provider, the
last tool, the system prompt and the newest user turn (three of Anthropic's four
breakpoints), so each call in a conversation can read the prefix the previous call
wrote. Breakpoints are five-minute ephemeral writes; Pi's cacheRetention: "long"
selects one-hour writes only on an anthropic_ephemeral_1h scope, and
cacheRetention: "none" sends none. An anthropic_no_cache scope gets no
breakpoints, and cache usage it reports is rejected as an invalid result. The
breakpoints carry no session ID or other key: the provider cache is keyed by the
content prefix alone. Cache warming stays off, so no unpaid or extra paid call
is made to keep a cache alive.
An expired/revoked catalog scope cannot authorize payment just because Pi remembers
an earlier model list: the provider refreshes before each invocation and the wallet
client validates the actual quote again.
Commands:
/xgate status: pending purchases and budget totals./xgate budget: show the wallet service's fixed authorization caps./xgate receipts: recent content-free payment records./xgate reconcile <invocation ID>: inspect/reconcile the original purchase without resubmitting inference or payment./xgate refresh: reload private configuration and available models.
Pi cannot raise the service budget. Authorized ceilings remain consumed after successful settlement even when most of a deposit is returned. A new budget is an explicit wallet-operator action, never automatic rollover or a fresh journal.
Host-injected transport
A host that owns payment, such as TaskMarket, builds the provider around its own transport instead of the wallet broker. The supported entry point ships built JavaScript and type declarations:
import {
createXgateExtension,
createXgateProvider,
type XgateTransport,
} from "@lucid-agents/pi-xgate";
const transport: XgateTransport = {
models: (signal) => hostCatalog(signal), // router catalog rows
purchase: (invocation, signal) => hostPurchase(invocation, signal),
};
// As a Pi extension: the broker configuration and `/xgate setup` are not used.
export default createXgateExtension({ transport });
// Or directly, e.g. for an agent's stream function.
const provider = await createXgateProvider(transport, {
sessionId: () => sessionId,
});
createXgateExtension({ transport }) also accepts a factory, called on load and on
/xgate refresh. /xgate status, budget, receipts and reconcile use the
transport's optional methods of the same names. The default extension
(@lucid-agents/pi-xgate/extension, and Pi's pi.extensions entry) keeps the
standalone BrokerClient.
The provider keeps catalog qualification, native codecs and invocation identity;
the transport only moves the frozen request and payment. A transport must send at
most one paid submission per Invocation.id, never retry a paid request, reject a
repeated ID rather than pay again, and resolve only with a verified result.
BrokerClient satisfies this contract.
TaskMarket payer
createTaskMarketTransport adapts TaskMarket's x402 payer (@taskmarket/x402-payer
API v1). That package is private, so this one declares a structural
TaskMarketPayer (request, status, reconcile) and TaskMarket injects its
instance. The payer remains the only wallet, signer and payment journal; the
adapter keeps no payment state.
const transport = createTaskMarketTransport({
payer, // TaskMarket's X402Payer
origin: "https://api.xgate.run",
allocationId, // the agent session's inference allocation
nonInteractive: true,
balances: () => allocations.balances(), // optional, for display
});
const provider = await createXgateProvider(transport, {
sessionId: () => sessionId,
onPaid: (r) => show(r.payment), // network, scheme, asset, atomic amounts, channel
onPurchaseFailed: (f) => record(f), // original invocation ID, code, pending
});
- The Pi invocation ID is the payer
invocationId. A retried turn reuses it, so the payer refuses to send it again (invocation_duplicate). - The provider refreshes the router catalog (an unpaid
GET /v1/models) before every paid request. The payer's rail echoes the router quote binding and verifies settlement. - A result is accepted only with a
settledcharge, the Anonymous privacy headers and a native JSON body. Every other outcome rejects withXgatePurchaseError. It ispendingunless the payer proved no unresolved payment; unknown future charge states count as unverified. - Pi gets a terminal
error/abortedmessage naming the original invocation ID, worded so Pi's transient-error retry does not fire. Only payer error codes reach Pi, never payer messages. Hooks see onlycontent-typeandcache-control. - Payment details are rail-neutral (
XgatePayment): CAIP-2 network, scheme, asset, payee, rail kind, authorized and charged atomic amounts, status, reference and, for a single matching session deposit, its channel balance. Pi displays them and never selects or configures a rail.
Behavior and limits
Responses are buffered, not token-streamed over the network. After a complete response and independently verified payment, the adapter emits Pi's normal text, thinking and tool-call events. It never exposes partial tool calls before payment verification. It emits one terminal event and honors cancellation; cancellation can still leave a charged or unresolved purchase requiring status inspection.
There is no automatic paid POST retry. Stable invocation IDs bind retries to the same ledger record even when a payload hook changes its output. Unresolved purchases block new purchases. A lost result is not recoverable from xgate and cannot be regenerated by replaying the same invocation. Keep Pi agent/provider retry disabled for an initial deployment; the durable wallet guard is independent of that setting. Cache warming is disabled for this provider.
The initial scope is text-only, with a maximum advertised output of 8,192 tokens
and a default request cap of 4,096. A lower Pi models.json modelOverrides
maxTokens value lowers that default; it cannot raise the live catalog limit. Pi thinking controls expose the reviewed levels (GPT-5 Mini currently permits only
minimal) and clamp the selected level through Pi's native model handling.
It rejects images, unsupported sampling/forced
tool settings, excessive schemas and incompatible thinking levels before paying.
OpenAI allows eight custom tools / 8 KiB of declarations; Anthropic allows 32 /
64 KiB. The router additionally validates tool/result identities and message bounds.
Opaque OpenAI reasoning reserves the reviewed full input ceiling because ciphertext
length cannot prove its decoded token count. Consequently its authorization hold
can be much larger than its eventual charge; the wallet cap still applies.
Pi's token-cost figures are supplier-rate estimates, excluding router markup and
success floors. /xgate status and receipts report actual atomic payment amounts.
The extension sends no Pi session ID, caller metadata, cache affinity key or wallet
credential to the inference provider. Payload hooks run before quoting and cannot
change the model, output cap or tool declarations. Response hooks receive sanitized
headers without payment/status capabilities.
Pi retains its own transcripts by default. That is separate from xgate's
application content-retention policy. Use Pi's --no-session option if local
transcript persistence is unwanted; financial reconciliation records are separate.
Changing models can discard incompatible opaque reasoning signatures, but preserves
text/tool history and consistent tool IDs. Removing a historical Anthropic tool
from the current tool set is rejected rather than silently losing that history.
Tests and rollout
Run pnpm pi:check at the repository root. Tests instantiate Pi's real agent core,
execute a fixture tool, and verify the next request contains its exact result and
reasoning history. pnpm packages:check installs the packed tarballs with Pi 1.1.0
and 0.85.1 and runs each real Pi CLI through the mock broker and an injected
transport, type-checks a consumer of the supported exports, and runs the TaskMarket
payer loop suite (test/installed-loop.test.ts: continuation, qualification,
cancellation, duplicate and lost response) against each installed Pi. Separate wallet tests cover durable financial boundaries.
These are fixture tests. The 9 October staging campaign
separately exercised the installed Pi CLI with live providers and test tokens.
That evidence is staging-only; it does not qualify production payment execution.
Installing the extension does not publish router qualifications or deploy any service.
License
MIT. See LICENSE.