auxilo-mcp 0.9.24 → 0.9.26
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/bin/auxilo-cli.js +18 -14
- package/mcp-server.js +1 -1
- package/package.json +1 -1
- package/scripts/extract-local.js +8 -7
- package/scripts/providers/claude-code.js +33 -3
- package/scripts/providers/codex-cli.js +7 -0
- package/scripts/providers/index.js +60 -35
- package/scripts/providers/provider.interface.js +1 -1
- package/scripts/providers/schemas/extraction-envelope.schema.json +35 -24
- package/scripts/providers/schemas/judge-decisions.schema.json +7 -5
package/bin/auxilo-cli.js
CHANGED
|
@@ -167,12 +167,12 @@ const CONSENT_TEXT = `
|
|
|
167
167
|
• READS the session transcript on your machine,
|
|
168
168
|
• SCRUBS it locally (sensitivity filter: API keys, tokens, emails, PII
|
|
169
169
|
are redacted first),
|
|
170
|
-
• EXTRACTS reusable learnings locally through
|
|
171
|
-
are signed in to
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
account
|
|
175
|
-
|
|
170
|
+
• EXTRACTS reusable learnings locally through Claude Code, when you
|
|
171
|
+
are signed in to it, or a provider key you set yourself. For this
|
|
172
|
+
step your scrubbed transcript goes only to that provider, under
|
|
173
|
+
your own account with them, and any use is charged to that
|
|
174
|
+
account, never to Auxilo. It is never sent to Auxilo, raw or
|
|
175
|
+
scrubbed.
|
|
176
176
|
• UPLOADS only the finished learning drafts (title, body, category,
|
|
177
177
|
tags, task context, outcome) to Auxilo (${'POST /learn'}). Everything
|
|
178
178
|
waits in your review queue until you approve it, one learning at a
|
|
@@ -645,7 +645,7 @@ const CLI_CLEAN_LANE_CALIBRATED_PROVIDERS = ['claude-code'];
|
|
|
645
645
|
* that with two read-only, no-detect sources of TRUTH, in priority order:
|
|
646
646
|
* (1) AUXILO_EXTRACTION_PROVIDER, if set, is a certain fact about the
|
|
647
647
|
* current session's config — reading it is not a guess — validated
|
|
648
|
-
* against providers.
|
|
648
|
+
* against providers.KNOWN_PROVIDER_IDS exactly as resolveProvider() itself
|
|
649
649
|
* validates an override, without calling it.
|
|
650
650
|
* (2) Otherwise, providers.json's `selected` field — the LAST provider a
|
|
651
651
|
* genuine resolveProvider() full-scan actually chose and persisted
|
|
@@ -661,10 +661,10 @@ const CLI_CLEAN_LANE_CALIBRATED_PROVIDERS = ['claude-code'];
|
|
|
661
661
|
function lastRecordedProviderResolution() {
|
|
662
662
|
const override = process.env.AUXILO_EXTRACTION_PROVIDER;
|
|
663
663
|
if (override) {
|
|
664
|
-
if (providers.
|
|
664
|
+
if (providers.KNOWN_PROVIDER_IDS.includes(override)) return { ok: true, id: override };
|
|
665
665
|
return {
|
|
666
666
|
ok: false,
|
|
667
|
-
reason: `AUXILO_EXTRACTION_PROVIDER="${override}" is not a known provider (expected one of: ${providers.
|
|
667
|
+
reason: `AUXILO_EXTRACTION_PROVIDER="${override}" is not a known provider (expected one of: ${providers.KNOWN_PROVIDER_IDS.join(', ')})`,
|
|
668
668
|
};
|
|
669
669
|
}
|
|
670
670
|
try {
|
|
@@ -709,7 +709,9 @@ function extractionProviderLine(resolution) {
|
|
|
709
709
|
* 'no-model-provider-available' (nothing even LOOKED usable at the detect()
|
|
710
710
|
* stage) — this is the selection-fall-through exhaustion code from
|
|
711
711
|
* scripts/providers/index.js's runModel(), where every provider in
|
|
712
|
-
*
|
|
712
|
+
* AUTOMATIC_PROVIDER_ORDER was actually tried and each failed for its own
|
|
713
|
+
* reason. codex-cli remains reachable only through the explicit override,
|
|
714
|
+
* which never enters this fallback walk.
|
|
713
715
|
*/
|
|
714
716
|
const STATUS_WORTHY_SKIP_REASON_CODES = Object.freeze([
|
|
715
717
|
'cli-billing-helper-configured',
|
|
@@ -1513,7 +1515,7 @@ async function cmdProvider(flags) {
|
|
|
1513
1515
|
}
|
|
1514
1516
|
throw err;
|
|
1515
1517
|
}
|
|
1516
|
-
console.log(`\n✓ Saved to ${written} (mode 0600). This machine
|
|
1518
|
+
console.log(`\n✓ Saved to ${written} (mode 0600). This machine drafts through Claude Code first when you are signed in to it; your ${vendor} key only takes over when Claude Code is not usable, and once it does, it keeps drafting even after Claude Code becomes usable again.`);
|
|
1517
1519
|
}
|
|
1518
1520
|
|
|
1519
1521
|
// ─── Entry point ────────────────────────────────────────────────────────────
|
|
@@ -1575,9 +1577,11 @@ available on your account every subcommand says so and changes nothing.
|
|
|
1575
1577
|
learnings keep their 7-day retraction window.`,
|
|
1576
1578
|
provider: `Usage: auxilo provider <status|set|clear>
|
|
1577
1579
|
|
|
1578
|
-
Configure a bring-your-own (BYO) model provider key for local extraction
|
|
1579
|
-
|
|
1580
|
-
|
|
1580
|
+
Configure a bring-your-own (BYO) model provider key for local extraction.
|
|
1581
|
+
Claude Code drafts first when you are signed in to it; your key only
|
|
1582
|
+
takes over when Claude Code is not usable, and once it does, it keeps
|
|
1583
|
+
drafting even after Claude Code becomes usable again. Auxilo never sees
|
|
1584
|
+
or bills this key.
|
|
1581
1585
|
|
|
1582
1586
|
status Show the configured vendor and model (never the key itself).
|
|
1583
1587
|
set Configure a vendor, model, and key. Interactive ONLY: the key is
|
package/mcp-server.js
CHANGED
|
@@ -198,7 +198,7 @@ async function postBulkChunks(headers, decisions) {
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
const server = new Server(
|
|
201
|
-
{ name: 'auxilo', version: '0.9.
|
|
201
|
+
{ name: 'auxilo', version: '0.9.26' },
|
|
202
202
|
{
|
|
203
203
|
capabilities: { tools: {} },
|
|
204
204
|
instructions: `You are connected to Auxilo, a knowledge marketplace where AI agents buy and sell operational learnings.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auxilo-mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.26",
|
|
4
4
|
"mcpName": "io.github.silent-architects/auxilo",
|
|
5
5
|
"description": "MCP server for Auxilo. Your agent stops solving the same problem twice: auto-extracted learnings, free self-unlocks, and earnings when other agents unlock yours.",
|
|
6
6
|
"main": "mcp-server.js",
|
package/scripts/extract-local.js
CHANGED
|
@@ -681,13 +681,14 @@ function formatArgvForLog(argv) {
|
|
|
681
681
|
* PRE_SPAWN_SKIP_REASON_CODES, so they render finder=skipped, not ran. The
|
|
682
682
|
* observed defect lines (finder=ran, flags=n/a) come from a DIFFERENT case:
|
|
683
683
|
* scripts/providers/index.js's runModel() falls through from claude-code to
|
|
684
|
-
* the next
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
684
|
+
* the next automatic provider (byo-key) whenever claude-code's own attempt
|
|
685
|
+
* fails with a NON_RETRYABLE_FOR_THIS_PROVIDER reasonCode, and returns that
|
|
686
|
+
* OTHER provider's result directly when it stops there. codex-cli is not in
|
|
687
|
+
* that automatic walk; it remains reachable only through the explicit env
|
|
688
|
+
* override, which never falls through. A non-Claude provider's result carries
|
|
689
|
+
* no `argv` field at all (argv is a claude-code-only concept), so there is no
|
|
690
|
+
* shipped argv being hidden here — 'unknown' remains the correct, honest
|
|
691
|
+
* `hooks` value regardless of which provider is named.
|
|
691
692
|
*
|
|
692
693
|
* EXTRACTION-MODEL-PROVENANCE (PUNCH-LIST P1) closed the mismatch this
|
|
693
694
|
* docblock used to describe as a known, deferred gap: a fallthrough
|
|
@@ -218,8 +218,9 @@ const SCRUBBED_CLIENT_ENV_VARS = Object.freeze([
|
|
|
218
218
|
// of the identical contents. 0.9.15 (EXTRACTION-CHILD-HOOKS) appends
|
|
219
219
|
// SETTING_SOURCES_ARGS to both — the child loads none of user/project/local
|
|
220
220
|
// settings, so the operator's own SessionStart hooks never fire.
|
|
221
|
-
|
|
222
|
-
const
|
|
221
|
+
// Strict mode with no --mcp-config also excludes account-connected MCP servers.
|
|
222
|
+
const EXTRACT_MODE_ARGV = Object.freeze(['-p', '--no-session-persistence', '--tools', '', ...SETTING_SOURCES_ARGS, '--strict-mcp-config']);
|
|
223
|
+
const JUDGE_MODE_ARGV = Object.freeze(['-p', '--output-format', 'json', '--no-session-persistence', '--tools', '', ...SETTING_SOURCES_ARGS, '--strict-mcp-config']);
|
|
223
224
|
|
|
224
225
|
/**
|
|
225
226
|
* Build the subscription-auth-only environment shared by BOTH the extraction and
|
|
@@ -228,9 +229,23 @@ const JUDGE_MODE_ARGV = Object.freeze(['-p', '--output-format', 'json', '--no-se
|
|
|
228
229
|
function claudeChildEnv() {
|
|
229
230
|
const childEnv = { ...process.env, AUXILO_EXTRACTING: '1' };
|
|
230
231
|
for (const key of SCRUBBED_CLIENT_ENV_VARS) delete childEnv[key];
|
|
232
|
+
childEnv.ENABLE_CLAUDEAI_MCP_SERVERS = 'false';
|
|
231
233
|
return childEnv;
|
|
232
234
|
}
|
|
233
235
|
|
|
236
|
+
// A managed-config refusal is observed after spawn. Keep it out of the
|
|
237
|
+
// pre-spawn skip and provider-fallback sets; never expose the CLI's message.
|
|
238
|
+
function enterpriseMcpRefusal(res, authStatus) {
|
|
239
|
+
if (!Number.isInteger(res.status) || res.status === 0) return null;
|
|
240
|
+
const phrase = 'You cannot use --strict-mcp-config when an enterprise MCP config is present';
|
|
241
|
+
if (![res.stdout, res.stderr].some(value => String(value || '').includes(phrase))) return null;
|
|
242
|
+
return {
|
|
243
|
+
ok: false, text: '', usage: null,
|
|
244
|
+
reason: 'Claude Code refused MCP isolation with managed configuration',
|
|
245
|
+
reasonCode: 'isolation-unverified', authStatus,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
234
249
|
// ─── Billing-helper detector ────────────────────────────────────────────────
|
|
235
250
|
//
|
|
236
251
|
// NOT scrubbable via env: settings.json `apiKeyHelper`, `awsAuthRefresh`,
|
|
@@ -489,6 +504,8 @@ function runExtractMode(opts) {
|
|
|
489
504
|
if (res.error) {
|
|
490
505
|
return { ok: false, text: '', usage: null, reason: `spawn failed (${bin}): ${res.error.message}`, reasonCode: 'unknown', authStatus, argv, cliVersion };
|
|
491
506
|
}
|
|
507
|
+
const mcpRefusal = enterpriseMcpRefusal(res, authStatus);
|
|
508
|
+
if (mcpRefusal) return { ...mcpRefusal, argv, cliVersion };
|
|
492
509
|
// Claude prints auth failures ("API Error: 401 ... Please run /login") to stdout.
|
|
493
510
|
if (/Please run \/login|authentication_error|401/i.test(out) || /Please run \/login|authentication_error/i.test(String(res.stderr || ''))) {
|
|
494
511
|
return {
|
|
@@ -570,6 +587,8 @@ function runJudgeMode(opts) {
|
|
|
570
587
|
if (res.error) {
|
|
571
588
|
return { ok: false, text: '', usage: null, reason: `judge spawn failed (${bin}): ${res.error.message}`, reasonCode: 'unknown', authStatus: 'unknown', argv, cliVersion };
|
|
572
589
|
}
|
|
590
|
+
const mcpRefusal = enterpriseMcpRefusal(res, 'unknown');
|
|
591
|
+
if (mcpRefusal) return { ...mcpRefusal, argv, cliVersion };
|
|
573
592
|
if (/Please run \/login|authentication_error|401/i.test(stdout) || /Please run \/login|authentication_error/i.test(String(res.stderr || ''))) {
|
|
574
593
|
return { ok: false, text: '', usage: null, reason: 'local judge model is not authenticated', reasonCode: 'cli-unauthenticated', authStatus: 'unknown', argv, cliVersion };
|
|
575
594
|
}
|
|
@@ -589,7 +608,18 @@ function runJudgeMode(opts) {
|
|
|
589
608
|
try {
|
|
590
609
|
wrapper = JSON.parse(stdout);
|
|
591
610
|
} catch {
|
|
592
|
-
|
|
611
|
+
// Some CLI builds append a non-JSON MCP SDK diagnostic to stdout. Recover
|
|
612
|
+
// only a single result value; an extra JSON value of ANY type is ambiguous.
|
|
613
|
+
const values = [];
|
|
614
|
+
for (const rawLine of stdout.split('\n')) {
|
|
615
|
+
const line = rawLine.trim();
|
|
616
|
+
if (!line) continue;
|
|
617
|
+
try { values.push(JSON.parse(line)); } catch { /* discard non-JSON noise */ }
|
|
618
|
+
}
|
|
619
|
+
if (values.length !== 1 || !values[0] || typeof values[0] !== 'object' || values[0].type !== 'result') {
|
|
620
|
+
return { ok: false, text: '', usage: null, reason: 'local judge returned malformed JSON wrapper', reasonCode: 'model-error', authStatus: 'unknown', argv, cliVersion };
|
|
621
|
+
}
|
|
622
|
+
[wrapper] = values;
|
|
593
623
|
}
|
|
594
624
|
if (!wrapper || typeof wrapper.result !== 'string' || wrapper.is_error === true) {
|
|
595
625
|
return { ok: false, text: '', usage: null, reason: 'local judge returned no successful result', reasonCode: 'model-error', authStatus: 'unknown', argv, cliVersion };
|
|
@@ -380,6 +380,13 @@ function invoke(opts, mode) {
|
|
|
380
380
|
const stdout = String(res.stdout || '');
|
|
381
381
|
const stderr = String(res.stderr || '');
|
|
382
382
|
const events = parseJsonlEvents(stdout);
|
|
383
|
+
if (eventAuthMessages(events).some(message => message.includes('invalid_json_schema'))) {
|
|
384
|
+
return {
|
|
385
|
+
ok: false, text: '', usage: null,
|
|
386
|
+
reason: 'codex rejected the output schema (invalid_json_schema)',
|
|
387
|
+
reasonCode: 'output-schema-rejected', authStatus: 'unknown',
|
|
388
|
+
};
|
|
389
|
+
}
|
|
383
390
|
if (AUTH_FAILURE_RE.test(stderr) || eventAuthMessages(events).some((message) => AUTH_FAILURE_RE.test(message))) {
|
|
384
391
|
return { ok: false, text: '', usage: null, reason: 'codex CLI reported it is not authenticated', reasonCode: 'cli-unauthenticated', authStatus: 'unknown' };
|
|
385
392
|
}
|
|
@@ -10,20 +10,22 @@
|
|
|
10
10
|
* from ~/.auxilo/providers.json if it is STILL usable (re-verified via its
|
|
11
11
|
* own detect() every call, never trusted blindly — a stale persisted choice
|
|
12
12
|
* falls through to a full re-scan rather than failing) → else the first
|
|
13
|
-
* provider whose detect() is true, in
|
|
14
|
-
* byo-key) → else ok:false, with a reason naming every provider
|
|
13
|
+
* provider whose detect() is true, in automatic order (claude-code →
|
|
14
|
+
* byo-key) → else ok:false, with a reason naming every automatic provider
|
|
15
|
+
* tried. codex-cli remains available only through the explicit env override.
|
|
15
16
|
*
|
|
16
17
|
* runModel(): resolves via resolveProvider(), then actually RUNS it. A
|
|
17
18
|
* non-override resolution that fails with a reasonCode meaning "this
|
|
18
19
|
* provider cannot run at all" (NON_RETRYABLE_FOR_THIS_PROVIDER — e.g.
|
|
19
20
|
* unauthenticated, not installed, a billing helper is configured) falls
|
|
20
|
-
* through to the next provider in
|
|
21
|
-
* hard failure; a working provider that merely failed once
|
|
22
|
-
* error) does not fall through — that is still the builder's
|
|
23
|
-
* provider having a bad run, not a reason to switch under them. An
|
|
24
|
-
* env override never falls through, honoring
|
|
25
|
-
* choice. Every provider exhausted →
|
|
26
|
-
* every attempt's reason summarized in
|
|
21
|
+
* through to the next provider in AUTOMATIC_PROVIDER_ORDER rather than
|
|
22
|
+
* reporting a hard failure; a working provider that merely failed once
|
|
23
|
+
* (timeout, model error) does not fall through — that is still the builder's
|
|
24
|
+
* chosen provider having a bad run, not a reason to switch under them. An
|
|
25
|
+
* explicit env override, including codex-cli, never falls through, honoring
|
|
26
|
+
* the operator's explicit choice. Every automatic provider exhausted →
|
|
27
|
+
* reasonCode 'no-usable-provider' with every attempt's reason summarized in
|
|
28
|
+
* `reason`.
|
|
27
29
|
*
|
|
28
30
|
* codex-cli.js and byo-key.js don't exist yet (PART B/C). loadOptionalProvider()
|
|
29
31
|
* degrades a missing module into a "not installed yet" stub so this file — and
|
|
@@ -71,7 +73,8 @@ function loadOptionalProvider(id, modulePath) {
|
|
|
71
73
|
const codexCli = loadOptionalProvider('codex-cli', './codex-cli.js');
|
|
72
74
|
const byoKey = loadOptionalProvider('byo-key', './byo-key.js');
|
|
73
75
|
|
|
74
|
-
const
|
|
76
|
+
const KNOWN_PROVIDER_IDS = Object.freeze(['claude-code', 'codex-cli', 'byo-key']);
|
|
77
|
+
const AUTOMATIC_PROVIDER_ORDER = Object.freeze(['claude-code', 'byo-key']);
|
|
75
78
|
const PROVIDERS = {
|
|
76
79
|
'claude-code': claudeCode,
|
|
77
80
|
'codex-cli': codexCli,
|
|
@@ -145,19 +148,16 @@ async function resolveProvider(opts = {}) {
|
|
|
145
148
|
if (!Object.prototype.hasOwnProperty.call(PROVIDERS, override)) {
|
|
146
149
|
return {
|
|
147
150
|
ok: false,
|
|
148
|
-
reason: `AUXILO_EXTRACTION_PROVIDER="${override}" is not a known provider (expected one of: ${
|
|
151
|
+
reason: `AUXILO_EXTRACTION_PROVIDER="${override}" is not a known provider (expected one of: ${KNOWN_PROVIDER_IDS.join(', ')})`,
|
|
149
152
|
};
|
|
150
153
|
}
|
|
151
154
|
return { ok: true, id: override, module: PROVIDERS[override] };
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
const cache = opts.providerCache || defaultCache;
|
|
155
|
-
if (cache.resolved) return cache.resolved;
|
|
156
|
-
|
|
157
157
|
// Fast path (EXTRACT-PER-CLIENT W1 FIX, PUNCH-LIST P1, item 3): the
|
|
158
158
|
// persisted `selected` choice — written by a prior successful resolution,
|
|
159
159
|
// possibly in an earlier process — skips re-probing every provider ahead
|
|
160
|
-
// of it in
|
|
160
|
+
// of it in AUTOMATIC_PROVIDER_ORDER when it is STILL usable (the common
|
|
161
161
|
// steady-state case: same builder, same login, repeated extraction
|
|
162
162
|
// calls). "Usable" is re-verified via that provider's own detect() every
|
|
163
163
|
// time, never trusted blindly from the file alone. A stale persisted
|
|
@@ -185,6 +185,26 @@ async function resolveProvider(opts = {}) {
|
|
|
185
185
|
const log = typeof opts.log === 'function' ? opts.log : console.error;
|
|
186
186
|
const statePath = opts.providersStatePath || PROVIDERS_STATE_PATH;
|
|
187
187
|
const persistedState = readProvidersState(statePath);
|
|
188
|
+
|
|
189
|
+
// EPC2-2 E0: codex-cli failed the live isolation sentinel and is therefore
|
|
190
|
+
// ineligible for automatic selection. Treat a cached pre-E0 winner as
|
|
191
|
+
// absent in memory on EVERY resolution, even if the best-effort migration
|
|
192
|
+
// below cannot rewrite providers.json. The explicit env override above is
|
|
193
|
+
// intentionally untouched and remains the only route to codex-cli.
|
|
194
|
+
if (persistedState.selected === 'codex-cli') {
|
|
195
|
+
delete persistedState.selected;
|
|
196
|
+
try {
|
|
197
|
+
if (typeof byoKey.writeProvidersStateAtomic !== 'function') throw new Error('writer unavailable');
|
|
198
|
+
byoKey.writeProvidersStateAtomic(persistedState, opts);
|
|
199
|
+
} catch {
|
|
200
|
+
log('[providers] could not clear retired automatic codex-cli selection; continuing without it');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const cache = opts.providerCache || defaultCache;
|
|
205
|
+
if (cache.resolved && cache.resolved.id !== 'codex-cli') return cache.resolved;
|
|
206
|
+
if (cache.resolved && cache.resolved.id === 'codex-cli') delete cache.resolved;
|
|
207
|
+
|
|
188
208
|
const persistedId = persistedState.selected;
|
|
189
209
|
if (typeof persistedId === 'string' && Object.prototype.hasOwnProperty.call(PROVIDERS, persistedId)) {
|
|
190
210
|
const persistedMod = PROVIDERS[persistedId];
|
|
@@ -203,7 +223,7 @@ async function resolveProvider(opts = {}) {
|
|
|
203
223
|
}
|
|
204
224
|
|
|
205
225
|
const tried = [];
|
|
206
|
-
for (const id of
|
|
226
|
+
for (const id of AUTOMATIC_PROVIDER_ORDER) {
|
|
207
227
|
const mod = PROVIDERS[id];
|
|
208
228
|
tried.push(id);
|
|
209
229
|
let available = false;
|
|
@@ -224,7 +244,7 @@ async function resolveProvider(opts = {}) {
|
|
|
224
244
|
|
|
225
245
|
/**
|
|
226
246
|
* reasonCodes meaning "this provider cannot run at all right now" — safe to
|
|
227
|
-
* try the NEXT provider in
|
|
247
|
+
* try the NEXT provider in AUTOMATIC_PROVIDER_ORDER rather than reporting a hard
|
|
228
248
|
* failure (EXTRACT-PER-CLIENT W1 FIX, PUNCH-LIST P1, item 2). Everything
|
|
229
249
|
* else (timeouts, model errors, malformed output, rate limits) means the
|
|
230
250
|
* chosen provider DID run and failed on THIS call — that is not a signal to
|
|
@@ -241,7 +261,8 @@ const NON_RETRYABLE_FOR_THIS_PROVIDER = new Set([
|
|
|
241
261
|
// EXTRACTION-CHILD-HOOKS (0.9.15): the resolved claude-code CLI doesn't
|
|
242
262
|
// support --setting-sources, so it can never run isolated — same
|
|
243
263
|
// "cannot run at all right now" class as the codes above, safe to try the
|
|
244
|
-
// next provider
|
|
264
|
+
// next automatic provider rather than reporting a hard failure. codex-cli
|
|
265
|
+
// remains reachable only through the explicit override, which never walks.
|
|
245
266
|
'cli-settings-isolation-unsupported',
|
|
246
267
|
]);
|
|
247
268
|
|
|
@@ -288,9 +309,9 @@ function hasUsableIdentity(identity) {
|
|
|
288
309
|
* what lets the per-run `[providers]` log name the provider that actually
|
|
289
310
|
* ran even when that provider's own failure return carries no identity
|
|
290
311
|
* (codex-cli and byo-key only self-stamp on their SINGLE success return —
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
312
|
+
* the automatic fall-through failure case is now: claude-code skipped,
|
|
313
|
+
* byo-key ran and failed, no identity of its own, and the label must name
|
|
314
|
+
* byo-key rather than be re-guessed as claude-code by the old fallback).
|
|
294
315
|
* - Every other provider on a SUCCESS: `{provider:'unknown', ...}` —
|
|
295
316
|
* byo-key.js and codex-cli.js both self-stamp `identity` on their only
|
|
296
317
|
* success return BY CONTRACT; a success with none means that contract was
|
|
@@ -321,10 +342,10 @@ function deriveIdentity(id, result) {
|
|
|
321
342
|
* byo-key's real model name). Applied to every result this registry
|
|
322
343
|
* returns, success or failure, so the per-run `[providers]` log
|
|
323
344
|
* (scripts/extract-local.js's logProviderRunSummary) names the provider
|
|
324
|
-
* that actually ran even on a failure —
|
|
325
|
-
* case
|
|
326
|
-
*
|
|
327
|
-
*
|
|
345
|
+
* that actually ran even on a failure — the automatic fall-through failure
|
|
346
|
+
* case is now claude-code skipped, byo-key ran and failed with no identity
|
|
347
|
+
* of its own, and the stamp must not be re-guessed as claude-code by the
|
|
348
|
+
* caller.
|
|
328
349
|
*/
|
|
329
350
|
function withIdentity(id, result) {
|
|
330
351
|
if (hasUsableIdentity(result && result.identity)) return result;
|
|
@@ -333,19 +354,20 @@ function withIdentity(id, result) {
|
|
|
333
354
|
|
|
334
355
|
/**
|
|
335
356
|
* runModel(opts) — resolve a starting provider via resolveProvider(), then
|
|
336
|
-
* walk
|
|
357
|
+
* walk AUTOMATIC_PROVIDER_ORDER from there, calling each candidate's OWN runModel()
|
|
337
358
|
* (never a separate detect() pre-check — a provider's runModel() already
|
|
338
359
|
* performs the equivalent authoritative check internally and returns a
|
|
339
360
|
* specific, accurate reason, so a second detect() call would only add a
|
|
340
361
|
* redundant probe without adding information). Falls through to the next
|
|
341
362
|
* provider when the current one's failure reasonCode is in
|
|
342
363
|
* NON_RETRYABLE_FOR_THIS_PROVIDER; when resolveProvider itself found no
|
|
343
|
-
* usable provider at all, the walk starts at
|
|
344
|
-
* every provider still gets an actual runModel()
|
|
345
|
-
* own reason (not just a single generic "no
|
|
346
|
-
* explicit AUXILO_EXTRACTION_PROVIDER override
|
|
347
|
-
*
|
|
348
|
-
* as-is. When every provider
|
|
364
|
+
* usable provider at all, the walk starts at AUTOMATIC_PROVIDER_ORDER's
|
|
365
|
+
* beginning so every automatic provider still gets an actual runModel()
|
|
366
|
+
* call and contributes its own reason (not just a single generic "no
|
|
367
|
+
* provider available"). An explicit AUXILO_EXTRACTION_PROVIDER override,
|
|
368
|
+
* including codex-cli, never falls through — it is the operator's explicit
|
|
369
|
+
* choice, so its own failure reason is reported as-is. When every provider
|
|
370
|
+
* tried is exhausted, returns reasonCode
|
|
349
371
|
* 'no-usable-provider' with a bounded summary of every provider's reason in
|
|
350
372
|
* `reason` (no secrets — each provider's own reason string is already
|
|
351
373
|
* secret-free by contract) and NO identity — nothing actually ran to
|
|
@@ -375,8 +397,10 @@ async function runModel(opts = {}) {
|
|
|
375
397
|
|
|
376
398
|
const log = typeof opts.log === 'function' ? opts.log : console.error;
|
|
377
399
|
const resolved = await resolveProvider(opts);
|
|
378
|
-
const startIdx = resolved.ok ?
|
|
379
|
-
const order = startIdx === -1
|
|
400
|
+
const startIdx = resolved.ok ? AUTOMATIC_PROVIDER_ORDER.indexOf(resolved.id) : -1;
|
|
401
|
+
const order = startIdx === -1
|
|
402
|
+
? AUTOMATIC_PROVIDER_ORDER.slice()
|
|
403
|
+
: AUTOMATIC_PROVIDER_ORDER.slice(startIdx);
|
|
380
404
|
|
|
381
405
|
const attempts = [];
|
|
382
406
|
for (const id of order) {
|
|
@@ -411,7 +435,8 @@ async function runModel(opts = {}) {
|
|
|
411
435
|
module.exports = {
|
|
412
436
|
runModel,
|
|
413
437
|
resolveProvider,
|
|
414
|
-
|
|
438
|
+
KNOWN_PROVIDER_IDS,
|
|
439
|
+
AUTOMATIC_PROVIDER_ORDER,
|
|
415
440
|
PROVIDERS_STATE_PATH,
|
|
416
441
|
NON_RETRYABLE_FOR_THIS_PROVIDER,
|
|
417
442
|
};
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
* are not required to estimate on the caller's behalf.
|
|
62
62
|
* @property {string} [reasonCode] - Machine-matchable failure/skip classifier
|
|
63
63
|
* (e.g. 'cli-unauthenticated', 'cli-billing-helper-configured', 'model-error',
|
|
64
|
-
* 'isolation-precondition', 'isolation-unverified', 'isolation-violation',
|
|
64
|
+
* 'isolation-precondition', 'isolation-unverified', 'isolation-violation', 'output-schema-rejected',
|
|
65
65
|
* 'unknown'). Present on both success and failure paths where applicable.
|
|
66
66
|
* @property {string|null} [reason] - Human-readable reason, present when !ok.
|
|
67
67
|
* @property {string} [authStatus] - 'logged-in' | 'logged-out' | 'unknown', when
|
|
@@ -1,42 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$comment": "
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
{
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
"$comment": "CODEX-OUTPUT-SCHEMA-REJECTED — strict output transport for codex exec. The API validates this schema before generation. parseExtractionOutput and normalizeLearningArray remain authoritative for length, scope, quality and normalization; legacy bare arrays remain accepted by the parser only.",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"learnings": { "type": "array", "items": { "$ref": "#/$defs/learning" } },
|
|
7
|
+
"dedup_drops": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"candidate": { "$ref": "#/$defs/learning" },
|
|
13
|
+
"matched_index_id": { "type": "string" },
|
|
14
|
+
"matched_title": { "type": "string" }
|
|
15
|
+
},
|
|
16
|
+
"required": ["candidate", "matched_index_id", "matched_title"],
|
|
17
|
+
"additionalProperties": false
|
|
18
|
+
}
|
|
13
19
|
}
|
|
14
|
-
|
|
15
|
-
"
|
|
20
|
+
},
|
|
21
|
+
"required": ["dedup_drops", "learnings"],
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"$defs": {
|
|
16
24
|
"learning": {
|
|
17
25
|
"type": "object",
|
|
18
26
|
"properties": {
|
|
19
|
-
"title": { "type": "string"
|
|
20
|
-
"body": { "type": "string"
|
|
27
|
+
"title": { "type": "string" },
|
|
28
|
+
"body": { "type": "string" },
|
|
21
29
|
"category": {
|
|
22
30
|
"type": "string",
|
|
23
31
|
"enum": ["data-processing", "web-interaction", "code-execution", "storage-state", "payment-financial", "monitoring", "non-technical"]
|
|
24
32
|
},
|
|
25
|
-
"tags": { "type": "array", "items": { "type": "string" }
|
|
26
|
-
"task_context": { "type": "string" },
|
|
27
|
-
"outcome": { "type": "string", "enum": ["success", "partial", "failure", "workaround"] },
|
|
33
|
+
"tags": { "type": ["array", "null"], "items": { "type": "string" } },
|
|
34
|
+
"task_context": { "type": ["string", "null"] },
|
|
35
|
+
"outcome": { "type": ["string", "null"], "enum": ["success", "partial", "failure", "workaround", null] },
|
|
28
36
|
"quality_self_assessment": {
|
|
29
|
-
"type": "object",
|
|
37
|
+
"type": ["object", "null"],
|
|
30
38
|
"properties": {
|
|
31
|
-
"specificity": { "type": "integer"
|
|
32
|
-
"actionability": { "type": "integer"
|
|
33
|
-
"novelty": { "type": "integer"
|
|
34
|
-
"completeness": { "type": "integer"
|
|
39
|
+
"specificity": { "type": "integer" },
|
|
40
|
+
"actionability": { "type": "integer" },
|
|
41
|
+
"novelty": { "type": "integer" },
|
|
42
|
+
"completeness": { "type": "integer" },
|
|
35
43
|
"total": { "type": "integer" }
|
|
36
|
-
}
|
|
44
|
+
},
|
|
45
|
+
"required": ["actionability", "completeness", "novelty", "specificity", "total"],
|
|
46
|
+
"additionalProperties": false
|
|
37
47
|
}
|
|
38
48
|
},
|
|
39
|
-
"required": ["
|
|
49
|
+
"required": ["body", "category", "outcome", "quality_self_assessment", "tags", "task_context", "title"],
|
|
50
|
+
"additionalProperties": false
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$comment": "
|
|
3
|
+
"$comment": "CODEX-OUTPUT-SCHEMA-REJECTED — strict output transport for judge mode. parseJudgeDecisions enforces candidate bounds, unique indices, decision count and duplicate ids within the candidate's top-K list. A null matched_index_id on a nonduplicate is equivalent to omission in that parser.",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
6
6
|
"decisions": {
|
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
"items": {
|
|
9
9
|
"type": "object",
|
|
10
10
|
"properties": {
|
|
11
|
-
"candidate_index": { "type": "integer"
|
|
11
|
+
"candidate_index": { "type": "integer" },
|
|
12
12
|
"duplicate": { "type": "boolean" },
|
|
13
|
-
"matched_index_id": { "type": "string" }
|
|
13
|
+
"matched_index_id": { "type": ["string", "null"] }
|
|
14
14
|
},
|
|
15
|
-
"required": ["candidate_index", "duplicate"]
|
|
15
|
+
"required": ["candidate_index", "duplicate", "matched_index_id"],
|
|
16
|
+
"additionalProperties": false
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
|
-
"required": ["decisions"]
|
|
20
|
+
"required": ["decisions"],
|
|
21
|
+
"additionalProperties": false
|
|
20
22
|
}
|