auxilo-mcp 0.9.25 → 0.9.27
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 +4 -0
- package/bin/auxilo-cli.js +20 -16
- package/mcp-server.js +6 -6
- package/package.json +1 -1
- package/scripts/extract-local.js +36 -11
- package/scripts/providers/byo-key.js +20 -5
- package/scripts/providers/claude-code.js +105 -142
- package/scripts/providers/codex-cli.js +25 -9
- package/scripts/providers/index.js +164 -160
- package/scripts/providers/provider.interface.js +24 -17
- package/scripts/providers/route-binding.js +232 -0
- package/scripts/runner.js +20 -2
package/README.md
CHANGED
|
@@ -175,6 +175,10 @@ node mcp-server.js # the MCP server (stdio)
|
|
|
175
175
|
|
|
176
176
|
The live API runs on [Fly.io](https://fly.io) from the repo `Dockerfile`.
|
|
177
177
|
|
|
178
|
+
### One machine
|
|
179
|
+
|
|
180
|
+
The money ledgers are guarded by locks held in memory in one running process. The server is meant to run as one machine with one data volume. Do not run a second machine against the same data, and do not start an overlapping deploy where the old and new process both run against the same files at once — the locks on one machine cannot see or coordinate with a second one, so two processes writing the same ledger files at the same time can corrupt them or double-spend a balance. This must not change until the ledgers move to a shared store that more than one process can safely coordinate through.
|
|
181
|
+
|
|
178
182
|
## Links
|
|
179
183
|
|
|
180
184
|
- Site: [auxilo.io](https://auxilo.io)
|
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
|
|
@@ -180,8 +180,8 @@ const CONSENT_TEXT = `
|
|
|
180
180
|
waits for operator review. A draft that any screen flags (sensitive,
|
|
181
181
|
duplicate, uncertain quality) waits in your private queue for
|
|
182
182
|
\`auxilo review\`. Auto-publish for learnings that pass every screen is
|
|
183
|
-
off unless you turn it on in your dashboard. Your
|
|
184
|
-
unlock by another agent
|
|
183
|
+
off unless you turn it on in your dashboard. Your earnings from a paid
|
|
184
|
+
unlock by another agent go to your Auxilo account, 70% of what they
|
|
185
185
|
paid on a direct unlock and 60% via discovery. A repeat unlock by the
|
|
186
186
|
same buyer within 30 days earns nothing. Earnings depend on whether
|
|
187
187
|
other agents unlock your learnings and are not guaranteed. Earnings
|
|
@@ -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
|
@@ -77,7 +77,7 @@ function unlockPaymentRequired(status, data, http_endpoint) {
|
|
|
77
77
|
status: 'payment_required',
|
|
78
78
|
content_advisory: UNTRUSTED_PREVIEW_ADVISORY,
|
|
79
79
|
cost: `${price} USDC on Base (set by contributor)`,
|
|
80
|
-
how_to_pay: 'Pass an x402 payment via the x_payment argument, or configure an API key with
|
|
80
|
+
how_to_pay: 'Pass an x402 payment via the x_payment argument, or configure an API key with a funded balance (npx auxilo setup).',
|
|
81
81
|
http_endpoint,
|
|
82
82
|
payment_details: fencePaymentChallenge(data),
|
|
83
83
|
};
|
|
@@ -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.27' },
|
|
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.
|
|
@@ -217,7 +217,7 @@ RATE AFTER YOU USE: After unlocking and applying a learning from the Auxilo mark
|
|
|
217
217
|
|
|
218
218
|
DEDUP BEFORE SUBMITTING: Search auxilo_knowledge for your topic before contributing to avoid duplicates.
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
When another agent unlocks one of your contributions, the builder who connected you earns 70% of what that agent paid on a direct unlock and 60% when Auxilo search surfaced it.`
|
|
221
221
|
}
|
|
222
222
|
);
|
|
223
223
|
|
|
@@ -298,7 +298,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
298
298
|
},
|
|
299
299
|
required: ['specificity', 'actionability', 'novelty', 'completeness', 'total'],
|
|
300
300
|
},
|
|
301
|
-
contributor_wallet: { type: 'string', description: 'Your Base wallet (0x...) for
|
|
301
|
+
contributor_wallet: { type: 'string', description: 'Your Base wallet (0x...) for earnings' },
|
|
302
302
|
unlock_price: { type: 'number', description: 'Price in USD to unlock this learning (min $0.05, default auto-calculated). Set higher for deep, high-value knowledge.' },
|
|
303
303
|
contributor_agent: { type: 'string', description: 'Optional: identify yourself' },
|
|
304
304
|
related_skills: { type: 'array', items: { type: 'string' }, description: 'Optional: related Auxilo skill IDs' },
|
|
@@ -362,7 +362,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
362
362
|
},
|
|
363
363
|
{
|
|
364
364
|
name: 'auxilo_withdraw',
|
|
365
|
-
description: 'Check how your earnings are paid out, and how to receive any legacy accrued balance. HOW PAYOUT WORKS NOW: earnings from sales settled on-chain are paid directly to your linked, verified wallet at the moment of sale — there is no balance to withdraw for those, because Auxilo never holds your
|
|
365
|
+
description: 'Check how your earnings are paid out, and how to receive any legacy accrued balance. HOW PAYOUT WORKS NOW: earnings from sales settled on-chain are paid directly to your linked, verified wallet at the moment of sale — there is no balance to withdraw for those, because Auxilo never holds your earnings. LEGACY BALANCE: earnings accrued before direct settlement sit in your account\'s pending balance; withdraw them from your dashboard at https://auxilo.io/dashboard (bank payout via Stripe; $0.50 minimum, $0.25 flat fee), where you sign in with the email on your account. This tool reports your current balances and payout status; it does not move funds. To be paid at sale time going forward: auxilo_accept_terms, then auxilo_verify_wallet, then auxilo_link_wallet.',
|
|
366
366
|
inputSchema: {
|
|
367
367
|
type: 'object',
|
|
368
368
|
properties: {
|
|
@@ -408,7 +408,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
name: 'auxilo_account_earnings',
|
|
411
|
-
description: 'View aggregate and per-learning earnings for your authenticated Auxilo account. Authenticates with your configured API key automatically, or pass a session_token (JWT). Returns total gross, contributor
|
|
411
|
+
description: 'View aggregate and per-learning earnings for your authenticated Auxilo account. Authenticates with your configured API key automatically, or pass a session_token (JWT). Returns total gross, contributor earnings, pending balance, total withdrawn, whether withdrawal is available (can_withdraw), and held_pending_assent — undisbursable receipts recorded before you accepted the current Terms, released to your withdrawable balance when you accept via auxilo_accept_terms. Earnings from on-chain-settled sales are paid to your wallet at sale time and appear in settlement history, not in pending balance. Free.',
|
|
412
412
|
inputSchema: {
|
|
413
413
|
type: 'object',
|
|
414
414
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auxilo-mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.27",
|
|
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
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const os = require('os');
|
|
18
|
+
const crypto = require('crypto');
|
|
18
19
|
const {
|
|
19
20
|
loadIndexForExtraction,
|
|
20
21
|
readExtractionIndex,
|
|
@@ -373,9 +374,10 @@ function resolveExtractionModelIdentity(runModelResult) {
|
|
|
373
374
|
&& typeof runModelResult.identity.provider === 'string'
|
|
374
375
|
&& runModelResult.identity.provider
|
|
375
376
|
) {
|
|
376
|
-
|
|
377
|
+
const identity = runModelResult.identity;
|
|
378
|
+
return { ...identity, model: identity.observed_model || null };
|
|
377
379
|
}
|
|
378
|
-
return { provider: 'unknown', model: null, version: null, vendor: null };
|
|
380
|
+
return { provider: 'unknown', model: null, requested_model: null, observed_model: null, identity_unresolved: 'missing', version: null, vendor: null };
|
|
379
381
|
}
|
|
380
382
|
|
|
381
383
|
/**
|
|
@@ -402,6 +404,8 @@ async function defaultInvokeModel(transcript, invokeOpts, opts) {
|
|
|
402
404
|
reasonCode: result.reasonCode,
|
|
403
405
|
authStatus: result.authStatus,
|
|
404
406
|
extractionModel: resolveExtractionModelIdentity(result),
|
|
407
|
+
...(result.hold && { hold: result.hold }),
|
|
408
|
+
...(result.deferred && { deferred: result.deferred }),
|
|
405
409
|
...(result.authDiscrepancy !== undefined && { authDiscrepancy: result.authDiscrepancy }),
|
|
406
410
|
// EXTRACTION-RUN-LOG (0.9.15): additive passthrough for the one-line-per-run
|
|
407
411
|
// provider summary logged at the end of extractLocally() below. Only
|
|
@@ -432,6 +436,9 @@ function defaultInvokeJudge(opts) {
|
|
|
432
436
|
out: result.text,
|
|
433
437
|
usage: result.usage,
|
|
434
438
|
reason: result.reason,
|
|
439
|
+
identity: result.identity,
|
|
440
|
+
...(result.hold && { hold: result.hold }),
|
|
441
|
+
...(result.deferred && { deferred: result.deferred }),
|
|
435
442
|
// EXTRACTION-RUN-LOG (0.9.15): threaded through so runAnchoredJudge below
|
|
436
443
|
// can report the judge call's status/argv/CLI version on the run summary
|
|
437
444
|
// log line. Additive — every existing invokeJudge stub in this repo's
|
|
@@ -526,11 +533,16 @@ async function runAnchoredJudge(candidates, indexState, opts = {}) {
|
|
|
526
533
|
judgeReasonCode: result && result.reasonCode,
|
|
527
534
|
judgeArgv: result && result.argv,
|
|
528
535
|
judgeCliVersion: result && result.cliVersion,
|
|
536
|
+
...(result && result.hold && { hold: result.hold }),
|
|
537
|
+
...(result && result.deferred && { deferred: result.deferred }),
|
|
529
538
|
};
|
|
530
539
|
}
|
|
531
540
|
const parsed = parseJudgeDecisions(result.out, input, rankings);
|
|
532
541
|
const usage = judgeUsage(result.usage, prompt, result.out);
|
|
533
542
|
const judgeMeta = {
|
|
543
|
+
identity: result.identity,
|
|
544
|
+
...(result.hold && { hold: result.hold }),
|
|
545
|
+
...(result.deferred && { deferred: result.deferred }),
|
|
534
546
|
judgeAttempted: true,
|
|
535
547
|
judgeReasonCode: result.reasonCode,
|
|
536
548
|
judgeArgv: result.argv,
|
|
@@ -681,13 +693,14 @@ function formatArgvForLog(argv) {
|
|
|
681
693
|
* PRE_SPAWN_SKIP_REASON_CODES, so they render finder=skipped, not ran. The
|
|
682
694
|
* observed defect lines (finder=ran, flags=n/a) come from a DIFFERENT case:
|
|
683
695
|
* scripts/providers/index.js's runModel() falls through from claude-code to
|
|
684
|
-
* the next
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
696
|
+
* the next automatic provider (byo-key) whenever claude-code's own attempt
|
|
697
|
+
* fails with a NON_RETRYABLE_FOR_THIS_PROVIDER reasonCode, and returns that
|
|
698
|
+
* OTHER provider's result directly when it stops there. codex-cli is not in
|
|
699
|
+
* that automatic walk; it remains reachable only through the explicit env
|
|
700
|
+
* override, which never falls through. A non-Claude provider's result carries
|
|
701
|
+
* no `argv` field at all (argv is a claude-code-only concept), so there is no
|
|
702
|
+
* shipped argv being hidden here — 'unknown' remains the correct, honest
|
|
703
|
+
* `hooks` value regardless of which provider is named.
|
|
691
704
|
*
|
|
692
705
|
* EXTRACTION-MODEL-PROVENANCE (PUNCH-LIST P1) closed the mismatch this
|
|
693
706
|
* docblock used to describe as a known, deferred gap: a fallthrough
|
|
@@ -735,6 +748,8 @@ function logProviderRunSummary(opts, runId, modelResult, judged) {
|
|
|
735
748
|
}
|
|
736
749
|
|
|
737
750
|
async function extractLocally(transcript, sourceType, opts = {}) {
|
|
751
|
+
opts = { ...opts, source: sourceType || '', sessionId: opts.sessionId || opts.runId || crypto.randomUUID(),
|
|
752
|
+
jobSha: opts.jobSha || crypto.createHash('sha256').update(transcript).digest('hex'), routeContext: opts.routeContext || {} };
|
|
738
753
|
if (sourceType && !EXTRACTABLE_SOURCES.has(sourceType)) {
|
|
739
754
|
return { learnings: [], skipped: `local extraction not implemented for "${sourceType}" — agent contributes via auxilo_contribute` };
|
|
740
755
|
}
|
|
@@ -779,6 +794,8 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
779
794
|
return {
|
|
780
795
|
learnings: [],
|
|
781
796
|
skipped: reason,
|
|
797
|
+
...(modelResult.hold && { hold: modelResult.hold }),
|
|
798
|
+
...(modelResult.deferred && { deferred: modelResult.deferred }),
|
|
782
799
|
...(modelResult.reasonCode !== undefined && { reasonCode: modelResult.reasonCode }),
|
|
783
800
|
...(modelResult.authStatus !== undefined && { authStatus: modelResult.authStatus }),
|
|
784
801
|
...(modelResult.authDiscrepancy !== undefined && {
|
|
@@ -791,7 +808,9 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
791
808
|
// own opts.invokeModel that doesn't report one (every existing test does
|
|
792
809
|
// this) — extraction_model then simply never appears, byte-identical to
|
|
793
810
|
// pre-PART-C behavior.
|
|
794
|
-
const extractionModel = modelResult.extractionModel
|
|
811
|
+
const extractionModel = modelResult.extractionModel
|
|
812
|
+
? { ...modelResult.extractionModel, model: modelResult.extractionModel.observed_model || null }
|
|
813
|
+
: null;
|
|
795
814
|
const parsed = parseExtractionOutput(out, opts);
|
|
796
815
|
const promptDropResult = applyPromptMemoryDrops(
|
|
797
816
|
parsed.prompt_drops,
|
|
@@ -829,7 +848,10 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
829
848
|
lexicalKept.push(drop.candidate);
|
|
830
849
|
}
|
|
831
850
|
}
|
|
832
|
-
const judged =
|
|
851
|
+
const judged = modelResult.hold || modelResult.deferred
|
|
852
|
+
? { kept: lexicalKept, dropped: [], called: false, prompt_tokens: 0, completion_tokens: 0,
|
|
853
|
+
...(modelResult.hold && { hold: modelResult.hold }), ...(modelResult.deferred && { deferred: modelResult.deferred }) }
|
|
854
|
+
: await runAnchoredJudge(lexicalKept, filterState, opts);
|
|
833
855
|
const allDropped = [
|
|
834
856
|
...promptDropResult.dropped,
|
|
835
857
|
...lexicalDropped,
|
|
@@ -845,6 +867,9 @@ async function extractLocally(transcript, sourceType, opts = {}) {
|
|
|
845
867
|
logProviderRunSummary(opts, opts.runId, modelResult, judged);
|
|
846
868
|
return {
|
|
847
869
|
learnings: judged.kept,
|
|
870
|
+
...(judged.hold && { hold: judged.hold }),
|
|
871
|
+
...(judged.deferred && { deferred: judged.deferred }),
|
|
872
|
+
...providers.finishJob(opts.routeContext),
|
|
848
873
|
dedup_dropped: allDropped.length,
|
|
849
874
|
drop_audit: allDropped.map((drop) => drop.audit),
|
|
850
875
|
prompt_memory_tokens: promptMemory.estimated_tokens,
|
|
@@ -537,6 +537,7 @@ async function runModel(opts = {}) {
|
|
|
537
537
|
usage: null,
|
|
538
538
|
reason: 'cannot resolve ~/.auxilo/providers.json — the home directory did not resolve to an absolute path',
|
|
539
539
|
reasonCode: 'provider-home-unresolved',
|
|
540
|
+
refusal: 'pre-invocation',
|
|
540
541
|
authStatus: 'unknown',
|
|
541
542
|
};
|
|
542
543
|
}
|
|
@@ -552,6 +553,7 @@ async function runModel(opts = {}) {
|
|
|
552
553
|
usage: null,
|
|
553
554
|
reason: '~/.auxilo/providers.json is not owner-read-only; refusing to use the stored key until its permissions are fixed (chmod 600 ~/.auxilo/providers.json) or the file is removed (`auxilo provider clear`)',
|
|
554
555
|
reasonCode: 'providers-file-mode-unsafe',
|
|
556
|
+
refusal: 'pre-invocation',
|
|
555
557
|
authStatus: 'unknown',
|
|
556
558
|
};
|
|
557
559
|
}
|
|
@@ -563,6 +565,7 @@ async function runModel(opts = {}) {
|
|
|
563
565
|
usage: null,
|
|
564
566
|
reason: 'no BYO provider key configured — run `auxilo provider set`',
|
|
565
567
|
reasonCode: 'provider-not-configured',
|
|
568
|
+
refusal: 'pre-invocation',
|
|
566
569
|
authStatus: 'unknown',
|
|
567
570
|
};
|
|
568
571
|
}
|
|
@@ -575,8 +578,9 @@ async function runModel(opts = {}) {
|
|
|
575
578
|
ok: false,
|
|
576
579
|
text: '',
|
|
577
580
|
usage: null,
|
|
578
|
-
reason:
|
|
581
|
+
reason: 'configured base URL is not https; refusing the request',
|
|
579
582
|
reasonCode: 'provider-base-url-insecure',
|
|
583
|
+
refusal: 'pre-invocation',
|
|
580
584
|
authStatus: 'unknown',
|
|
581
585
|
};
|
|
582
586
|
}
|
|
@@ -587,7 +591,7 @@ async function runModel(opts = {}) {
|
|
|
587
591
|
const fetchImpl = typeof opts.fetchImpl === 'function' ? opts.fetchImpl : fetch;
|
|
588
592
|
const timeoutMs = opts.timeoutMs || 120000;
|
|
589
593
|
|
|
590
|
-
const identity = { provider: 'byo-key', model: config.model, version: null, vendor };
|
|
594
|
+
const identity = { provider: 'byo-key', model: null, requested_model: config.model, observed_model: null, identity_unresolved: 'missing', version: null, vendor };
|
|
591
595
|
|
|
592
596
|
const controller = typeof AbortController === 'function' ? new AbortController() : null;
|
|
593
597
|
const timer = controller ? setTimeout(() => controller.abort(), timeoutMs) : null;
|
|
@@ -595,6 +599,8 @@ async function runModel(opts = {}) {
|
|
|
595
599
|
|
|
596
600
|
try {
|
|
597
601
|
let res;
|
|
602
|
+
const gate = require('./route-binding.js').invocationGate(opts);
|
|
603
|
+
if (gate) return gate;
|
|
598
604
|
try {
|
|
599
605
|
res = await fetchImpl(request.url, {
|
|
600
606
|
method: 'POST',
|
|
@@ -611,7 +617,7 @@ async function runModel(opts = {}) {
|
|
|
611
617
|
ok: false,
|
|
612
618
|
text: '',
|
|
613
619
|
usage: null,
|
|
614
|
-
reason:
|
|
620
|
+
reason: 'BYO provider request failed',
|
|
615
621
|
reasonCode: 'provider-error',
|
|
616
622
|
authStatus: 'unknown',
|
|
617
623
|
identity,
|
|
@@ -634,7 +640,7 @@ async function runModel(opts = {}) {
|
|
|
634
640
|
ok: false,
|
|
635
641
|
text: '',
|
|
636
642
|
usage: null,
|
|
637
|
-
reason: `BYO provider returned HTTP ${res.status}
|
|
643
|
+
reason: Number.isInteger(res.status) ? `BYO provider returned HTTP ${res.status}` : 'BYO provider request failed',
|
|
638
644
|
reasonCode: 'provider-error',
|
|
639
645
|
authStatus: 'unknown',
|
|
640
646
|
identity,
|
|
@@ -660,13 +666,19 @@ async function runModel(opts = {}) {
|
|
|
660
666
|
ok: false,
|
|
661
667
|
text: '',
|
|
662
668
|
usage: null,
|
|
663
|
-
reason:
|
|
669
|
+
reason: 'BYO provider returned a non-JSON body',
|
|
664
670
|
reasonCode: 'provider-error',
|
|
665
671
|
authStatus: 'unknown',
|
|
666
672
|
identity,
|
|
667
673
|
};
|
|
668
674
|
}
|
|
669
675
|
|
|
676
|
+
const observed = data && (vendor === 'gemini' ? data.modelVersion : data.model);
|
|
677
|
+
if (typeof observed === 'string' && observed.trim()) {
|
|
678
|
+
identity.observed_model = observed;
|
|
679
|
+
identity.model = observed;
|
|
680
|
+
delete identity.identity_unresolved;
|
|
681
|
+
}
|
|
670
682
|
return {
|
|
671
683
|
ok: true,
|
|
672
684
|
text: extractText(vendor, data),
|
|
@@ -675,6 +687,8 @@ async function runModel(opts = {}) {
|
|
|
675
687
|
authStatus: 'unknown',
|
|
676
688
|
identity,
|
|
677
689
|
};
|
|
690
|
+
} catch {
|
|
691
|
+
return { ok: false, text: '', usage: null, reason: 'BYO provider request failed', reasonCode: 'provider-error', authStatus: 'unknown' };
|
|
678
692
|
} finally {
|
|
679
693
|
clearTimer();
|
|
680
694
|
}
|
|
@@ -689,6 +703,7 @@ module.exports = {
|
|
|
689
703
|
writeByoConfig,
|
|
690
704
|
clearProvidersFile,
|
|
691
705
|
resolveVendor,
|
|
706
|
+
baseUrlFor,
|
|
692
707
|
DEFAULT_PROVIDERS_STATE_PATH,
|
|
693
708
|
// Exported for direct unit coverage (test/byo-key-provider.test.js,
|
|
694
709
|
// test/extract-w1-fix2.test.js) AND for scripts/providers/index.js, which
|