@lucid-agents/pi-xgate 0.1.8 → 0.1.10
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/README.md +54 -11
- package/dist/extension.d.ts +3 -0
- package/dist/extension.js +8 -1
- package/dist/generated/openai-model-capabilities.json +23 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/provider.d.ts +5 -0
- package/dist/provider.js +7 -1
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js +13 -4
- package/package.json +8 -8
- package/src/extension.ts +9 -6
- package/src/generated/openai-model-capabilities.json +23 -0
- package/src/index.ts +4 -1
- package/src/provider.ts +16 -1
- package/src/transport.ts +14 -3
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# xgate provider for Pi
|
|
2
2
|
|
|
3
|
-
A Pi provider extension for wallet-paid inference, supported on Pi **1.
|
|
4
|
-
**0.85.1**. It uses native
|
|
3
|
+
A Pi provider extension for wallet-paid inference, supported on Pi **1.5.2**,
|
|
4
|
+
**1.1.0** and **0.85.1**. It uses native
|
|
5
5
|
Anthropic Messages and structured OpenAI Responses through xgate. Pi executes its
|
|
6
6
|
own tools; xgate never executes local tools. Text, tool calls/results and supported
|
|
7
7
|
reasoning signatures survive the full agent loop.
|
|
@@ -10,10 +10,10 @@ reasoning signatures survive the full agent loop.
|
|
|
10
10
|
|
|
11
11
|
The release package is `@lucid-agents/pi-xgate`. It includes the client dependency
|
|
12
12
|
and the reviewed model-capability snapshot; the internal router contracts package
|
|
13
|
-
is not required. With Node 24.19 or later and Pi 1.
|
|
13
|
+
is not required. With Node 24.19 or later and Pi 1.5.2:
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
pi install npm:@lucid-agents/pi-xgate@0.1.
|
|
16
|
+
pi install npm:@lucid-agents/pi-xgate@0.1.10
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
For repository development before a release is available:
|
|
@@ -24,8 +24,9 @@ pi install ./packages/pi-xgate
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
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.
|
|
27
|
+
This release supports exactly 1.5.2, 1.1.0 and 0.85.1 (the version TaskMarket's agent
|
|
28
|
+
runtime pins); other versions need their own check. `pi install` does not enforce
|
|
29
|
+
the peer range, so on another Pi version the extension loads unchecked.
|
|
29
30
|
|
|
30
31
|
## Connect a wallet broker
|
|
31
32
|
|
|
@@ -81,9 +82,50 @@ Commands:
|
|
|
81
82
|
- `/xgate budget`: show the wallet service's fixed authorization caps.
|
|
82
83
|
- `/xgate receipts`: recent content-free payment records.
|
|
83
84
|
- `/xgate reconcile <invocation ID>`: inspect/reconcile the original purchase without
|
|
84
|
-
resubmitting inference or payment.
|
|
85
|
+
resubmitting inference or payment. If it reports `authorization_still_live`, the
|
|
86
|
+
router refused the request before funding but the signed Tempo or Base
|
|
87
|
+
authorization has not provably expired yet: run the same command again after it
|
|
88
|
+
expires (Tempo signs about 25 seconds ahead and must then be finalized; Base signs
|
|
89
|
+
up to 120 seconds ahead and must then reach the policy's confirmation level). Do
|
|
90
|
+
not start a replacement purchase. Other errors and their actions are in the wallet
|
|
91
|
+
client's refused before funding sections for
|
|
92
|
+
[Tempo](../xgate-client/README.md#tempo-purchases-refused-before-funding) and
|
|
93
|
+
[Base](../xgate-client/README.md#base-purchases-refused-before-funding).
|
|
85
94
|
- `/xgate refresh`: reload private configuration and available models.
|
|
86
95
|
|
|
96
|
+
### Rate limits
|
|
97
|
+
|
|
98
|
+
On Tempo the router sponsors the network fee for each purchase's payment channel and
|
|
99
|
+
limits those openings: per wallet **4 a minute** and **13 an hour** (2.00 of
|
|
100
|
+
reserved fee at the current 0.15 cap), with at most 3 unconfirmed at once; across
|
|
101
|
+
all wallets 30 a minute and 66 an hour. Every model call is one purchase and an
|
|
102
|
+
agent turn makes two or more, so an active session reaches these in normal use.
|
|
103
|
+
The full table and what each limit bounds are in the wallet client's
|
|
104
|
+
[Tempo rate limits](../xgate-client/README.md#tempo-rate-limits).
|
|
105
|
+
|
|
106
|
+
A call over a limit is refused before any funds move. The turn ends with an error
|
|
107
|
+
that says it was a rate limit and how long the router suggests waiting, for example:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
Xgate: rate_limited_requires_reconciliation. The router refused this purchase with
|
|
111
|
+
a rate limit and suggests waiting 32 seconds before a new one. Reconcile this
|
|
112
|
+
purchase first; never resend it. Billing invocation: 3f…c1.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The status line shows the same two steps:
|
|
116
|
+
`xgate rate limited (unresolved): /xgate reconcile <invocation ID>, then wait 32s before a new request`.
|
|
117
|
+
|
|
118
|
+
1. Run `/xgate reconcile <invocation ID>`. The wallet finishes the purchase at a
|
|
119
|
+
charge of zero only after it has proven on chain that the signed authorization
|
|
120
|
+
expired unused; until then the command reports `authorization_still_live` (run
|
|
121
|
+
it again a little later).
|
|
122
|
+
2. Send the request again as a new turn once the wait has passed. It is a new
|
|
123
|
+
purchase with a new quote.
|
|
124
|
+
|
|
125
|
+
Pi does not retry the turn by itself, and the provider never resends a paid
|
|
126
|
+
request. A host can read the wait from `onPurchaseFailed` as `retryAfterSeconds`.
|
|
127
|
+
A host transport reports one by passing it to `XgatePurchaseError`.
|
|
128
|
+
|
|
87
129
|
Pi cannot raise the service budget. Authorized ceilings remain consumed after
|
|
88
130
|
successful settlement even when most of a deposit is returned. A new budget is an
|
|
89
131
|
explicit wallet-operator action, never automatic rollover or a fresh journal.
|
|
@@ -144,7 +186,7 @@ const transport = createTaskMarketTransport({
|
|
|
144
186
|
const provider = await createXgateProvider(transport, {
|
|
145
187
|
sessionId: () => sessionId,
|
|
146
188
|
onPaid: (r) => show(r.payment), // network, scheme, asset, atomic amounts, channel
|
|
147
|
-
onPurchaseFailed: (f) => record(f), // original invocation ID, code, pending
|
|
189
|
+
onPurchaseFailed: (f) => record(f), // original invocation ID, code, pending, retryAfterSeconds
|
|
148
190
|
});
|
|
149
191
|
```
|
|
150
192
|
|
|
@@ -210,13 +252,14 @@ from the current tool set is rejected rather than silently losing that history.
|
|
|
210
252
|
|
|
211
253
|
Run `pnpm pi:check` at the repository root. Tests instantiate Pi's real agent core,
|
|
212
254
|
execute a fixture tool, and verify the next request contains its exact result and
|
|
213
|
-
reasoning history. `pnpm packages:check` installs the packed tarballs with Pi 1.
|
|
214
|
-
and 0.85.1 and runs each real Pi CLI through the mock broker and an injected
|
|
255
|
+
reasoning history. `pnpm packages:check` installs the packed tarballs with Pi 1.5.2,
|
|
256
|
+
1.1.0 and 0.85.1 and runs each real Pi CLI through the mock broker and an injected
|
|
215
257
|
transport, type-checks a consumer of the supported exports, and runs the TaskMarket
|
|
216
258
|
payer loop suite (`test/installed-loop.test.ts`: continuation, qualification,
|
|
217
259
|
cancellation, duplicate and lost response) against each installed Pi. Separate wallet tests cover durable financial boundaries.
|
|
218
260
|
These are fixture tests. The [9 October staging campaign](../../docs/validation/pi-client-staging-20261009.md)
|
|
219
|
-
separately exercised the installed Pi CLI with live providers and test tokens
|
|
261
|
+
separately exercised the installed Pi 1.1.0 CLI with live providers and test tokens;
|
|
262
|
+
Pi 1.5.2 and 0.85.1 have fixture coverage only.
|
|
220
263
|
That evidence is staging-only; it does not qualify production payment execution.
|
|
221
264
|
Installing the extension does not publish router qualifications or deploy any service.
|
|
222
265
|
|
package/dist/extension.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type XgatePurchaseFailure } from "./provider.ts";
|
|
2
3
|
import type { XgatePayment, XgateTransport } from "./transport.ts";
|
|
3
4
|
export interface XgateExtensionOptions {
|
|
4
5
|
/**
|
|
@@ -10,6 +11,8 @@ export interface XgateExtensionOptions {
|
|
|
10
11
|
}
|
|
11
12
|
/** Rail-neutral: atomic units of the named asset; decimals are not assumed. */
|
|
12
13
|
export declare function paymentStatus(p: XgatePayment): string;
|
|
14
|
+
/** What to do about a purchase that ended without a verified result. */
|
|
15
|
+
export declare function failureStatus(f: XgatePurchaseFailure): string;
|
|
13
16
|
/** Pi extension factory. The default export is the standalone wallet-broker extension. */
|
|
14
17
|
export declare function createXgateExtension(options?: XgateExtensionOptions): (pi: ExtensionAPI) => Promise<void>;
|
|
15
18
|
declare const _default: (pi: ExtensionAPI) => Promise<void>;
|
package/dist/extension.js
CHANGED
|
@@ -28,6 +28,13 @@ export function paymentStatus(p) {
|
|
|
28
28
|
: "";
|
|
29
29
|
return `xgate paid ${p.chargedAtomic ?? "?"}/${p.authorizedAtomic} ${p.asset} on ${p.network} (${p.scheme})${left}`;
|
|
30
30
|
}
|
|
31
|
+
/** What to do about a purchase that ended without a verified result. */
|
|
32
|
+
export function failureStatus(f) {
|
|
33
|
+
const state = f.pending ? "unresolved" : "failed";
|
|
34
|
+
return f.retryAfterSeconds === undefined
|
|
35
|
+
? `xgate ${state}: ${f.invocationId}`
|
|
36
|
+
: `xgate rate limited (${state}): ${f.pending ? `/xgate reconcile ${f.invocationId}, then ` : ""}wait ${f.retryAfterSeconds}s before a new request`;
|
|
37
|
+
}
|
|
31
38
|
/** Pi extension factory. The default export is the standalone wallet-broker extension. */
|
|
32
39
|
export function createXgateExtension(options = {}) {
|
|
33
40
|
return (pi) => register(pi, options);
|
|
@@ -49,7 +56,7 @@ async function register(pi, options) {
|
|
|
49
56
|
onPaid: (r) => context?.ui.setStatus("xgate", r.payment
|
|
50
57
|
? paymentStatus(r.payment)
|
|
51
58
|
: `xgate paid ${(Number(r.settlement.chargedAtomic) / 1e6).toFixed(6)}`),
|
|
52
|
-
onPurchaseFailed: (f) => context?.ui.setStatus("xgate",
|
|
59
|
+
onPurchaseFailed: (f) => context?.ui.setStatus("xgate", failureStatus(f)),
|
|
53
60
|
});
|
|
54
61
|
pi.registerProvider(provider);
|
|
55
62
|
broker = next;
|
|
@@ -843,5 +843,28 @@
|
|
|
843
843
|
"gpt-6.1-sol"
|
|
844
844
|
],
|
|
845
845
|
"chat_tools": "unsupported"
|
|
846
|
+
},
|
|
847
|
+
"gpt-6.1-terra": {
|
|
848
|
+
"canonical": "gpt-6.1-terra",
|
|
849
|
+
"price_label": "gpt-6.1-terra",
|
|
850
|
+
"operations": [
|
|
851
|
+
"chat",
|
|
852
|
+
"responses"
|
|
853
|
+
],
|
|
854
|
+
"reasoning_efforts": [
|
|
855
|
+
"none",
|
|
856
|
+
"low",
|
|
857
|
+
"medium",
|
|
858
|
+
"high",
|
|
859
|
+
"xhigh",
|
|
860
|
+
"max"
|
|
861
|
+
],
|
|
862
|
+
"cache": "write_priced",
|
|
863
|
+
"tools": true,
|
|
864
|
+
"chat_legacy_max_tokens": false,
|
|
865
|
+
"response_models": [
|
|
866
|
+
"gpt-6.1-terra"
|
|
867
|
+
],
|
|
868
|
+
"chat_tools": "unsupported"
|
|
846
869
|
}
|
|
847
870
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createXgateProvider, invocationId, type Broker, type XgateProviderOptions, type XgatePurchaseFailure, } from "./provider.ts";
|
|
2
2
|
export { XgatePurchaseError, type XgateChannelBalance, type XgatePayment, type XgatePurchaseResult, type XgateResponseHead, type XgateTransport, } from "./transport.ts";
|
|
3
3
|
export { createXgateExtension, type XgateExtensionOptions, } from "./extension.ts";
|
|
4
|
-
export { XgateError } from "@lucid-agents/xgate-client/types";
|
|
4
|
+
export { XgateError, XgateRateLimitedError, } from "@lucid-agents/xgate-client/types";
|
|
5
5
|
export type { Invocation, PurchaseResult, Route, Settlement, } from "@lucid-agents/xgate-client/types";
|
|
6
6
|
export { createTaskMarketTransport, type TaskMarketChargeStatus, type TaskMarketFundingBalance, type TaskMarketInvocationState, type TaskMarketInvocationStatus, type TaskMarketPayCallOptions, type TaskMarketPayErrorCode, type TaskMarketPayer, type TaskMarketPayerLimits, type TaskMarketPayRequest, type TaskMarketPayResponse, type TaskMarketPayResult, type TaskMarketPaymentSummary, type TaskMarketPaymentTerms, type TaskMarketTransportOptions, } from "./taskmarket.ts";
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
export { createXgateProvider, invocationId, } from "./provider.js";
|
|
4
4
|
export { XgatePurchaseError, } from "./transport.js";
|
|
5
5
|
export { createXgateExtension, } from "./extension.js";
|
|
6
|
-
export { XgateError } from "@lucid-agents/xgate-client/types";
|
|
6
|
+
export { XgateError, XgateRateLimitedError, } from "@lucid-agents/xgate-client/types";
|
|
7
7
|
export { createTaskMarketTransport, } from "./taskmarket.js";
|
package/dist/provider.d.ts
CHANGED
|
@@ -18,6 +18,11 @@ export interface XgatePurchaseFailure {
|
|
|
18
18
|
/** False only when the transport proved no unresolved payment. */
|
|
19
19
|
pending: boolean;
|
|
20
20
|
payment?: XgatePayment;
|
|
21
|
+
/**
|
|
22
|
+
* Set when the payee refused the purchase with a rate limit: its suggested wait, in
|
|
23
|
+
* seconds, before a new purchase. Reconciling this invocation still comes first.
|
|
24
|
+
*/
|
|
25
|
+
retryAfterSeconds?: number;
|
|
21
26
|
}
|
|
22
27
|
/** Stable across automatic retries and payload hooks; a changed paid payload cannot claim a new intent. */
|
|
23
28
|
export declare function invocationId(model: string, context: PiContext, session: string): string;
|
package/dist/provider.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { createAssistantMessageEventStream, } from "@earendil-works/pi-ai";
|
|
3
|
-
import { XgateError } from "@lucid-agents/xgate-client/types";
|
|
3
|
+
import { XgateError, XgateRateLimitedError, } from "@lucid-agents/xgate-client/types";
|
|
4
4
|
import { catalogEntries, encode, decode } from "./codec.js";
|
|
5
5
|
import { XgatePurchaseError, } from "./transport.js";
|
|
6
6
|
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
@@ -188,12 +188,18 @@ export async function createXgateProvider(transport, { sessionId: identity = ()
|
|
|
188
188
|
// After a verified result the charge is known even if Pi discards the content.
|
|
189
189
|
const payment = paid?.payment ??
|
|
190
190
|
(error instanceof XgatePurchaseError ? error.payment : undefined);
|
|
191
|
+
const wait = paid === undefined &&
|
|
192
|
+
(error instanceof XgateRateLimitedError ||
|
|
193
|
+
error instanceof XgatePurchaseError)
|
|
194
|
+
? error.retryAfterSeconds
|
|
195
|
+
: undefined;
|
|
191
196
|
const failure = {
|
|
192
197
|
invocationId: attempted,
|
|
193
198
|
code: error instanceof XgateError ? error.code : "purchase_failed",
|
|
194
199
|
pending: paid === undefined &&
|
|
195
200
|
!(error instanceof XgatePurchaseError && !error.pending),
|
|
196
201
|
...(payment ? { payment } : {}),
|
|
202
|
+
...(wait !== undefined ? { retryAfterSeconds: wait } : {}),
|
|
197
203
|
};
|
|
198
204
|
// The original ID is the only reconciliation key. "Billing" also keeps Pi's transient
|
|
199
205
|
// error classifier from retrying a message whose hex ID happens to contain "500".
|
package/dist/transport.d.ts
CHANGED
|
@@ -47,7 +47,9 @@ export declare class XgatePurchaseError extends XgateError {
|
|
|
47
47
|
readonly invocationId: string;
|
|
48
48
|
readonly pending: boolean;
|
|
49
49
|
readonly payment?: XgatePayment;
|
|
50
|
-
|
|
50
|
+
/** The payee's rate-limit hint, in seconds, when that is why the purchase ended. */
|
|
51
|
+
readonly retryAfterSeconds?: number;
|
|
52
|
+
constructor(code: string, invocationId: string, pending: boolean, payment?: XgatePayment, retryAfterSeconds?: number);
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* Moves one frozen Pi request to xgate and returns the verified result. The provider owns
|
package/dist/transport.js
CHANGED
|
@@ -7,15 +7,24 @@ export class XgatePurchaseError extends XgateError {
|
|
|
7
7
|
invocationId;
|
|
8
8
|
pending;
|
|
9
9
|
payment;
|
|
10
|
-
|
|
10
|
+
/** The payee's rate-limit hint, in seconds, when that is why the purchase ended. */
|
|
11
|
+
retryAfterSeconds;
|
|
12
|
+
constructor(code, invocationId, pending, payment, retryAfterSeconds) {
|
|
11
13
|
super(code);
|
|
12
14
|
this.name = "XgatePurchaseError";
|
|
13
|
-
|
|
14
|
-
?
|
|
15
|
-
: `
|
|
15
|
+
const limited = retryAfterSeconds === undefined
|
|
16
|
+
? ""
|
|
17
|
+
: ` The payee rate limited it and suggests waiting ${retryAfterSeconds} seconds before a new purchase.`;
|
|
18
|
+
this.message =
|
|
19
|
+
(pending
|
|
20
|
+
? `Xgate: ${code}. The payment outcome is unknown; do not retry. Reconcile it before another purchase.`
|
|
21
|
+
: `Xgate: ${code}. Inspect the payment status before another purchase.`) +
|
|
22
|
+
limited;
|
|
16
23
|
this.invocationId = invocationId;
|
|
17
24
|
this.pending = pending;
|
|
18
25
|
if (payment)
|
|
19
26
|
this.payment = payment;
|
|
27
|
+
if (retryAfterSeconds !== undefined)
|
|
28
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
20
29
|
}
|
|
21
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucid-agents/pi-xgate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"LICENSE"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@lucid-agents/xgate-client": "0.1.
|
|
35
|
+
"@lucid-agents/xgate-client": "0.1.10"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@earendil-works/pi-ai": "0.85.1 || 1.1.0",
|
|
39
|
-
"@earendil-works/pi-coding-agent": "0.85.1 || 1.1.0"
|
|
38
|
+
"@earendil-works/pi-ai": "0.85.1 || 1.1.0 || 1.5.2",
|
|
39
|
+
"@earendil-works/pi-coding-agent": "0.85.1 || 1.1.0 || 1.5.2"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=24.19"
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"registry": "https://registry.npmjs.org"
|
|
57
57
|
},
|
|
58
58
|
"license": "MIT",
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "4b2e77819216a41c265171bdaae9291f9b129037",
|
|
60
60
|
"xgateRelease": {
|
|
61
|
-
"version": "v0.1.
|
|
62
|
-
"sourceSha": "
|
|
63
|
-
"candidateId": "rc-
|
|
61
|
+
"version": "v0.1.10",
|
|
62
|
+
"sourceSha": "4b2e77819216a41c265171bdaae9291f9b129037",
|
|
63
|
+
"candidateId": "rc-4b2e77819216-43169339829-1"
|
|
64
64
|
}
|
|
65
65
|
}
|
package/src/extension.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { XgateError } from "@lucid-agents/xgate-client/types";
|
|
|
7
7
|
import { readFileSync, lstatSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { dirname, isAbsolute, join } from "node:path";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
|
-
import { createXgateProvider } from "./provider.ts";
|
|
10
|
+
import { createXgateProvider, type XgatePurchaseFailure } from "./provider.ts";
|
|
11
11
|
import type { XgatePayment, XgateTransport } from "./transport.ts";
|
|
12
12
|
export interface XgateExtensionOptions {
|
|
13
13
|
/**
|
|
@@ -42,6 +42,13 @@ export function paymentStatus(p: XgatePayment): string {
|
|
|
42
42
|
: "";
|
|
43
43
|
return `xgate paid ${p.chargedAtomic ?? "?"}/${p.authorizedAtomic} ${p.asset} on ${p.network} (${p.scheme})${left}`;
|
|
44
44
|
}
|
|
45
|
+
/** What to do about a purchase that ended without a verified result. */
|
|
46
|
+
export function failureStatus(f: XgatePurchaseFailure): string {
|
|
47
|
+
const state = f.pending ? "unresolved" : "failed";
|
|
48
|
+
return f.retryAfterSeconds === undefined
|
|
49
|
+
? `xgate ${state}: ${f.invocationId}`
|
|
50
|
+
: `xgate rate limited (${state}): ${f.pending ? `/xgate reconcile ${f.invocationId}, then ` : ""}wait ${f.retryAfterSeconds}s before a new request`;
|
|
51
|
+
}
|
|
45
52
|
/** Pi extension factory. The default export is the standalone wallet-broker extension. */
|
|
46
53
|
export function createXgateExtension(
|
|
47
54
|
options: XgateExtensionOptions = {},
|
|
@@ -74,11 +81,7 @@ async function register(
|
|
|
74
81
|
? paymentStatus(r.payment)
|
|
75
82
|
: `xgate paid ${(Number(r.settlement.chargedAtomic) / 1e6).toFixed(6)}`,
|
|
76
83
|
),
|
|
77
|
-
onPurchaseFailed: (f) =>
|
|
78
|
-
context?.ui.setStatus(
|
|
79
|
-
"xgate",
|
|
80
|
-
`xgate ${f.pending ? "unresolved" : "failed"}: ${f.invocationId}`,
|
|
81
|
-
),
|
|
84
|
+
onPurchaseFailed: (f) => context?.ui.setStatus("xgate", failureStatus(f)),
|
|
82
85
|
});
|
|
83
86
|
pi.registerProvider(provider);
|
|
84
87
|
broker = next;
|
|
@@ -843,5 +843,28 @@
|
|
|
843
843
|
"gpt-6.1-sol"
|
|
844
844
|
],
|
|
845
845
|
"chat_tools": "unsupported"
|
|
846
|
+
},
|
|
847
|
+
"gpt-6.1-terra": {
|
|
848
|
+
"canonical": "gpt-6.1-terra",
|
|
849
|
+
"price_label": "gpt-6.1-terra",
|
|
850
|
+
"operations": [
|
|
851
|
+
"chat",
|
|
852
|
+
"responses"
|
|
853
|
+
],
|
|
854
|
+
"reasoning_efforts": [
|
|
855
|
+
"none",
|
|
856
|
+
"low",
|
|
857
|
+
"medium",
|
|
858
|
+
"high",
|
|
859
|
+
"xhigh",
|
|
860
|
+
"max"
|
|
861
|
+
],
|
|
862
|
+
"cache": "write_priced",
|
|
863
|
+
"tools": true,
|
|
864
|
+
"chat_legacy_max_tokens": false,
|
|
865
|
+
"response_models": [
|
|
866
|
+
"gpt-6.1-terra"
|
|
867
|
+
],
|
|
868
|
+
"chat_tools": "unsupported"
|
|
846
869
|
}
|
|
847
870
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,7 +19,10 @@ export {
|
|
|
19
19
|
createXgateExtension,
|
|
20
20
|
type XgateExtensionOptions,
|
|
21
21
|
} from "./extension.ts";
|
|
22
|
-
export {
|
|
22
|
+
export {
|
|
23
|
+
XgateError,
|
|
24
|
+
XgateRateLimitedError,
|
|
25
|
+
} from "@lucid-agents/xgate-client/types";
|
|
23
26
|
export type {
|
|
24
27
|
Invocation,
|
|
25
28
|
PurchaseResult,
|
package/src/provider.ts
CHANGED
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
type Provider,
|
|
7
7
|
type SimpleStreamOptions,
|
|
8
8
|
} from "@earendil-works/pi-ai";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
XgateError,
|
|
11
|
+
XgateRateLimitedError,
|
|
12
|
+
} from "@lucid-agents/xgate-client/types";
|
|
10
13
|
import { catalogEntries, encode, decode, type PiContext } from "./codec.ts";
|
|
11
14
|
import {
|
|
12
15
|
XgatePurchaseError,
|
|
@@ -33,6 +36,11 @@ export interface XgatePurchaseFailure {
|
|
|
33
36
|
/** False only when the transport proved no unresolved payment. */
|
|
34
37
|
pending: boolean;
|
|
35
38
|
payment?: XgatePayment;
|
|
39
|
+
/**
|
|
40
|
+
* Set when the payee refused the purchase with a rate limit: its suggested wait, in
|
|
41
|
+
* seconds, before a new purchase. Reconciling this invocation still comes first.
|
|
42
|
+
*/
|
|
43
|
+
retryAfterSeconds?: number;
|
|
36
44
|
}
|
|
37
45
|
const zero = () => ({
|
|
38
46
|
input: 0,
|
|
@@ -259,6 +267,12 @@ export async function createXgateProvider(
|
|
|
259
267
|
const payment =
|
|
260
268
|
paid?.payment ??
|
|
261
269
|
(error instanceof XgatePurchaseError ? error.payment : undefined);
|
|
270
|
+
const wait =
|
|
271
|
+
paid === undefined &&
|
|
272
|
+
(error instanceof XgateRateLimitedError ||
|
|
273
|
+
error instanceof XgatePurchaseError)
|
|
274
|
+
? error.retryAfterSeconds
|
|
275
|
+
: undefined;
|
|
262
276
|
const failure: XgatePurchaseFailure = {
|
|
263
277
|
invocationId: attempted,
|
|
264
278
|
code: error instanceof XgateError ? error.code : "purchase_failed",
|
|
@@ -266,6 +280,7 @@ export async function createXgateProvider(
|
|
|
266
280
|
paid === undefined &&
|
|
267
281
|
!(error instanceof XgatePurchaseError && !error.pending),
|
|
268
282
|
...(payment ? { payment } : {}),
|
|
283
|
+
...(wait !== undefined ? { retryAfterSeconds: wait } : {}),
|
|
269
284
|
};
|
|
270
285
|
// The original ID is the only reconciliation key. "Billing" also keeps Pi's transient
|
|
271
286
|
// error classifier from retrying a message whose hex ID happens to contain "500".
|
package/src/transport.ts
CHANGED
|
@@ -51,20 +51,31 @@ export class XgatePurchaseError extends XgateError {
|
|
|
51
51
|
readonly invocationId: string;
|
|
52
52
|
readonly pending: boolean;
|
|
53
53
|
readonly payment?: XgatePayment;
|
|
54
|
+
/** The payee's rate-limit hint, in seconds, when that is why the purchase ended. */
|
|
55
|
+
readonly retryAfterSeconds?: number;
|
|
54
56
|
constructor(
|
|
55
57
|
code: string,
|
|
56
58
|
invocationId: string,
|
|
57
59
|
pending: boolean,
|
|
58
60
|
payment?: XgatePayment,
|
|
61
|
+
retryAfterSeconds?: number,
|
|
59
62
|
) {
|
|
60
63
|
super(code);
|
|
61
64
|
this.name = "XgatePurchaseError";
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
const limited =
|
|
66
|
+
retryAfterSeconds === undefined
|
|
67
|
+
? ""
|
|
68
|
+
: ` The payee rate limited it and suggests waiting ${retryAfterSeconds} seconds before a new purchase.`;
|
|
69
|
+
this.message =
|
|
70
|
+
(pending
|
|
71
|
+
? `Xgate: ${code}. The payment outcome is unknown; do not retry. Reconcile it before another purchase.`
|
|
72
|
+
: `Xgate: ${code}. Inspect the payment status before another purchase.`) +
|
|
73
|
+
limited;
|
|
65
74
|
this.invocationId = invocationId;
|
|
66
75
|
this.pending = pending;
|
|
67
76
|
if (payment) this.payment = payment;
|
|
77
|
+
if (retryAfterSeconds !== undefined)
|
|
78
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
68
79
|
}
|
|
69
80
|
}
|
|
70
81
|
/**
|