zk-credits 0.2.5 → 0.2.7
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 +52 -23
- package/dist/base-registered-trial.d.ts +6 -2
- package/dist/base-registered-trial.js +18 -2
- package/dist/base-sidecar.d.ts +2 -0
- package/dist/base-sidecar.js +13 -1
- package/dist/cli.js +152 -40
- package/dist/codex-profile.js +1 -1
- package/dist/gateway-trial-status.d.ts +13 -0
- package/dist/gateway-trial-status.js +70 -0
- package/dist/local-x402-agent.d.ts +34 -0
- package/dist/local-x402-agent.js +130 -0
- package/dist/responses-bridge.js +5 -3
- package/dist/sidecar.d.ts +2 -0
- package/dist/sidecar.js +8 -0
- package/dist/zk-credits.js +641 -356
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Loopback sidecar that attaches a hash-pinned BN254 Groth16 proof to each
|
|
4
4
|
coding-agent LLM request through the experimental x402 `zk-prepaid` scheme.
|
|
5
|
-
Built for the invite-only
|
|
6
|
-
|
|
7
|
-
audited.
|
|
5
|
+
Built for the invite-only experimental Base Sepolia pilot. Product calls use
|
|
6
|
+
founder-provisioned test credits and have no product payment step; the circuit
|
|
7
|
+
is not independently audited. External research participants in slots A and C
|
|
8
|
+
receive $25 for a 30-minute session regardless of setup success. That payment
|
|
9
|
+
is not product revenue or willingness-to-pay evidence.
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
|
-
npm install --global zk-credits@0.2.
|
|
12
|
+
npm install --global zk-credits@0.2.7
|
|
11
13
|
```
|
|
12
14
|
|
|
13
15
|
## Pinned pilot versions
|
|
@@ -17,17 +19,17 @@ not mix versions between the sidecar, the adapter package, and the gateway:
|
|
|
17
19
|
|
|
18
20
|
| Package | Version |
|
|
19
21
|
| --- | --- |
|
|
20
|
-
| `zk-credits` (this sidecar) | `0.2.
|
|
22
|
+
| `zk-credits` (this sidecar) | `0.2.7` |
|
|
21
23
|
| `@zk-credits/x402-zk-prepaid` (adapter) | `0.1.0` |
|
|
22
24
|
| `@zk-credits/shared` | `0.1.0` |
|
|
23
25
|
|
|
24
|
-
The pinned Base Sepolia trial version is `0.2.
|
|
25
|
-
`POST /v1/chat/completions` path below, includes the loopback Responses
|
|
26
|
-
for Codex
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
The pinned Base Sepolia trial version is `0.2.7`. It uses the non-streaming
|
|
27
|
+
`POST /v1/chat/completions` path below, includes the narrow loopback Responses
|
|
28
|
+
bridge for supported Codex text and function-tool requests, and acquires the
|
|
29
|
+
pinned private proving bundle through the local GitHub CLI session. It also
|
|
30
|
+
synchronizes V2 Base events and checks the gateway known root before local
|
|
31
|
+
setup completes. The proving artifacts are available only from the private
|
|
32
|
+
immutable release pinned by `circuits/manifest.json`.
|
|
31
33
|
|
|
32
34
|
Verify the shipped artifacts before your first prove. The package's
|
|
33
35
|
`circuits/manifest.json` fixes the SHA-256 of the frozen
|
|
@@ -57,14 +59,19 @@ still receives the class. A rejected request consumes no credit.
|
|
|
57
59
|
|
|
58
60
|
## First run
|
|
59
61
|
|
|
60
|
-
1. Redeem
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
1. Redeem the founder-prepared invite and download the encrypted V2 credential
|
|
63
|
+
export. The founder grants access to the private bundle repository
|
|
64
|
+
`mangekyou-labs/zk-credits-base-sepolia-v2-bundle` before the session.
|
|
65
|
+
2. Give Codex the single setup instruction in the
|
|
66
|
+
[first-use guide](../../docs/onboarding/base-zk-credits-codex-first-use.md).
|
|
67
|
+
Codex checks local GitHub CLI authentication and repository access, installs
|
|
68
|
+
this pinned release, downloads and verifies the bundle, and runs setup.
|
|
69
|
+
The human signs in if needed, enters the recovery password at the hidden
|
|
70
|
+
prompt, gives consent, and chooses their own task. No product payment is
|
|
71
|
+
involved. The sidecar uses the local GitHub CLI session and never receives
|
|
72
|
+
or stores the GitHub token.
|
|
73
|
+
3. The setup command sets the credential, gateway, and Base Sepolia RPC
|
|
74
|
+
endpoint:
|
|
68
75
|
|
|
69
76
|
```bash
|
|
70
77
|
export ZK_CREDITS_CREDENTIAL_PATH="$HOME/Downloads/zk-credits-credential.json"
|
|
@@ -99,7 +106,7 @@ still receives the class. A rejected request consumes no credit.
|
|
|
99
106
|
leaves, and the local slot stays reusable. The gateway never serves a path
|
|
100
107
|
for a named leaf or commitment.
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
4. Start setup. It verifies the credential, pinned artifact hashes, and
|
|
103
110
|
witness locally, then prompts for the backup password without echo:
|
|
104
111
|
|
|
105
112
|
```bash
|
|
@@ -107,7 +114,9 @@ still receives the class. A rejected request consumes no credit.
|
|
|
107
114
|
```
|
|
108
115
|
|
|
109
116
|
No gateway admin token is required. Then run `zk-credits codex` to open
|
|
110
|
-
Codex with the local sidecar.
|
|
117
|
+
Codex with the local sidecar. A/C first-use measurement targets five
|
|
118
|
+
minutes of active human work; unattended download, proving, and provider
|
|
119
|
+
waits are measured separately. More than five minutes is a friction finding.
|
|
111
120
|
|
|
112
121
|
The sidecar binds `127.0.0.1:3210` only. It does not modify `~/.cline` or
|
|
113
122
|
`~/.codex`.
|
|
@@ -198,11 +207,20 @@ zk-credits cline [cline arguments...]
|
|
|
198
207
|
zk-credits setup codex [--model <model>]
|
|
199
208
|
zk-credits codex [codex arguments...]
|
|
200
209
|
zk-credits status
|
|
201
|
-
zk-credits serve [--port <port>]
|
|
210
|
+
zk-credits serve [--port <port>] [--internal-trial-one-proof]
|
|
211
|
+
zk-credits x402-agent
|
|
212
|
+
zk-credits founder-demo-agent
|
|
202
213
|
zk-credits trial-registered-adapter
|
|
203
214
|
eval "$(zk-credits env)"
|
|
204
215
|
```
|
|
205
216
|
|
|
217
|
+
`serve --internal-trial-one-proof` is an opt-in maintainer mode for the
|
|
218
|
+
internal Base Sepolia trial. During that process lifetime, one authenticated,
|
|
219
|
+
valid spend request may reach the prepaid transport and the proof factory may
|
|
220
|
+
attempt one proof. A later valid request receives HTTP 409 with
|
|
221
|
+
`internal_trial_limit_reached`; ordinary `serve` behavior is unchanged without
|
|
222
|
+
the flag.
|
|
223
|
+
|
|
206
224
|
`trial-registered-adapter` is a maintainer-only internal trial command. It
|
|
207
225
|
requires the locally recovered credential and pinned proving bundle, prompts
|
|
208
226
|
for the credential password and gateway admin token without echo when they are
|
|
@@ -220,6 +238,17 @@ Codex SDK:
|
|
|
220
238
|
import { buildCodexSdkOptions, buildCodexThreadOptions } from 'zk-credits/codex';
|
|
221
239
|
```
|
|
222
240
|
|
|
241
|
+
`zk-credits/x402` exports `createLocalX402Agent` for an operator who is
|
|
242
|
+
integrating the adapter into their own x402 agent. It loads the same local
|
|
243
|
+
credential, pinned proof artifacts, public witness, proof engine, and slot
|
|
244
|
+
ledger used by the sidecar. The request-aware client binds the actual method,
|
|
245
|
+
URL, and body into each spend; its metrics endpoint exposes authenticated
|
|
246
|
+
aggregate counters on loopback. See
|
|
247
|
+
[`docs/onboarding/base-zk-credits-x402-agent.md`](../../docs/onboarding/base-zk-credits-x402-agent.md)
|
|
248
|
+
for integration and measured-call steps. The included `zk-credits x402-agent`
|
|
249
|
+
command is a small task-running starter for the founder demo and local
|
|
250
|
+
rehearsal; external slot C uses their own agent.
|
|
251
|
+
|
|
223
252
|
The credential secret is decrypted only in local memory. The sidecar cache
|
|
224
253
|
contains public Base event leaves and never stores the secret, prompt, response,
|
|
225
254
|
proof, account, or wallet.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One-shot local transport for
|
|
2
|
+
* One-shot local transport for the founder's task-running x402 demo agent.
|
|
3
3
|
*
|
|
4
4
|
* @x402/core creates scheme payloads from PaymentRequired and does not pass
|
|
5
5
|
* HTTP method/body context to a scheme client. This harness captures that
|
|
6
6
|
* request context locally, then binds it into the same Base proof factory
|
|
7
7
|
* before invoking the registered `zk-prepaid` scheme. It is intended only for
|
|
8
|
-
* the internal Base Sepolia
|
|
8
|
+
* the internal Base Sepolia founder demo, not as a general HTTP client.
|
|
9
9
|
*
|
|
10
10
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
11
11
|
*/
|
|
@@ -28,6 +28,10 @@ export interface BaseRegisteredTrialOptions extends BasePrepaidClientOptions {
|
|
|
28
28
|
body: string;
|
|
29
29
|
headers?: HeadersInit;
|
|
30
30
|
fetch?: typeof fetch;
|
|
31
|
+
/** The response can be shown locally to the operator; it is never retained by metrics. */
|
|
32
|
+
onResponse?: (response: Response) => void | Promise<void>;
|
|
31
33
|
}
|
|
34
|
+
/** A bounded, single-task request for the founder's internal x402 demo. */
|
|
35
|
+
export declare function buildBaseRegisteredTrialRequestBody(task?: string): string;
|
|
32
36
|
/** Runs one POST to the pilot chat route through a directly registered adapter. */
|
|
33
37
|
export declare function runBaseRegisteredTrialExchange(options: BaseRegisteredTrialOptions): Promise<BaseRegisteredTrialResult>;
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One-shot local transport for
|
|
2
|
+
* One-shot local transport for the founder's task-running x402 demo agent.
|
|
3
3
|
*
|
|
4
4
|
* @x402/core creates scheme payloads from PaymentRequired and does not pass
|
|
5
5
|
* HTTP method/body context to a scheme client. This harness captures that
|
|
6
6
|
* request context locally, then binds it into the same Base proof factory
|
|
7
7
|
* before invoking the registered `zk-prepaid` scheme. It is intended only for
|
|
8
|
-
* the internal Base Sepolia
|
|
8
|
+
* the internal Base Sepolia founder demo, not as a general HTTP client.
|
|
9
9
|
*
|
|
10
10
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
11
11
|
*/
|
|
12
12
|
import { x402Client } from '@x402/core/client';
|
|
13
13
|
import { BASE_SEPOLIA_NETWORK, decodeHeader, encodeHeader, lifecycleFailure, lifecycleStage, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PAYMENT_SIGNATURE_HEADER, registerZkPrepaidClient, ZK_PREPAID_SCHEME, } from '@zk-credits/x402-zk-prepaid';
|
|
14
14
|
import { createBasePaymentFactory } from './base-sidecar.js';
|
|
15
|
+
/** A bounded, single-task request for the founder's internal x402 demo. */
|
|
16
|
+
export function buildBaseRegisteredTrialRequestBody(task = 'Reply with exactly: internal trial complete.') {
|
|
17
|
+
if (typeof task !== 'string' || task.trim().length === 0 || task.length > 4_000) {
|
|
18
|
+
throw new Error('Task must contain between 1 and 4000 characters');
|
|
19
|
+
}
|
|
20
|
+
return JSON.stringify({
|
|
21
|
+
model: 'deepseek/deepseek-v4-flash',
|
|
22
|
+
messages: [{ role: 'user', content: task }],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
15
25
|
function observe(options, event) {
|
|
16
26
|
try {
|
|
17
27
|
options.lifecycle?.(event);
|
|
@@ -163,6 +173,12 @@ export async function runBaseRegisteredTrialExchange(options) {
|
|
|
163
173
|
}
|
|
164
174
|
result.responseSucceeded = true;
|
|
165
175
|
observe(options, lifecycleStage('exchange_succeeded'));
|
|
176
|
+
try {
|
|
177
|
+
await options.onResponse?.(paidResponse.clone());
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Displaying a completed response is local convenience, not settlement.
|
|
181
|
+
}
|
|
166
182
|
result.committedSlotDelta = ledger.snapshot().committed - startingCommittedSlots;
|
|
167
183
|
return result;
|
|
168
184
|
}
|
package/dist/base-sidecar.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ export interface BasePrepaidClientOptions {
|
|
|
50
50
|
credential: CreditCredential;
|
|
51
51
|
witnessProvider: BaseWitnessProvider;
|
|
52
52
|
prove: BaseProofGenerator;
|
|
53
|
+
/** Limits this local client to one proof attempt for a single internal trial. */
|
|
54
|
+
internalTrialOneProof?: boolean;
|
|
53
55
|
fetch?: typeof fetch;
|
|
54
56
|
/**
|
|
55
57
|
* Durable local slot ledger. A slot is provisional until the proof passes
|
package/dist/base-sidecar.js
CHANGED
|
@@ -16,6 +16,12 @@ const TREE_DEPTH = 20;
|
|
|
16
16
|
const FIELD_ORDER = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
17
17
|
const REPLAY_TTL_MS = 24 * 60 * 60 * 1000;
|
|
18
18
|
const MAX_PENDING_PAYMENTS = 64;
|
|
19
|
+
const PAYMENT_PROOF_FIELDS = ['pi_a', 'pi_b', 'pi_c'];
|
|
20
|
+
function proofForPayment(proof) {
|
|
21
|
+
return Object.fromEntries(PAYMENT_PROOF_FIELDS
|
|
22
|
+
.filter((field) => Object.prototype.hasOwnProperty.call(proof, field))
|
|
23
|
+
.map((field) => [field, proof[field]]));
|
|
24
|
+
}
|
|
19
25
|
function field(value, label) {
|
|
20
26
|
if (!/^\d+$/u.test(value))
|
|
21
27
|
throw new Error(`Invalid ${label}`);
|
|
@@ -63,6 +69,7 @@ function requestBodyBytes(body) {
|
|
|
63
69
|
/** Creates the shared local proof factory used by both client transports. */
|
|
64
70
|
export function createBasePaymentFactory(options) {
|
|
65
71
|
const secret = secretFromBase64Url(options.credential.secret);
|
|
72
|
+
let internalTrialProofAttempted = false;
|
|
66
73
|
return async ({ method, url, body, requirements }) => {
|
|
67
74
|
if (requirements.extra.deploymentDomain !== options.credential.deploymentDomain) {
|
|
68
75
|
throw new Error('Credential deployment domain does not match the gateway challenge');
|
|
@@ -98,6 +105,11 @@ export function createBasePaymentFactory(options) {
|
|
|
98
105
|
nullifier,
|
|
99
106
|
share,
|
|
100
107
|
];
|
|
108
|
+
if (options.internalTrialOneProof) {
|
|
109
|
+
if (internalTrialProofAttempted)
|
|
110
|
+
throw new Error('internal_trial_limit_reached');
|
|
111
|
+
internalTrialProofAttempted = true;
|
|
112
|
+
}
|
|
101
113
|
const proof = await options.prove({
|
|
102
114
|
secret: secretToField(secret),
|
|
103
115
|
tier_id: String(options.credential.tierId),
|
|
@@ -118,7 +130,7 @@ export function createBasePaymentFactory(options) {
|
|
|
118
130
|
await slotLedger.commit(selectedSlot);
|
|
119
131
|
const payment = buildPaymentPayload({
|
|
120
132
|
requirements,
|
|
121
|
-
proof: proof.proof,
|
|
133
|
+
proof: proofForPayment(proof.proof),
|
|
122
134
|
publicSignals: [...proof.publicSignals],
|
|
123
135
|
nonce,
|
|
124
136
|
responseKey: responseKeys.publicKey,
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { launchCodexProcess } from './codex-launcher.js';
|
|
|
7
7
|
import { launchClineProcess } from './cline-launcher.js';
|
|
8
8
|
import { isCodexProfileInstalled, resolveCodexHome, writeCodexProfile, } from './codex-profile.js';
|
|
9
9
|
import { createBasePrepaidClient, createFileWitnessProvider, validateBaseCreditWitness } from './base-sidecar.js';
|
|
10
|
+
import { createLocalX402Agent } from './local-x402-agent.js';
|
|
10
11
|
import { createBaseEventWitnessProvider } from './base-event-sync.js';
|
|
11
12
|
import { createPinnedBaseProofGenerator } from './proof-coordinator.js';
|
|
12
13
|
import { createBaseProofMetrics } from './proof-metrics.js';
|
|
@@ -21,10 +22,11 @@ import { applySetupConfig, readSetupConfig, resolveSetupInputs, writeSetupConfig
|
|
|
21
22
|
import { ensureSidecarReady } from './sidecar-lifecycle.js';
|
|
22
23
|
import { readLoopbackToken, writeLoopbackToken } from './sidecar-state.js';
|
|
23
24
|
import { createSidecarServer } from './sidecar.js';
|
|
24
|
-
import { runBaseRegisteredTrialExchange } from './base-registered-trial.js';
|
|
25
|
+
import { buildBaseRegisteredTrialRequestBody, runBaseRegisteredTrialExchange } from './base-registered-trial.js';
|
|
25
26
|
import { loadCircuitManifest } from './artifact-bundle.js';
|
|
26
27
|
import { BASE_SEPOLIA_NETWORK } from '@zk-credits/x402-zk-prepaid';
|
|
27
28
|
import { requireGatewayKnownRoot as checkGatewayKnownRoot } from './gateway-root-check.js';
|
|
29
|
+
import { evaluateGatewayReadiness, isGatewayStatusFresh } from './gateway-trial-status.js';
|
|
28
30
|
const DEFAULT_GATEWAY_URL = 'https://zk-credits-gateway.onrender.com';
|
|
29
31
|
const DEFAULT_PORT = 3210;
|
|
30
32
|
function stateDirectory() {
|
|
@@ -164,16 +166,25 @@ function printHelp() {
|
|
|
164
166
|
zk-credits setup codex [--model <model>]
|
|
165
167
|
zk-credits codex [codex arguments...]
|
|
166
168
|
zk-credits status
|
|
167
|
-
zk-credits serve [--port <port>]
|
|
168
|
-
zk-credits
|
|
169
|
+
zk-credits serve [--port <port>] [--internal-trial-one-proof]
|
|
170
|
+
zk-credits x402-agent
|
|
171
|
+
zk-credits founder-demo-agent
|
|
169
172
|
eval "$(zk-credits env)"
|
|
170
173
|
|
|
171
174
|
"zk-credits cline" and the Codex companion configure and launch coding agents
|
|
172
175
|
against the x402 zk-prepaid loopback sidecar. The gateway spend path is
|
|
173
176
|
non-streaming POST /v1/chat/completions; Codex Responses requests pass through
|
|
174
177
|
the sidecar's bounded local compatibility bridge.
|
|
175
|
-
"zk-credits
|
|
176
|
-
|
|
178
|
+
"zk-credits founder-demo-agent" asks for one local task and runs it through
|
|
179
|
+
the request-aware x402 adapter and local proof engine. It requires explicit
|
|
180
|
+
confirmation before one billable internal exchange; the task and response are
|
|
181
|
+
displayed locally and never added to activation evidence.
|
|
182
|
+
"zk-credits x402-agent" is a small task-running starter for the participant's
|
|
183
|
+
own x402 agent loop. It asks for tasks locally and sends each through the same
|
|
184
|
+
request-aware adapter and local proof engine; aggregate metrics stay on the
|
|
185
|
+
loopback-only authenticated endpoint.
|
|
186
|
+
"zk-credits serve --internal-trial-one-proof" limits this sidecar process to
|
|
187
|
+
one authenticated, valid spend request and one local proof attempt.
|
|
177
188
|
Set ZK_CREDITS_CREDENTIAL_PATH and BASE_RPC_URL before setup. For Base Sepolia
|
|
178
189
|
V2, setup can download the immutable bundle pinned by the package manifest by
|
|
179
190
|
using this machine's authenticated GitHub CLI session, then synchronizes the
|
|
@@ -193,16 +204,22 @@ async function loadBaseClientRuntime(passwordInput) {
|
|
|
193
204
|
if (!artifactDirectory) {
|
|
194
205
|
throw new Error('Set ZK_CREDITS_ARTIFACT_DIR to the directory holding the installed pinned proving bundle');
|
|
195
206
|
}
|
|
207
|
+
const baseRpcUrl = process.env.BASE_RPC_URL;
|
|
208
|
+
const baseContractAddress = process.env.BASE_PRIVATE_CREDIT_BOND_ADDRESS;
|
|
196
209
|
const witnessProvider = witnessPath
|
|
197
210
|
? createFileWitnessProvider(JSON.parse(await readFile(witnessPath, 'utf8')))
|
|
198
|
-
:
|
|
199
|
-
?
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
: baseRpcUrl && baseContractAddress
|
|
212
|
+
? await witnessProviderForSource({
|
|
213
|
+
kind: 'base-events',
|
|
214
|
+
rpcUrl: baseRpcUrl,
|
|
215
|
+
contractAddress: baseContractAddress,
|
|
216
|
+
...(process.env.BASE_DEPLOYMENT_BLOCK && /^\d+$/u.test(process.env.BASE_DEPLOYMENT_BLOCK)
|
|
217
|
+
? { deploymentBlock: process.env.BASE_DEPLOYMENT_BLOCK }
|
|
218
|
+
: {}),
|
|
219
|
+
...(process.env.BASE_CONFIRMATIONS && /^\d+$/u.test(process.env.BASE_CONFIRMATIONS)
|
|
220
|
+
? { confirmations: process.env.BASE_CONFIRMATIONS }
|
|
221
|
+
: {}),
|
|
222
|
+
}, join(stateDirectory(), 'base-event-sync.json'))
|
|
206
223
|
: (() => { throw new Error('Set ZK_CREDITS_WITNESS_PATH or configure BASE_RPC_URL and BASE_PRIVATE_CREDIT_BOND_ADDRESS'); })();
|
|
207
224
|
const proofMetrics = createBaseProofMetrics();
|
|
208
225
|
const exchangeMetrics = createZkPrepaidLifecycleMetrics();
|
|
@@ -230,7 +247,6 @@ const GATEWAY_COUNTER_NAMES = [
|
|
|
230
247
|
'request_rejected',
|
|
231
248
|
];
|
|
232
249
|
const GATEWAY_CLAIM_COUNT_NAMES = ['reserved', 'ready', 'committed', 'cancelled'];
|
|
233
|
-
const GATEWAY_READINESS_CHECK_NAMES = new Set(['launchControl', 'database', 'baseRoot', 'baseRpc', 'verifierAssets', 'provider']);
|
|
234
250
|
function isRecord(value) {
|
|
235
251
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
236
252
|
}
|
|
@@ -265,16 +281,10 @@ async function requireGatewayKnownRoot(root) {
|
|
|
265
281
|
}
|
|
266
282
|
async function gatewayReadiness(origin) {
|
|
267
283
|
const response = await readGatewayJson(new URL('/ready', origin), { accept: 'application/json' });
|
|
268
|
-
const
|
|
269
|
-
const checks = Array.isArray(report?.checks) ? report.checks : [];
|
|
270
|
-
const failedChecks = checks.flatMap((check) => (isRecord(check) && check.ok === true
|
|
271
|
-
? []
|
|
272
|
-
: [isRecord(check) && typeof check.name === 'string' && GATEWAY_READINESS_CHECK_NAMES.has(check.name) ? check.name : 'unknown']));
|
|
273
|
-
const hasLaunchCheck = checks.some((check) => isRecord(check) && check.name === 'launchControl' && check.ok === true);
|
|
284
|
+
const gate = evaluateGatewayReadiness(response.status, response.body);
|
|
274
285
|
return {
|
|
275
286
|
status: response.status,
|
|
276
|
-
|
|
277
|
-
failedChecks,
|
|
287
|
+
...gate,
|
|
278
288
|
};
|
|
279
289
|
}
|
|
280
290
|
async function gatewayTrialSnapshot(origin, token) {
|
|
@@ -314,6 +324,7 @@ async function gatewayTrialSnapshot(origin, token) {
|
|
|
314
324
|
status: response.status,
|
|
315
325
|
...(snapshot ? { snapshot } : {}),
|
|
316
326
|
gatePassed: response.status === 200
|
|
327
|
+
&& isGatewayStatusFresh(body?.generatedAt)
|
|
317
328
|
&& body?.network === BASE_SEPOLIA_NETWORK
|
|
318
329
|
&& launch?.state === 'enabled'
|
|
319
330
|
&& knownRoot
|
|
@@ -338,15 +349,107 @@ async function confirmDirectTrial() {
|
|
|
338
349
|
input.close();
|
|
339
350
|
}
|
|
340
351
|
}
|
|
352
|
+
async function readFounderDemoTask() {
|
|
353
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
354
|
+
throw new Error('the founder demo requires a local interactive terminal');
|
|
355
|
+
}
|
|
356
|
+
const input = createInterface({ input: process.stdin, output: process.stdout });
|
|
357
|
+
try {
|
|
358
|
+
const task = (await input.question('Task for the founder x402 demo agent (up to 4000 characters): ')).trim();
|
|
359
|
+
if (task.length === 0 || task.length > 4_000) {
|
|
360
|
+
throw new Error('Task must contain between 1 and 4000 characters');
|
|
361
|
+
}
|
|
362
|
+
return task;
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
input.close();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function readX402AgentTask() {
|
|
369
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
370
|
+
throw new Error('the x402 starter agent requires a local interactive terminal');
|
|
371
|
+
}
|
|
372
|
+
const input = createInterface({ input: process.stdin, output: process.stdout });
|
|
373
|
+
try {
|
|
374
|
+
const task = (await input.question('Task for your x402 agent (or :quit): ')).trim();
|
|
375
|
+
if (task === ':quit')
|
|
376
|
+
return null;
|
|
377
|
+
if (task.length === 0 || task.length > 4_000) {
|
|
378
|
+
throw new Error('Task must contain between 1 and 4000 characters');
|
|
379
|
+
}
|
|
380
|
+
return task;
|
|
381
|
+
}
|
|
382
|
+
finally {
|
|
383
|
+
input.close();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async function runX402Agent() {
|
|
387
|
+
const gateway = gatewayOrigin();
|
|
388
|
+
const runtime = await createLocalX402Agent({
|
|
389
|
+
credentialPassword: await readHiddenValue('Credential backup password: '),
|
|
390
|
+
});
|
|
391
|
+
const url = new URL('/v1/chat/completions', gateway).toString();
|
|
392
|
+
console.log(`Local aggregate metrics: ${runtime.metricsUrl}`);
|
|
393
|
+
console.log('Enter one task at a time. Each task can spend one founder-provisioned test credit.');
|
|
394
|
+
try {
|
|
395
|
+
while (true) {
|
|
396
|
+
const task = await readX402AgentTask();
|
|
397
|
+
if (task === null)
|
|
398
|
+
break;
|
|
399
|
+
const response = await runtime.client.fetch(url, {
|
|
400
|
+
method: 'POST',
|
|
401
|
+
headers: { 'content-type': 'application/json' },
|
|
402
|
+
body: buildBaseRegisteredTrialRequestBody(task),
|
|
403
|
+
});
|
|
404
|
+
await displayAgentResponse(response);
|
|
405
|
+
if (!response.ok)
|
|
406
|
+
console.log(`Request ended with HTTP ${response.status}.`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
finally {
|
|
410
|
+
await runtime.close();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async function displayAgentResponse(response) {
|
|
414
|
+
let content;
|
|
415
|
+
try {
|
|
416
|
+
const body = await response.json();
|
|
417
|
+
if (isRecord(body) && Array.isArray(body.choices) && isRecord(body.choices[0])) {
|
|
418
|
+
const message = body.choices[0].message;
|
|
419
|
+
if (isRecord(message))
|
|
420
|
+
content = message.content;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
catch {
|
|
424
|
+
// The response is a local display convenience; activation recording uses
|
|
425
|
+
// only the closed exchange and counter summaries below.
|
|
426
|
+
}
|
|
427
|
+
console.log('\nAgent response (shown locally only):');
|
|
428
|
+
if (typeof content === 'string') {
|
|
429
|
+
console.log(content);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (Array.isArray(content)) {
|
|
433
|
+
const textParts = content
|
|
434
|
+
.filter((part) => isRecord(part))
|
|
435
|
+
.filter((part) => part.type === 'text' && typeof part.text === 'string')
|
|
436
|
+
.map((part) => part.text);
|
|
437
|
+
if (textParts.length > 0) {
|
|
438
|
+
console.log(textParts.join('\n'));
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
console.log('[The response completed, but its text content was not available for local display.]');
|
|
443
|
+
}
|
|
341
444
|
function gatewayCounterDelta(before, after) {
|
|
342
445
|
return {
|
|
343
446
|
metrics: Object.fromEntries(GATEWAY_COUNTER_NAMES.map((name) => [name, after.metrics[name] - before.metrics[name]])),
|
|
344
447
|
claims: Object.fromEntries(GATEWAY_CLAIM_COUNT_NAMES.map((name) => [name, after.claims[name] - before.claims[name]])),
|
|
345
448
|
};
|
|
346
449
|
}
|
|
347
|
-
async function trialRegisteredAdapter() {
|
|
450
|
+
async function trialRegisteredAdapter(task) {
|
|
348
451
|
if (!await confirmDirectTrial()) {
|
|
349
|
-
console.log(JSON.stringify({ path: '
|
|
452
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'not_run' }));
|
|
350
453
|
return;
|
|
351
454
|
}
|
|
352
455
|
try {
|
|
@@ -354,13 +457,13 @@ async function trialRegisteredAdapter() {
|
|
|
354
457
|
if (runtime.credential.tierId !== 0
|
|
355
458
|
|| runtime.credential.deploymentDomain !== '84532'
|
|
356
459
|
|| runtime.credential.expiry <= Math.floor(Date.now() / 1000)) {
|
|
357
|
-
console.log(JSON.stringify({ path: '
|
|
460
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'credential_gate' }));
|
|
358
461
|
return;
|
|
359
462
|
}
|
|
360
463
|
const startingSlots = runtime.slotLedger.snapshot();
|
|
361
464
|
if (startingSlots.committed >= startingSlots.capacity) {
|
|
362
465
|
console.log(JSON.stringify({
|
|
363
|
-
path: '
|
|
466
|
+
path: 'founder-demo-agent',
|
|
364
467
|
status: 'stopped',
|
|
365
468
|
failurePhase: 'local_credit_gate',
|
|
366
469
|
localCredits: { capacity: startingSlots.capacity, committed: startingSlots.committed, available: 0 },
|
|
@@ -370,7 +473,7 @@ async function trialRegisteredAdapter() {
|
|
|
370
473
|
const gateway = gatewayOrigin();
|
|
371
474
|
const adminToken = process.env.BILLING_INTERNAL_TOKEN || await readHiddenValue('Gateway admin token: ');
|
|
372
475
|
if (!adminToken.trim()) {
|
|
373
|
-
console.log(JSON.stringify({ path: '
|
|
476
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'admin_auth' }));
|
|
374
477
|
return;
|
|
375
478
|
}
|
|
376
479
|
const lifecycle = createNodeSidecarLifecycle({
|
|
@@ -387,33 +490,29 @@ async function trialRegisteredAdapter() {
|
|
|
387
490
|
]);
|
|
388
491
|
const launcherStatus = { codexProfileInstalled, sidecarRunning };
|
|
389
492
|
if (sidecarRunning) {
|
|
390
|
-
console.log(JSON.stringify({ path: '
|
|
493
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'sidecar_running', launcherStatus }));
|
|
391
494
|
return;
|
|
392
495
|
}
|
|
393
496
|
const witness = await runtime.witnessProvider.witnessForCredential(runtime.credential);
|
|
394
497
|
const readiness = await gatewayReadiness(gateway);
|
|
395
498
|
if (!readiness.passed) {
|
|
396
|
-
console.log(JSON.stringify({ path: '
|
|
499
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'readiness', launcherStatus, readiness }));
|
|
397
500
|
return;
|
|
398
501
|
}
|
|
399
502
|
const adminBefore = await gatewayTrialSnapshot(gateway, adminToken);
|
|
400
503
|
if (!adminBefore.gatePassed || !adminBefore.snapshot) {
|
|
401
|
-
console.log(JSON.stringify({ path: '
|
|
504
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'admin_status', launcherStatus, readiness, adminStatus: adminBefore.status }));
|
|
402
505
|
return;
|
|
403
506
|
}
|
|
404
507
|
if (witness.root !== adminBefore.snapshot.currentRoot) {
|
|
405
|
-
console.log(JSON.stringify({ path: '
|
|
508
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'root_mismatch', launcherStatus, readiness, adminStatus: adminBefore.status }));
|
|
406
509
|
return;
|
|
407
510
|
}
|
|
408
511
|
const baselineSlots = runtime.slotLedger.snapshot();
|
|
409
512
|
const baselineProof = runtime.proofMetrics.snapshot();
|
|
410
513
|
const baselineLifecycle = runtime.exchangeMetrics.snapshot();
|
|
411
514
|
const requestUrl = new URL('/v1/chat/completions', gateway).toString();
|
|
412
|
-
const requestBody =
|
|
413
|
-
model: 'deepseek/deepseek-v4-flash',
|
|
414
|
-
messages: [{ role: 'user', content: 'Reply with exactly: internal trial complete.' }],
|
|
415
|
-
stream: false,
|
|
416
|
-
});
|
|
515
|
+
const requestBody = buildBaseRegisteredTrialRequestBody(task);
|
|
417
516
|
const exchange = await runBaseRegisteredTrialExchange({
|
|
418
517
|
url: requestUrl,
|
|
419
518
|
body: requestBody,
|
|
@@ -423,6 +522,7 @@ async function trialRegisteredAdapter() {
|
|
|
423
522
|
prove: runtime.prove,
|
|
424
523
|
slotLedger: runtime.slotLedger,
|
|
425
524
|
lifecycle: runtime.exchangeMetrics.observe,
|
|
525
|
+
onResponse: displayAgentResponse,
|
|
426
526
|
});
|
|
427
527
|
const adminAfter = await gatewayTrialSnapshot(gateway, adminToken);
|
|
428
528
|
const proofAfter = runtime.proofMetrics.snapshot();
|
|
@@ -451,7 +551,7 @@ async function trialRegisteredAdapter() {
|
|
|
451
551
|
?? (adminAfter.status !== 200 || !adminAfter.snapshot ? 'postflight_admin_status' : undefined)
|
|
452
552
|
?? (!gatewayCountersMatch ? 'gateway_counter_delta' : undefined);
|
|
453
553
|
console.log(JSON.stringify({
|
|
454
|
-
path: '
|
|
554
|
+
path: 'founder-demo-agent',
|
|
455
555
|
readiness: { status: readiness.status, passed: readiness.passed, failedChecks: readiness.failedChecks },
|
|
456
556
|
launcherStatus,
|
|
457
557
|
adminStatus: { before: adminBefore.status, after: adminAfter.status },
|
|
@@ -479,11 +579,12 @@ async function trialRegisteredAdapter() {
|
|
|
479
579
|
}));
|
|
480
580
|
}
|
|
481
581
|
catch {
|
|
482
|
-
console.log(JSON.stringify({ path: '
|
|
582
|
+
console.log(JSON.stringify({ path: 'founder-demo-agent', status: 'stopped', failurePhase: 'local_setup' }));
|
|
483
583
|
}
|
|
484
584
|
}
|
|
485
585
|
async function serve(args, passwordInput) {
|
|
486
586
|
const port = readPort(args);
|
|
587
|
+
const internalTrialOneProof = args.includes('--internal-trial-one-proof');
|
|
487
588
|
const statePaths = sidecarStatePaths(stateDirectory());
|
|
488
589
|
const localToken = createLoopbackToken();
|
|
489
590
|
const { credential, witnessProvider, proofMetrics, exchangeMetrics, slotLedger, prove } = await loadBaseClientRuntime(passwordInput);
|
|
@@ -492,6 +593,7 @@ async function serve(args, passwordInput) {
|
|
|
492
593
|
witnessProvider,
|
|
493
594
|
prove,
|
|
494
595
|
slotLedger,
|
|
596
|
+
internalTrialOneProof,
|
|
495
597
|
lifecycle: exchangeMetrics.observe,
|
|
496
598
|
});
|
|
497
599
|
const gatewayBaseUrl = process.env.ZK_CREDITS_GATEWAY_URL || DEFAULT_GATEWAY_URL;
|
|
@@ -499,6 +601,7 @@ async function serve(args, passwordInput) {
|
|
|
499
601
|
localToken,
|
|
500
602
|
gatewayBaseUrl,
|
|
501
603
|
prepaidClient: prepaid.client,
|
|
604
|
+
internalTrialOneProof,
|
|
502
605
|
metrics: () => ({ ...proofMetrics.snapshot(), exchange: exchangeMetrics.snapshot() }),
|
|
503
606
|
});
|
|
504
607
|
let address;
|
|
@@ -538,8 +641,17 @@ async function main() {
|
|
|
538
641
|
await serve(args.slice(1).filter((argument) => argument !== '--credential-password-stdin'), passwordFromStdin);
|
|
539
642
|
return;
|
|
540
643
|
}
|
|
541
|
-
if (args[0] === '
|
|
542
|
-
|
|
644
|
+
if (args[0] === 'founder-demo-agent') {
|
|
645
|
+
if (args.length !== 1)
|
|
646
|
+
throw new Error('founder-demo-agent does not accept arguments; it reads the task locally');
|
|
647
|
+
const task = await readFounderDemoTask();
|
|
648
|
+
await trialRegisteredAdapter(task);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if (args[0] === 'x402-agent') {
|
|
652
|
+
if (args.length !== 1)
|
|
653
|
+
throw new Error('x402-agent does not accept arguments; it reads tasks locally');
|
|
654
|
+
await runX402Agent();
|
|
543
655
|
return;
|
|
544
656
|
}
|
|
545
657
|
const statePaths = sidecarStatePaths(sidecarHome);
|
package/dist/codex-profile.js
CHANGED
|
@@ -70,7 +70,7 @@ export function codexModelsResponse() {
|
|
|
70
70
|
apply_patch_tool_type: 'freeform',
|
|
71
71
|
web_search_tool_type: 'text',
|
|
72
72
|
truncation_policy: { mode: 'tokens', limit: 10_000 },
|
|
73
|
-
supports_parallel_tool_calls:
|
|
73
|
+
supports_parallel_tool_calls: false,
|
|
74
74
|
supports_image_detail_original: false,
|
|
75
75
|
context_window: 128_000,
|
|
76
76
|
max_context_window: 128_000,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Freshness and readiness checks used by the separately registered adapter
|
|
3
|
+
* trial. Keep this gate fail-closed when a gateway omits a named check.
|
|
4
|
+
*
|
|
5
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
*/
|
|
7
|
+
export declare const GATEWAY_READINESS_CHECK_NAMES: readonly ["launchControl", "database", "baseRoot", "baseRpc", "verifierAssets", "provider"];
|
|
8
|
+
export declare function isGatewayStatusFresh(value: unknown, now?: number): boolean;
|
|
9
|
+
export declare function evaluateGatewayReadiness(status: number | null, body: unknown, now?: number): {
|
|
10
|
+
passed: boolean;
|
|
11
|
+
failedChecks: string[];
|
|
12
|
+
timestampFresh: boolean;
|
|
13
|
+
};
|