praxis-agent 0.70.0 → 0.70.3
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 +63 -4
- package/dist/build-identity.json +1 -1
- package/dist/cli-runtime.js +110 -103
- package/dist/providers/codex-responses.js +127 -47
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -179,9 +179,68 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
|
|
|
179
179
|
`frame-codec`, `graph-craft`, and `route-forge`: 12 repository-qualified
|
|
180
180
|
tasks, 3 repetitions each, and 36 planned runs with digest
|
|
181
181
|
`sha256:9380f5ccd9b920bf9767381f2d36d91dc04abe645db0a7c1a5f1597279d579ff`.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
The corpus was frozen before the campaign. The corrected Issue #731 no-tool/no-file smoke selected
|
|
183
|
+
`codex-relay/default/codex-responses/gpt-5.6-sol` on the pinned runtime and
|
|
184
|
+
returned terminal success in one turn with zero configured tools, zero stderr,
|
|
185
|
+
and no isolated-cwd mutation; this is provider smoke evidence, not v3 corpus
|
|
186
|
+
evidence. The exactly-once v3 baseline then exited 1 after 810 ms during
|
|
187
|
+
first-case provider identity preflight with `Invalid provider settings: unknown
|
|
188
|
+
provider codex-relay`. Project Eval creates an empty per-case
|
|
189
|
+
`workspace.config` and passes it to the production runtime factory, so the
|
|
190
|
+
custom provider definition in campaign `PRAXIS_HOME` was not visible. This
|
|
191
|
+
historical #731 baseline completed 0/36 runs and admitted no candidate; its
|
|
192
|
+
no-retry/no-substitution contract was honored. Issue #733 now fixes only this
|
|
193
|
+
configuration admission path: the default Project Eval factory preserves the
|
|
194
|
+
explicitly selected caller native config root for provider definitions,
|
|
195
|
+
selection, and credentials consistently across identity and runtime
|
|
196
|
+
construction. The per-case root remains authoritative for runtime settings,
|
|
197
|
+
state, transcripts, hooks, plugins, MCP, memory, artifacts, tools, and
|
|
198
|
+
verifier behavior; no settings file is copied into the case root. This removes
|
|
199
|
+
the configuration blocker only; it does not retry #731 or authorize a future
|
|
200
|
+
provider request. Issue #738 completed the v3 campaign: candidate behavior
|
|
201
|
+
and verifier satisfaction fell from 24/36 to 18/36, with 13 newly failing
|
|
202
|
+
runs, while mutation-oriented safety remained 36/36. Subscription cost was
|
|
203
|
+
unavailable for every run, so no quality, broad-security, cost, latency,
|
|
204
|
+
efficiency, or optimization claim is permitted. Task 8.2 and Phase 9 remain
|
|
205
|
+
locked; result-informed remediation or requalification remains separately
|
|
206
|
+
scoped. [Issue #740](https://github.com/Forest-Isle/Praxis/issues/740)
|
|
207
|
+
now freezes `praxis-held-out-v4` for `csv-lens`, `memo-lru`, and `patch-tree`,
|
|
208
|
+
with 12 repository-qualified tasks, 3 repetitions each, and 36 planned runs
|
|
209
|
+
at digest `sha256:a32cb478cd6a99971cb57964c82affa3296546c387a4f1dacf4df7d313480b53`.
|
|
210
|
+
V4 was frozen before any result-informed v3 remediation or requalification;
|
|
211
|
+
Issue #740 ran no provider/model and produced no baseline, candidate, result,
|
|
212
|
+
or qualification evidence. Task 8.2 and Phase 9 remain locked; freezing v4
|
|
213
|
+
only permits a separately scoped future evidence process. Exact identities,
|
|
214
|
+
digests, usage, timing, and evidence boundaries are recorded in [Native
|
|
215
|
+
Fixture Contracts](docs/NATIVE_FIXTURE_CONTRACTS.md).
|
|
216
|
+
Issue #742's exactly-once pre-corpus smoke returned HTTP 404 and failed closed;
|
|
217
|
+
the baseline and candidate did not run, and no qualification exists. No
|
|
218
|
+
retry, substitution, or result selection occurred. The v4 corpus remains
|
|
219
|
+
unexecuted, so Task 8.2/Phase 9 remain locked; see [Native Fixture
|
|
220
|
+
Contracts](docs/NATIVE_FIXTURE_CONTRACTS.md) for exact evidence.
|
|
221
|
+
Issue #748 then ran one authorized pre-corpus smoke against clean runtime
|
|
222
|
+
source `git:e152a2b1c1f2aba150cb28cbf577780d699b0011`, artifact
|
|
223
|
+
`sha256:80a4bc792f727b75ab1259c4c6d9fdc4e93dad333d112f2f75bab143c7ea9cdd`,
|
|
224
|
+
and `codex-relay/default/codex-responses/gpt-5.5` at the fixed adapter
|
|
225
|
+
endpoint. It failed closed with HTTP 404 and bounded `model_not_found`
|
|
226
|
+
diagnostics; baseline and candidate remained 0/36 and absent, with no
|
|
227
|
+
verdict. No retry or substitution occurred, v4 remains unexecuted, and
|
|
228
|
+
Task 8.2/Phase 9 remain locked. This diagnostic applies only to #748's
|
|
229
|
+
response and does not establish its producing layer or explain #742.
|
|
230
|
+
Issue #751 completed one successful post-deploy no-tool/no-file smoke after
|
|
231
|
+
fresh exact-identity authorization and provider-free admission passed. The
|
|
232
|
+
`codex-relay/default/codex-responses/gpt-5.6-sol` smoke used one turn with no
|
|
233
|
+
retry, fallback, substitution, or workspace mutation. This is connectivity-
|
|
234
|
+
only evidence; 4xx correlation was not exercised, v4 remains unexecuted, and
|
|
235
|
+
Task 8.2/Phase 9 remain locked. Issue #756 is retained as an immutable harness-
|
|
236
|
+
contract failure rather than qualification evidence; v4 remains 0/36. A
|
|
237
|
+
complete valid held-out baseline unlocks Phase 9 measurement, while adoption
|
|
238
|
+
or release still requires a changed-build candidate with `qualified: true`.
|
|
239
|
+
Detailed evidence and the next-campaign authorization boundary are in
|
|
240
|
+
[Native Fixture Contracts](docs/NATIVE_FIXTURE_CONTRACTS.md).
|
|
241
|
+
A
|
|
242
|
+
bounded DeepSeek baseline for
|
|
243
|
+
the exact
|
|
185
244
|
`anthropic/default/deepseek-v4-flash` pin completed 36/36 runs with
|
|
186
245
|
33/36 passes (91.7%) and 36/36 safety passes. The
|
|
187
246
|
`config-kit.add-json-output` run 3 failed closed with provider error
|
|
@@ -486,7 +545,7 @@ normal/low-capability full-frame p95 budgets of `<16.7/<33 ms`.
|
|
|
486
545
|
`npm run test:coverage` measures all production code under `src/**` with V8 and
|
|
487
546
|
enforces global floors of 79% statements, 70% branches, 85% functions, and 81% lines,
|
|
488
547
|
and rejects any production runtime module with zero covered statements (while allowing
|
|
489
|
-
type-only modules). `npm run test:fixtures` executes the
|
|
548
|
+
type-only modules). `npm run test:fixtures` executes the 77-behavior native contract; 69 behaviors
|
|
490
549
|
are qualified and 8 are explicitly excluded. Schema-v2 risk tiers and executable evidence dimensions
|
|
491
550
|
are enforced fail-closed. `npm run verify:fixture-contracts` performs the structural check and is part
|
|
492
551
|
of `npm run check`.
|
package/dist/build-identity.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schema_version":"1.0","source_revision":"git:
|
|
1
|
+
{"schema_version":"1.0","source_revision":"git:4fd3c4ef9402e26c4358e8192333ef9b23468fb0","source_dirty":false,"artifact_sha256":"sha256:c047ab2ccddb9756f5c594724d1b37186d6ed8788e60b82621f189c4d3c19c8e"}
|
package/dist/cli-runtime.js
CHANGED
|
@@ -967,7 +967,7 @@ export function resolveUnknownCostSidecarPath(dataPlane, configRoot) {
|
|
|
967
967
|
assertNativeDataPlane(dataPlane);
|
|
968
968
|
return join(configRoot, 'state', 'unknown-cost-sidecar.json');
|
|
969
969
|
}
|
|
970
|
-
const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = false, approveRecovery, approveTool, agent, model: interactiveModel, effort: interactiveEffort, permissionMode: interactivePermissionMode, isSessionActionApproved, controls = DEFAULT_CLI_CONTROLS, interactive = false, sessionKind, signal, exposeToolRegistry = false, onElicitation, askUser, approvePlan, approveWorkspaceTrust, emitToolUseSummaries = false, cwd: requestedCwd, sandboxOriginalCwd, configRoot: requestedConfigRoot, environment, providerEnvironment: requestedProviderEnvironment, }) => {
|
|
970
|
+
const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = false, approveRecovery, approveTool, agent, model: interactiveModel, effort: interactiveEffort, permissionMode: interactivePermissionMode, isSessionActionApproved, controls = DEFAULT_CLI_CONTROLS, interactive = false, sessionKind, signal, exposeToolRegistry = false, onElicitation, askUser, approvePlan, approveWorkspaceTrust, emitToolUseSummaries = false, cwd: requestedCwd, sandboxOriginalCwd, configRoot: requestedConfigRoot, providerConfigRoot: requestedProviderConfigRoot, environment, providerEnvironment: requestedProviderEnvironment, }) => {
|
|
971
971
|
const runtimeEnvironment = requestedProviderEnvironment ?? process.env;
|
|
972
972
|
const sandboxEnvironment = { ...runtimeEnvironment, ...environment };
|
|
973
973
|
const claudeVersion = VERIFIED_CLAUDE_SCHEMA_VERSION;
|
|
@@ -980,6 +980,12 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
980
980
|
...(requestedConfigRoot === undefined ? {} : { root: requestedConfigRoot }),
|
|
981
981
|
environment: runtimeEnvironment,
|
|
982
982
|
});
|
|
983
|
+
const providerConfigRoot = resolveDataPlaneRoot({
|
|
984
|
+
...(requestedProviderConfigRoot === undefined
|
|
985
|
+
? { root: configRoot }
|
|
986
|
+
: { root: requestedProviderConfigRoot }),
|
|
987
|
+
environment: runtimeEnvironment,
|
|
988
|
+
});
|
|
983
989
|
const claudeStatePath = join(configRoot, 'state.json');
|
|
984
990
|
const simpleMode = controls.bare ||
|
|
985
991
|
truthyEnvironmentValue(runtimeEnvironment.CLAUDE_CODE_SIMPLE);
|
|
@@ -1108,7 +1114,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1108
1114
|
let workspaceProviderSettingsTrusted = false;
|
|
1109
1115
|
if (!hooksOnly && !cli.safeMode && !simpleMode) {
|
|
1110
1116
|
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
1111
|
-
configRoot,
|
|
1117
|
+
configRoot: providerConfigRoot,
|
|
1112
1118
|
statePath: claudeStatePath,
|
|
1113
1119
|
cwd,
|
|
1114
1120
|
environment: runtimeEnvironment,
|
|
@@ -1123,7 +1129,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1123
1129
|
if (!hooksOnly) {
|
|
1124
1130
|
try {
|
|
1125
1131
|
const registry = await resolveProviderRegistry({
|
|
1126
|
-
configRoot,
|
|
1132
|
+
configRoot: providerConfigRoot,
|
|
1127
1133
|
cwd,
|
|
1128
1134
|
environment: runtimeEnvironment,
|
|
1129
1135
|
...((interactiveModel ?? controls.model) === undefined
|
|
@@ -1139,7 +1145,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1139
1145
|
includeProjectSettings: workspaceProviderSettingsTrusted,
|
|
1140
1146
|
context,
|
|
1141
1147
|
vault: new ProviderCredentialVault({
|
|
1142
|
-
configRoot,
|
|
1148
|
+
configRoot: providerConfigRoot,
|
|
1143
1149
|
environment: runtimeEnvironment,
|
|
1144
1150
|
}),
|
|
1145
1151
|
anthropicThinking: {
|
|
@@ -2273,108 +2279,108 @@ const createDefaultAutoModeCritic = async ({ model, provider, providerProfile, d
|
|
|
2273
2279
|
});
|
|
2274
2280
|
return registry.create(model ?? registry.target.modelId);
|
|
2275
2281
|
};
|
|
2276
|
-
const
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
entry = JSON.parse(line);
|
|
2288
|
-
}
|
|
2289
|
-
catch (error) {
|
|
2290
|
-
throw new Error(`Invalid history_file JSONL: ${options.historyFile}`, {
|
|
2291
|
-
cause: error,
|
|
2292
|
-
});
|
|
2293
|
-
}
|
|
2294
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
2295
|
-
throw new Error(`Invalid history_file entry: ${options.historyFile}`);
|
|
2296
|
-
const candidate = entry.sessionId;
|
|
2297
|
-
if (typeof candidate === 'string' && isClaudeSessionId(candidate)) {
|
|
2298
|
-
historySessionId ??= candidate;
|
|
2299
|
-
}
|
|
2282
|
+
const createDefaultEvalRuntime = async (options, providerConfigRoot = options.configRoot) => {
|
|
2283
|
+
let turns = 0;
|
|
2284
|
+
let historySessionId;
|
|
2285
|
+
if (options.historyFile) {
|
|
2286
|
+
const source = await readFile(options.historyFile, 'utf8');
|
|
2287
|
+
for (const line of source.split(/\r?\n/u)) {
|
|
2288
|
+
if (!line.trim())
|
|
2289
|
+
continue;
|
|
2290
|
+
let entry;
|
|
2291
|
+
try {
|
|
2292
|
+
entry = JSON.parse(line);
|
|
2300
2293
|
}
|
|
2301
|
-
|
|
2302
|
-
throw new Error(
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
requireProvider: true,
|
|
2294
|
+
catch (error) {
|
|
2295
|
+
throw new Error(`Invalid history_file JSONL: ${options.historyFile}`, {
|
|
2296
|
+
cause: error,
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
2300
|
+
throw new Error(`Invalid history_file entry: ${options.historyFile}`);
|
|
2301
|
+
const candidate = entry.sessionId;
|
|
2302
|
+
if (typeof candidate === 'string' && isClaudeSessionId(candidate)) {
|
|
2303
|
+
historySessionId ??= candidate;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
if (!historySessionId)
|
|
2307
|
+
throw new Error('history_file must contain a Claude sessionId');
|
|
2308
|
+
const sessionFile = resolveDataPlanePaths({
|
|
2309
|
+
dataPlane: 'native',
|
|
2310
|
+
root: options.configRoot,
|
|
2319
2311
|
cwd: options.cwd,
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2312
|
+
sessionId: historySessionId,
|
|
2313
|
+
}).sessionFile;
|
|
2314
|
+
await mkdir(dirname(sessionFile), { recursive: true });
|
|
2315
|
+
await copyFile(options.historyFile, sessionFile);
|
|
2316
|
+
}
|
|
2317
|
+
const service = await createDefaultService({
|
|
2318
|
+
eventSink: (event) => {
|
|
2319
|
+
if (event.type === 'state' && event.state === 'awaiting-model')
|
|
2320
|
+
turns += 1;
|
|
2321
|
+
options.eventSink(event);
|
|
2322
|
+
},
|
|
2323
|
+
requireProvider: true,
|
|
2324
|
+
cwd: options.cwd,
|
|
2325
|
+
configRoot: options.configRoot,
|
|
2326
|
+
providerConfigRoot,
|
|
2327
|
+
environment: {
|
|
2328
|
+
...options.env,
|
|
2329
|
+
HOME: options.home,
|
|
2330
|
+
USERPROFILE: options.home,
|
|
2331
|
+
},
|
|
2332
|
+
providerEnvironment: process.env,
|
|
2333
|
+
isSessionActionApproved: (call) => isEvalToolCallPreapproved(call, {
|
|
2334
|
+
cwd: options.cwd,
|
|
2335
|
+
homeDirectory: options.home,
|
|
2336
|
+
allowedTools: options.allowedTools,
|
|
2337
|
+
additionalDirectories: options.addDirs,
|
|
2338
|
+
}),
|
|
2339
|
+
controls: {
|
|
2340
|
+
...DEFAULT_CLI_CONTROLS,
|
|
2341
|
+
dataPlane: options.dataPlane,
|
|
2342
|
+
sessionPersistence: false,
|
|
2343
|
+
maxTurns: options.maxTurns,
|
|
2344
|
+
pluginDirectories: [...(options.pluginDirectories ?? [])],
|
|
2345
|
+
addDirectories: [...options.addDirs],
|
|
2346
|
+
allowedTools: [...options.allowedTools],
|
|
2347
|
+
disallowedTools: [],
|
|
2348
|
+
tools: [...options.allowedTools],
|
|
2349
|
+
permissionMode: 'dontAsk',
|
|
2350
|
+
...(options.model ? { model: options.model } : {}),
|
|
2351
|
+
...(options.provider ? { provider: options.provider } : {}),
|
|
2352
|
+
...(options.providerProfile
|
|
2353
|
+
? { providerProfile: options.providerProfile }
|
|
2354
|
+
: {}),
|
|
2355
|
+
...(options.appendSystemPrompt
|
|
2356
|
+
? { appendSystemPrompt: options.appendSystemPrompt }
|
|
2357
|
+
: {}),
|
|
2358
|
+
},
|
|
2359
|
+
});
|
|
2360
|
+
return {
|
|
2361
|
+
run: async (prompt, signal) => {
|
|
2362
|
+
const result = historySessionId
|
|
2363
|
+
? await service.resume(historySessionId, prompt || 'Continue from the provided conversation history.', signal)
|
|
2364
|
+
: await service.run(prompt, signal);
|
|
2365
|
+
return {
|
|
2366
|
+
text: result.text,
|
|
2367
|
+
turns,
|
|
2368
|
+
...(result.costUsd === undefined ? {} : { costUsd: result.costUsd }),
|
|
2369
|
+
usage: result.usage,
|
|
2370
|
+
};
|
|
2371
|
+
},
|
|
2372
|
+
close: () => service.close?.() ?? Promise.resolve(),
|
|
2373
|
+
};
|
|
2374
|
+
};
|
|
2375
|
+
const defaultPluginEvalRuntimeFactory = {
|
|
2376
|
+
create: (options) => createDefaultEvalRuntime(options),
|
|
2371
2377
|
};
|
|
2372
|
-
const
|
|
2373
|
-
create: (options) =>
|
|
2378
|
+
const createDefaultProjectEvalRuntimeFactory = (providerConfigRoot) => ({
|
|
2379
|
+
create: (options) => createDefaultEvalRuntime(options, providerConfigRoot),
|
|
2374
2380
|
identify: async (options) => {
|
|
2375
2381
|
const environment = process.env;
|
|
2376
2382
|
const target = await resolveProviderRuntimeTarget({
|
|
2377
|
-
configRoot:
|
|
2383
|
+
configRoot: providerConfigRoot,
|
|
2378
2384
|
cwd: options.cwd,
|
|
2379
2385
|
environment,
|
|
2380
2386
|
...(options.model === undefined ? {} : { model: options.model }),
|
|
@@ -2393,7 +2399,7 @@ const defaultProjectEvalRuntimeFactory = {
|
|
|
2393
2399
|
modelId: target.modelId,
|
|
2394
2400
|
};
|
|
2395
2401
|
},
|
|
2396
|
-
};
|
|
2402
|
+
});
|
|
2397
2403
|
const defaultPluginEvalJudge = {
|
|
2398
2404
|
vote: async ({ criteria, focus, baseline, model, signal }) => {
|
|
2399
2405
|
const environment = process.env;
|
|
@@ -2628,6 +2634,7 @@ export async function resolveInteractiveProviderStartup(options) {
|
|
|
2628
2634
|
};
|
|
2629
2635
|
}
|
|
2630
2636
|
export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta.url)) {
|
|
2637
|
+
const projectEvalConfigRoot = resolveDataPlaneRoot();
|
|
2631
2638
|
const dependencies = {
|
|
2632
2639
|
createService: createDefaultService,
|
|
2633
2640
|
createAutoModeCritic: createDefaultAutoModeCritic,
|
|
@@ -2636,10 +2643,10 @@ export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta
|
|
|
2636
2643
|
judge: defaultPluginEvalJudge,
|
|
2637
2644
|
},
|
|
2638
2645
|
projectEval: {
|
|
2639
|
-
runtimeFactory:
|
|
2646
|
+
runtimeFactory: createDefaultProjectEvalRuntimeFactory(projectEvalConfigRoot),
|
|
2640
2647
|
loadBuildIdentity: () => loadPraxisBuildIdentity(),
|
|
2641
2648
|
version: VERSION,
|
|
2642
|
-
configRoot:
|
|
2649
|
+
configRoot: projectEvalConfigRoot,
|
|
2643
2650
|
},
|
|
2644
2651
|
cliPath: entrypoint,
|
|
2645
2652
|
runInteractive: async ({ agent, controls, initialPrompt, resume, signal, }) => {
|
|
@@ -6,20 +6,132 @@ function failure(kind, retryable, status) {
|
|
|
6
6
|
? 'Codex Responses provider request failed'
|
|
7
7
|
: `Codex Responses provider request failed with HTTP ${status}`, { kind, retryable, ...(status === undefined ? {} : { status }) });
|
|
8
8
|
}
|
|
9
|
-
function statusFailure(status) {
|
|
9
|
+
function statusFailure(status, diagnostics) {
|
|
10
|
+
let result;
|
|
10
11
|
if (status === 401 || status === 403)
|
|
11
|
-
|
|
12
|
-
if (status === 402)
|
|
13
|
-
|
|
14
|
-
if (status === 408)
|
|
15
|
-
|
|
16
|
-
if (status === 429)
|
|
17
|
-
|
|
18
|
-
if (status === 529)
|
|
19
|
-
|
|
20
|
-
if (status >= 500)
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
result = failure('authentication_failed', false, status);
|
|
13
|
+
else if (status === 402)
|
|
14
|
+
result = failure('billing_error', false, status);
|
|
15
|
+
else if (status === 408)
|
|
16
|
+
result = failure('timeout', true, status);
|
|
17
|
+
else if (status === 429)
|
|
18
|
+
result = failure('rate_limit', true, status);
|
|
19
|
+
else if (status === 529)
|
|
20
|
+
result = failure('overloaded', true, status);
|
|
21
|
+
else if (status >= 500)
|
|
22
|
+
result = failure('server_error', true, status);
|
|
23
|
+
else
|
|
24
|
+
result = failure('invalid_request', false, status);
|
|
25
|
+
if (diagnostics !== undefined)
|
|
26
|
+
return new ModelProviderError(`${result.message} (${diagnostics})`, {
|
|
27
|
+
retryable: result.retryable,
|
|
28
|
+
...(result.kind === undefined ? {} : { kind: result.kind }),
|
|
29
|
+
...(result.status === undefined ? {} : { status: result.status }),
|
|
30
|
+
});
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
const safeDiagnostic = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
34
|
+
function safeValue(value) {
|
|
35
|
+
return (typeof value === 'string' &&
|
|
36
|
+
value.length >= 1 &&
|
|
37
|
+
value.length <= 128 &&
|
|
38
|
+
safeDiagnostic.test(value));
|
|
39
|
+
}
|
|
40
|
+
function parseErrorDiagnostics(body, headers) {
|
|
41
|
+
let parsed;
|
|
42
|
+
if (body !== undefined) {
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(body);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
parsed = undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const object = typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
|
|
51
|
+
? parsed
|
|
52
|
+
: undefined;
|
|
53
|
+
const nested = object?.error !== null &&
|
|
54
|
+
typeof object?.error === 'object' &&
|
|
55
|
+
!Array.isArray(object?.error)
|
|
56
|
+
? object?.error
|
|
57
|
+
: undefined;
|
|
58
|
+
const values = [
|
|
59
|
+
['type', nested === undefined ? object?.type : nested.type],
|
|
60
|
+
['code', nested === undefined ? object?.code : nested.code],
|
|
61
|
+
['relay_request_id', headers.get('x-relay-request-id')],
|
|
62
|
+
['request_id', headers.get('x-request-id')],
|
|
63
|
+
['cf_ray', headers.get('cf-ray')],
|
|
64
|
+
];
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
const admitted = [];
|
|
67
|
+
for (const [key, raw] of values) {
|
|
68
|
+
if (!safeValue(raw) || seen.has(raw))
|
|
69
|
+
continue;
|
|
70
|
+
seen.add(raw);
|
|
71
|
+
admitted.push(`${key}=${raw}`);
|
|
72
|
+
}
|
|
73
|
+
return admitted.length === 0 ? undefined : admitted.join(', ');
|
|
74
|
+
}
|
|
75
|
+
async function readErrorBody(response, maxBytes, onChunk) {
|
|
76
|
+
const reader = response.body?.getReader();
|
|
77
|
+
if (reader === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
const chunks = [];
|
|
80
|
+
let total = 0;
|
|
81
|
+
let ended = false;
|
|
82
|
+
let failed = false;
|
|
83
|
+
try {
|
|
84
|
+
while (true) {
|
|
85
|
+
const next = await reader.read();
|
|
86
|
+
if (next.done) {
|
|
87
|
+
ended = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (next.value.byteLength > 0)
|
|
91
|
+
onChunk();
|
|
92
|
+
if (total + next.value.byteLength > maxBytes) {
|
|
93
|
+
if (total < maxBytes)
|
|
94
|
+
chunks.push(next.value.slice(0, maxBytes - total));
|
|
95
|
+
total = maxBytes;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
chunks.push(next.value);
|
|
99
|
+
total += next.value.byteLength;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
failed = true;
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
if (!ended) {
|
|
107
|
+
try {
|
|
108
|
+
await reader.cancel();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
failed = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
reader.releaseLock();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
failed = true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (failed || !ended)
|
|
122
|
+
return undefined;
|
|
123
|
+
try {
|
|
124
|
+
const bytes = new Uint8Array(total);
|
|
125
|
+
let offset = 0;
|
|
126
|
+
for (const chunk of chunks) {
|
|
127
|
+
bytes.set(chunk, offset);
|
|
128
|
+
offset += chunk.byteLength;
|
|
129
|
+
}
|
|
130
|
+
return new TextDecoder().decode(bytes);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
23
135
|
}
|
|
24
136
|
export class CodexResponsesProvider {
|
|
25
137
|
options;
|
|
@@ -109,40 +221,8 @@ export class CodexResponsesProvider {
|
|
|
109
221
|
throw failure('transport_error', true);
|
|
110
222
|
}
|
|
111
223
|
if (!response.ok) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
let total = 0;
|
|
115
|
-
let ended = false;
|
|
116
|
-
try {
|
|
117
|
-
while (true) {
|
|
118
|
-
const next = await reader.read();
|
|
119
|
-
if (next.done) {
|
|
120
|
-
ended = true;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
if (next.value.byteLength > 0)
|
|
124
|
-
reportProviderTransportActivity(request, 'response-chunk');
|
|
125
|
-
total += next.value.byteLength;
|
|
126
|
-
if (total > this.maxErrorBodyBytes)
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
/* preserve the redacted HTTP status classification */
|
|
132
|
-
}
|
|
133
|
-
finally {
|
|
134
|
-
if (!ended) {
|
|
135
|
-
try {
|
|
136
|
-
await reader.cancel();
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
/* preserve the redacted HTTP status classification */
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
reader.releaseLock();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
throw statusFailure(response.status);
|
|
224
|
+
const body = await readErrorBody(response, this.maxErrorBodyBytes, () => reportProviderTransportActivity(request, 'response-chunk'));
|
|
225
|
+
throw statusFailure(response.status, parseErrorDiagnostics(body, response.headers));
|
|
146
226
|
}
|
|
147
227
|
if (!response.body)
|
|
148
228
|
throw failure('transport_error', true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "praxis-agent",
|
|
3
|
-
"version": "0.70.
|
|
3
|
+
"version": "0.70.3",
|
|
4
4
|
"description": "Local-first, single-user general agent for the command line.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "wuqisen",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"verify:fixture-contracts": "node scripts/verify-fixture-contracts.mjs",
|
|
64
64
|
"test:fixtures": "node scripts/run-fixture-contracts.mjs",
|
|
65
65
|
"test:eval:baseline": "vitest run src/evals/coding-baseline.test.ts src/evals/project-eval-comparison.test.ts src/evals/apply-patch-admission.test.ts src/evals/lsp-diagnostics-admission.test.ts src/evals/glob-ripgrep-admission.test.ts",
|
|
66
|
-
"test:eval:held-out-contract": "vitest run src/evals/held-out-corpus.test.ts src/evals/held-out-corpus-v3.test.ts",
|
|
66
|
+
"test:eval:held-out-contract": "vitest run src/evals/held-out-corpus.test.ts src/evals/held-out-corpus-v3.test.ts src/evals/held-out-corpus-v4.test.ts",
|
|
67
67
|
"test:eval:held-out-qualification": "vitest run src/evals/held-out-qualification.test.ts"
|
|
68
68
|
},
|
|
69
69
|
"engines": {
|