@lucid-agents/pi-xgate 0.0.0-stage → 0.1.6
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/LICENSE +21 -0
- package/README.md +211 -2
- package/dist/codec.d.ts +34 -0
- package/dist/codec.js +510 -0
- package/dist/extension.d.ts +16 -0
- package/dist/extension.js +132 -0
- package/dist/generated/openai-model-capabilities.json +847 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/provider.d.ts +24 -0
- package/dist/provider.js +243 -0
- package/dist/taskmarket.d.ts +131 -0
- package/dist/taskmarket.js +275 -0
- package/dist/transport.d.ts +72 -0
- package/dist/transport.js +21 -0
- package/package.json +63 -4
- package/src/codec.ts +658 -0
- package/src/extension.ts +178 -0
- package/src/generated/openai-model-capabilities.json +847 -0
- package/src/index.ts +45 -0
- package/src/provider.ts +313 -0
- package/src/taskmarket.ts +484 -0
- package/src/transport.ts +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xgate contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,212 @@
|
|
|
1
|
-
#
|
|
1
|
+
# xgate provider for Pi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A Pi provider extension for wallet-paid inference, supported on Pi **1.1.0** and
|
|
4
|
+
**0.85.1**. It uses native
|
|
5
|
+
Anthropic Messages and structured OpenAI Responses through xgate. Pi executes its
|
|
6
|
+
own tools; xgate never executes local tools. Text, tool calls/results and supported
|
|
7
|
+
reasoning signatures survive the full agent loop.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
The release package is `@lucid-agents/pi-xgate`. It includes the client dependency
|
|
12
|
+
and the reviewed model-capability snapshot; the internal router contracts package
|
|
13
|
+
is not required. With Node 24.19+ (below 25) and Pi 1.1.0:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pi install npm:@lucid-agents/pi-xgate@0.1.6
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For repository development before a release is available:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pnpm --filter @lucid-agents/xgate-client build
|
|
23
|
+
pi install ./packages/pi-xgate
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Pi supplies `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent` as peers.
|
|
27
|
+
This release supports exactly 1.1.0 and 0.85.1 (the version TaskMarket's agent
|
|
28
|
+
runtime pins); other versions need their own check.
|
|
29
|
+
|
|
30
|
+
## Connect a wallet broker
|
|
31
|
+
|
|
32
|
+
Choose Tempo MPP, Base x402 batch settlement, or Solana x402 exact in the
|
|
33
|
+
[wallet service policy](../xgate-client/README.md#x402-configuration). Pi's provider
|
|
34
|
+
and tool loop are the same for each; payment selection and keys stay in the broker.
|
|
35
|
+
The new x402 client adapters have fixture coverage and still require separately
|
|
36
|
+
budgeted signed live qualification before production use.
|
|
37
|
+
|
|
38
|
+
Run the [wallet service](../xgate-client/README.md) outside the filesystem/process
|
|
39
|
+
access available to Pi tools. The extension reads a **broker token**, not a wallet
|
|
40
|
+
key. A broker running under the same user as unrestricted Pi shell tools does not
|
|
41
|
+
provide key isolation.
|
|
42
|
+
|
|
43
|
+
Use `/xgate setup` in Pi to set the broker URL and an absolute path to its token
|
|
44
|
+
file. Setup writes `~/.pi/agent/xgate.json` only if it does not already exist. To
|
|
45
|
+
change an existing setup, edit that owner-only file and use `/xgate refresh`.
|
|
46
|
+
For headless runs, set `XGATE_CONFIG` to an absolute owner-only JSON file:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"url": "http://127.0.0.1:4021",
|
|
51
|
+
"tokenFile": "/absolute/private/broker-token"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Both files must be regular, nonsymlink files with no group/other permissions.
|
|
56
|
+
Select a model through `/model`. Only the broker policy's models with compatible
|
|
57
|
+
live qualifications are offered. OpenAI requires the new
|
|
58
|
+
`openai-anonymous-tools-v2` router profile; an older server will expose no OpenAI
|
|
59
|
+
coding models. Anthropic requires `anthropic-anonymous-usage-v3` and uses only
|
|
60
|
+
`anthropic_no_cache` native Messages scopes.
|
|
61
|
+
An expired/revoked catalog scope cannot authorize payment just because Pi remembers
|
|
62
|
+
an earlier model list: the provider refreshes before each invocation and the wallet
|
|
63
|
+
client validates the actual quote again.
|
|
64
|
+
|
|
65
|
+
Commands:
|
|
66
|
+
|
|
67
|
+
- `/xgate status`: pending purchases and budget totals.
|
|
68
|
+
- `/xgate budget`: show the wallet service's fixed authorization caps.
|
|
69
|
+
- `/xgate receipts`: recent content-free payment records.
|
|
70
|
+
- `/xgate reconcile <invocation ID>`: inspect/reconcile the original purchase without
|
|
71
|
+
resubmitting inference or payment.
|
|
72
|
+
- `/xgate refresh`: reload private configuration and available models.
|
|
73
|
+
|
|
74
|
+
Pi cannot raise the service budget. Authorized ceilings remain consumed after
|
|
75
|
+
successful settlement even when most of a deposit is returned. A new budget is an
|
|
76
|
+
explicit wallet-operator action, never automatic rollover or a fresh journal.
|
|
77
|
+
|
|
78
|
+
## Host-injected transport
|
|
79
|
+
|
|
80
|
+
A host that owns payment, such as TaskMarket, builds the provider around its own
|
|
81
|
+
transport instead of the wallet broker. The supported entry point ships built
|
|
82
|
+
JavaScript and type declarations:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import {
|
|
86
|
+
createXgateExtension,
|
|
87
|
+
createXgateProvider,
|
|
88
|
+
type XgateTransport,
|
|
89
|
+
} from "@lucid-agents/pi-xgate";
|
|
90
|
+
|
|
91
|
+
const transport: XgateTransport = {
|
|
92
|
+
models: (signal) => hostCatalog(signal), // router catalog rows
|
|
93
|
+
purchase: (invocation, signal) => hostPurchase(invocation, signal),
|
|
94
|
+
};
|
|
95
|
+
// As a Pi extension: the broker configuration and `/xgate setup` are not used.
|
|
96
|
+
export default createXgateExtension({ transport });
|
|
97
|
+
// Or directly, e.g. for an agent's stream function.
|
|
98
|
+
const provider = await createXgateProvider(transport, {
|
|
99
|
+
sessionId: () => sessionId,
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`createXgateExtension({ transport })` also accepts a factory, called on load and on
|
|
104
|
+
`/xgate refresh`. `/xgate status`, `budget`, `receipts` and `reconcile` use the
|
|
105
|
+
transport's optional methods of the same names. The default extension
|
|
106
|
+
(`@lucid-agents/pi-xgate/extension`, and Pi's `pi.extensions` entry) keeps the
|
|
107
|
+
standalone `BrokerClient`.
|
|
108
|
+
|
|
109
|
+
The provider keeps catalog qualification, native codecs and invocation identity;
|
|
110
|
+
the transport only moves the frozen request and payment. A transport must send at
|
|
111
|
+
most one paid submission per `Invocation.id`, never retry a paid request, reject a
|
|
112
|
+
repeated ID rather than pay again, and resolve only with a verified result.
|
|
113
|
+
`BrokerClient` satisfies this contract.
|
|
114
|
+
|
|
115
|
+
### TaskMarket payer
|
|
116
|
+
|
|
117
|
+
`createTaskMarketTransport` adapts TaskMarket's x402 payer (`@taskmarket/x402-payer`
|
|
118
|
+
API v1). That package is private, so this one declares a structural
|
|
119
|
+
`TaskMarketPayer` (`request`, `status`, `reconcile`) and TaskMarket injects its
|
|
120
|
+
instance. The payer remains the only wallet, signer and payment journal; the
|
|
121
|
+
adapter keeps no payment state.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const transport = createTaskMarketTransport({
|
|
125
|
+
payer, // TaskMarket's X402Payer
|
|
126
|
+
origin: "https://api.xgate.run",
|
|
127
|
+
allocationId, // the agent session's inference allocation
|
|
128
|
+
nonInteractive: true,
|
|
129
|
+
balances: () => allocations.balances(), // optional, for display
|
|
130
|
+
});
|
|
131
|
+
const provider = await createXgateProvider(transport, {
|
|
132
|
+
sessionId: () => sessionId,
|
|
133
|
+
onPaid: (r) => show(r.payment), // network, scheme, asset, atomic amounts, channel
|
|
134
|
+
onPurchaseFailed: (f) => record(f), // original invocation ID, code, pending
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- The Pi invocation ID is the payer `invocationId`. A retried turn reuses it, so
|
|
139
|
+
the payer refuses to send it again (`invocation_duplicate`).
|
|
140
|
+
- The provider refreshes the router catalog (an unpaid `GET /v1/models`) before
|
|
141
|
+
every paid request. The payer's rail echoes the router quote binding and verifies
|
|
142
|
+
settlement.
|
|
143
|
+
- A result is accepted only with a `settled` charge, the Anonymous privacy
|
|
144
|
+
headers and a native JSON body. Every other outcome rejects with
|
|
145
|
+
`XgatePurchaseError`. It is `pending` unless the payer proved no unresolved
|
|
146
|
+
payment; unknown future charge states count as unverified.
|
|
147
|
+
- Pi gets a terminal `error`/`aborted` message naming the original invocation ID,
|
|
148
|
+
worded so Pi's transient-error retry does not fire. Only payer error codes reach
|
|
149
|
+
Pi, never payer messages. Hooks see only `content-type` and `cache-control`.
|
|
150
|
+
- Payment details are rail-neutral (`XgatePayment`): CAIP-2 network, scheme, asset,
|
|
151
|
+
payee, rail kind, authorized and charged atomic amounts, status, reference and,
|
|
152
|
+
for a single matching session deposit, its channel balance. Pi displays them
|
|
153
|
+
and never selects or configures a rail.
|
|
154
|
+
|
|
155
|
+
## Behavior and limits
|
|
156
|
+
|
|
157
|
+
Responses are **buffered**, not token-streamed over the network. After a complete
|
|
158
|
+
response and independently verified payment, the adapter emits Pi's normal text,
|
|
159
|
+
thinking and tool-call events. It never exposes partial tool calls before payment
|
|
160
|
+
verification. It emits one terminal event and honors cancellation; cancellation
|
|
161
|
+
can still leave a charged or unresolved purchase requiring status inspection.
|
|
162
|
+
|
|
163
|
+
There is no automatic paid POST retry. Stable invocation IDs bind retries to the
|
|
164
|
+
same ledger record even when a payload hook changes its output. Unresolved
|
|
165
|
+
purchases block new purchases. A lost result is not recoverable from xgate and
|
|
166
|
+
cannot be regenerated by replaying the same invocation. Keep Pi agent/provider
|
|
167
|
+
retry disabled for an initial deployment; the durable wallet guard is independent
|
|
168
|
+
of that setting. Cache warming is disabled for this provider.
|
|
169
|
+
|
|
170
|
+
The initial scope is text-only, with a maximum advertised output of 8,192 tokens
|
|
171
|
+
and a default request cap of 4,096. A lower Pi `models.json` `modelOverrides`
|
|
172
|
+
`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
|
|
173
|
+
`minimal`) and clamp the selected level through Pi's native model handling.
|
|
174
|
+
It rejects images, unsupported sampling/forced
|
|
175
|
+
tool settings, excessive schemas and incompatible thinking levels before paying.
|
|
176
|
+
OpenAI allows eight custom tools / 8 KiB of declarations; Anthropic allows 32 /
|
|
177
|
+
64 KiB. The router additionally validates tool/result identities and message bounds.
|
|
178
|
+
Opaque OpenAI reasoning reserves the reviewed full input ceiling because ciphertext
|
|
179
|
+
length cannot prove its decoded token count. Consequently its authorization hold
|
|
180
|
+
can be much larger than its eventual charge; the wallet cap still applies.
|
|
181
|
+
|
|
182
|
+
Pi's token-cost figures are supplier-rate estimates, excluding router markup and
|
|
183
|
+
success floors. `/xgate` status and receipts report actual atomic payment amounts.
|
|
184
|
+
The extension sends no Pi session ID, caller metadata, cache affinity key or wallet
|
|
185
|
+
credential to the inference provider. Payload hooks run before quoting and cannot
|
|
186
|
+
change the model, output cap or tool declarations. Response hooks receive sanitized
|
|
187
|
+
headers without payment/status capabilities.
|
|
188
|
+
|
|
189
|
+
Pi retains its own transcripts by default. That is separate from xgate's
|
|
190
|
+
application content-retention policy. Use Pi's `--no-session` option if local
|
|
191
|
+
transcript persistence is unwanted; financial reconciliation records are separate.
|
|
192
|
+
Changing models can discard incompatible opaque reasoning signatures, but preserves
|
|
193
|
+
text/tool history and consistent tool IDs. Removing a historical Anthropic tool
|
|
194
|
+
from the current tool set is rejected rather than silently losing that history.
|
|
195
|
+
|
|
196
|
+
## Tests and rollout
|
|
197
|
+
|
|
198
|
+
Run `pnpm pi:check` at the repository root. Tests instantiate Pi's real agent core,
|
|
199
|
+
execute a fixture tool, and verify the next request contains its exact result and
|
|
200
|
+
reasoning history. `pnpm packages:check` installs the packed tarballs with Pi 1.1.0
|
|
201
|
+
and 0.85.1 and runs each real Pi CLI through the mock broker and an injected
|
|
202
|
+
transport, type-checks a consumer of the supported exports, and runs the TaskMarket
|
|
203
|
+
payer loop suite (`test/installed-loop.test.ts`: continuation, qualification,
|
|
204
|
+
cancellation, duplicate and lost response) against each installed Pi. Separate wallet tests cover durable financial boundaries.
|
|
205
|
+
These are fixture tests. The [9 October staging campaign](../../docs/validation/pi-client-staging-20261009.md)
|
|
206
|
+
separately exercised the installed Pi CLI with live providers and test tokens.
|
|
207
|
+
That evidence is staging-only; it does not qualify production payment execution.
|
|
208
|
+
Installing the extension does not publish router qualifications or deploy any service.
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT. See [LICENSE](LICENSE).
|
package/dist/codec.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AssistantMessage, Message, Model, SimpleStreamOptions, Tool, Usage } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type Route } from "@lucid-agents/xgate-client/types";
|
|
3
|
+
export type Entry = {
|
|
4
|
+
model: Model<any>;
|
|
5
|
+
catalog: any;
|
|
6
|
+
route: Route;
|
|
7
|
+
efforts: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function catalogEntries(rows: any[]): Entry[];
|
|
10
|
+
/**
|
|
11
|
+
* Pi 1.1 passes a normalized transcript; Pi 0.85 passes `systemPrompt` and `tools` beside the
|
|
12
|
+
* messages. Structural, so published types do not name a type only one Pi version has.
|
|
13
|
+
*/
|
|
14
|
+
export type PiContext = {
|
|
15
|
+
messages: Message[];
|
|
16
|
+
systemPrompt?: string;
|
|
17
|
+
tools?: Tool[];
|
|
18
|
+
};
|
|
19
|
+
type Turn = Exclude<Message, {
|
|
20
|
+
role: "system";
|
|
21
|
+
}>;
|
|
22
|
+
export declare function transcript(context: PiContext): {
|
|
23
|
+
system: string;
|
|
24
|
+
tools: Tool[];
|
|
25
|
+
messages: Turn[];
|
|
26
|
+
};
|
|
27
|
+
export declare function encode(entry: Entry, context: PiContext, options?: SimpleStreamOptions): any;
|
|
28
|
+
export declare function decode(entry: Entry, body: any): {
|
|
29
|
+
content: AssistantMessage["content"];
|
|
30
|
+
usage: Usage;
|
|
31
|
+
stopReason: "stop" | "toolUse";
|
|
32
|
+
responseId: string;
|
|
33
|
+
};
|
|
34
|
+
export {};
|