@eddyskywalker/dsh-chatgpt-subscription 0.10.9 → 0.10.11
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/CHANGELOG.md +34 -0
- package/README.md +10 -6
- package/lib/client.js +12 -1
- package/lib/client.js.map +1 -1
- package/lib/index.js +329 -89
- package/lib/types/client/claude/ClaudeSection.d.ts.map +1 -1
- package/lib/types/host/antigravity/client.d.ts +19 -0
- package/lib/types/host/antigravity/client.d.ts.map +1 -1
- package/lib/types/host/claude/adapter.d.ts.map +1 -1
- package/lib/types/host/claude/client.d.ts +6 -1
- package/lib/types/host/claude/client.d.ts.map +1 -1
- package/lib/types/host/claude/mapper.d.ts +33 -12
- package/lib/types/host/claude/mapper.d.ts.map +1 -1
- package/lib/types/host/claude/model-catalog.d.ts +19 -4
- package/lib/types/host/claude/model-catalog.d.ts.map +1 -1
- package/lib/types/host/claude/oauth.d.ts.map +1 -1
- package/lib/types/host/claude/types.d.ts +18 -1
- package/lib/types/host/claude/types.d.ts.map +1 -1
- package/lib/types/host/command-code/client.d.ts.map +1 -1
- package/lib/types/host/common/catalog-snapshot.d.ts +10 -0
- package/lib/types/host/common/catalog-snapshot.d.ts.map +1 -1
- package/lib/types/host/kimi-code/client.d.ts.map +1 -1
- package/lib/types/host/subagent-model-authorization.d.ts +44 -0
- package/lib/types/host/subagent-model-authorization.d.ts.map +1 -1
- package/lib/types/host/workbuddy/client.d.ts.map +1 -1
- package/lib/types/shared/model-catalog.d.ts +10 -1
- package/lib/types/shared/model-catalog.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -109,6 +109,16 @@ const CODEX_MODEL_CATALOG = [
|
|
|
109
109
|
supportsReasoningSummary: true,
|
|
110
110
|
maxTokens: GPT_6_MAX_TOKENS
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
id: "gpt-6.1-sol",
|
|
114
|
+
name: "6.1 Sol",
|
|
115
|
+
contextWindow: GPT_6_DEFAULT_CONTEXT_WINDOW,
|
|
116
|
+
inputModalities: ["text", "image"],
|
|
117
|
+
defaultReasoningEffort: "medium",
|
|
118
|
+
reasoningProfile: "gpt-6",
|
|
119
|
+
supportsReasoningSummary: true,
|
|
120
|
+
maxTokens: GPT_6_MAX_TOKENS
|
|
121
|
+
},
|
|
112
122
|
{
|
|
113
123
|
id: "gpt-6-sol",
|
|
114
124
|
name: "6 Sol",
|
|
@@ -189,6 +199,7 @@ const CODEX_MODEL_CATALOG = [
|
|
|
189
199
|
];
|
|
190
200
|
const DEFAULT_VISIBLE_CODEX_MODEL_IDS = [
|
|
191
201
|
"gpt-6-astra",
|
|
202
|
+
"gpt-6.1-sol",
|
|
192
203
|
"gpt-6-sol",
|
|
193
204
|
"gpt-6-luna",
|
|
194
205
|
"gpt-5.6-sol",
|
|
@@ -6421,6 +6432,30 @@ function parseCatalogModels(data) {
|
|
|
6421
6432
|
}
|
|
6422
6433
|
return list;
|
|
6423
6434
|
}
|
|
6435
|
+
/**
|
|
6436
|
+
* Merge a freshly discovered API catalog into the stored model selection.
|
|
6437
|
+
*
|
|
6438
|
+
* The API catalog is a *request-level* listing: its ids are runtime names
|
|
6439
|
+
* (`gemini-3.8-flash-tiered`, `claude-opus-4-6-thinking`, `gemini-3.1-pro-high`)
|
|
6440
|
+
* that share no vocabulary with the selectable plugin ids (`gemini-3.8-flash`,
|
|
6441
|
+
* `claude-opus-4-6`, `gemini-3.1-pro`). The picker and the adapter both iterate
|
|
6442
|
+
* the plugin table, so pruning the selection against the API ids kept nothing:
|
|
6443
|
+
* the first refresh after the shipped defaults were written dropped every
|
|
6444
|
+
* stored id, which left the card rendering every model unchecked and made a
|
|
6445
|
+
* deliberate pick look like it did not persist. Prune against the table the
|
|
6446
|
+
* selection actually indexes, and keep the API catalog as display metadata
|
|
6447
|
+
* only.
|
|
6448
|
+
*
|
|
6449
|
+
* @param stored - The selection currently persisted for the picker.
|
|
6450
|
+
* @param storedCatalog - The API catalog the previous refresh recorded.
|
|
6451
|
+
* @returns The selection to persist, drawn from the plugin model table.
|
|
6452
|
+
*/
|
|
6453
|
+
function mergeCatalogSelection(stored, storedCatalog) {
|
|
6454
|
+
const shipped = MODELS.map((model) => model.id);
|
|
6455
|
+
const known = new Set(shipped);
|
|
6456
|
+
if (stored.length === 0 && storedCatalog.length === 0) return shipped;
|
|
6457
|
+
return stored.filter((id) => known.has(id));
|
|
6458
|
+
}
|
|
6424
6459
|
async function fetchAccountQuota(store = new FileCredentialStore$2(), modelSettings, fetchFn = fetch, force = false) {
|
|
6425
6460
|
if (!force && cachedQuota$1 && Date.now() - (cachedQuota$1.fetchedAt || 0) < 12e4) return cachedQuota$1;
|
|
6426
6461
|
if (quotaFetchInFlight) return quotaFetchInFlight;
|
|
@@ -6461,9 +6496,7 @@ async function fetchAccountQuota(store = new FileCredentialStore$2(), modelSetti
|
|
|
6461
6496
|
cachedQuota$1 = snapshot;
|
|
6462
6497
|
if (modelSettings && catalogModels.length > 0) {
|
|
6463
6498
|
const current = await modelSettings.read();
|
|
6464
|
-
const
|
|
6465
|
-
const catalogIds = new Set(catalogModels.map((m) => m.id));
|
|
6466
|
-
const mergedEnabled = isFirstTime ? catalogModels.map((m) => m.id) : current.enabledModelIds.filter((id) => catalogIds.has(id));
|
|
6499
|
+
const mergedEnabled = mergeCatalogSelection(current.enabledModelIds, current.catalogModels);
|
|
6467
6500
|
await modelSettings.setCatalogModels(catalogModels, { enabledModelIds: mergedEnabled });
|
|
6468
6501
|
}
|
|
6469
6502
|
return snapshot;
|
|
@@ -9606,18 +9639,36 @@ async function readCatalogSnapshot(name, parseModels) {
|
|
|
9606
9639
|
return;
|
|
9607
9640
|
}
|
|
9608
9641
|
}
|
|
9642
|
+
/**
|
|
9643
|
+
* Snapshot writes still in flight.
|
|
9644
|
+
*
|
|
9645
|
+
* Callers fire-and-forget these writes, so nothing in production ever awaits
|
|
9646
|
+
* one. A test that deletes its private `storages` directory between cases
|
|
9647
|
+
* cannot know when that is safe, and a write that lands after the delete
|
|
9648
|
+
* recreates the file the next case asserts is absent — which is how a stale
|
|
9649
|
+
* listing leaked into a later test and failed it on CI. Registering each write
|
|
9650
|
+
* gives {@link flushCatalogSnapshots} a deterministic point to wait on.
|
|
9651
|
+
*/
|
|
9652
|
+
const pendingSnapshotWrites = /* @__PURE__ */ new Set();
|
|
9609
9653
|
/** Persist one snapshot; a failure is logged nowhere and never thrown. */
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9654
|
+
function writeCatalogSnapshot(name, models, fetchedAt) {
|
|
9655
|
+
const write = (async () => {
|
|
9656
|
+
try {
|
|
9657
|
+
const filePath = catalogSnapshotPath(name);
|
|
9658
|
+
await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
|
|
9659
|
+
const tmp = `${filePath}.tmp.${Date.now()}`;
|
|
9660
|
+
await fsPromises.writeFile(tmp, JSON.stringify({
|
|
9661
|
+
fetchedAt,
|
|
9662
|
+
models
|
|
9663
|
+
}, null, 2), "utf8");
|
|
9664
|
+
await fsPromises.rename(tmp, filePath);
|
|
9665
|
+
} catch {}
|
|
9666
|
+
})();
|
|
9667
|
+
pendingSnapshotWrites.add(write);
|
|
9668
|
+
write.finally(() => {
|
|
9669
|
+
pendingSnapshotWrites.delete(write);
|
|
9670
|
+
});
|
|
9671
|
+
return write;
|
|
9621
9672
|
}
|
|
9622
9673
|
/**
|
|
9623
9674
|
* Warm an in-memory cache from the persisted snapshot when it is fresher than
|
|
@@ -10191,6 +10242,8 @@ function extractUnlimited(payload) {
|
|
|
10191
10242
|
return deepValue(payload, ["unlimited", "isUnlimited"]) === true;
|
|
10192
10243
|
}
|
|
10193
10244
|
let cachedCatalog$1;
|
|
10245
|
+
/** Bumped by every cache clear so a fetch already in flight cannot publish. */
|
|
10246
|
+
let catalogCacheEpoch$2 = 0;
|
|
10194
10247
|
let catalogInFlight$2 = null;
|
|
10195
10248
|
/**
|
|
10196
10249
|
* Snapshot scopes already consulted in this process.
|
|
@@ -10282,7 +10335,9 @@ async function loadProviderModels(options = {}) {
|
|
|
10282
10335
|
/** One in-flight, single-flighted fetch of the live catalog. */
|
|
10283
10336
|
function refreshProviderModels$1(options = {}) {
|
|
10284
10337
|
if (catalogInFlight$2) return catalogInFlight$2;
|
|
10338
|
+
const epoch = catalogCacheEpoch$2;
|
|
10285
10339
|
const request = fetchProviderModels(options).then((models) => {
|
|
10340
|
+
if (epoch !== catalogCacheEpoch$2) return models.length > 0 ? models : cachedCatalog$1?.models ?? [];
|
|
10286
10341
|
if (models.length > 0) {
|
|
10287
10342
|
const fetchedAt = Date.now();
|
|
10288
10343
|
cachedCatalog$1 = {
|
|
@@ -10299,6 +10354,7 @@ function refreshProviderModels$1(options = {}) {
|
|
|
10299
10354
|
});
|
|
10300
10355
|
}
|
|
10301
10356
|
function clearCachedCatalog$3() {
|
|
10357
|
+
catalogCacheEpoch$2 += 1;
|
|
10302
10358
|
cachedCatalog$1 = void 0;
|
|
10303
10359
|
catalogInFlight$2 = null;
|
|
10304
10360
|
catalogSnapshotScopesLoaded$2.clear();
|
|
@@ -13926,6 +13982,16 @@ async function modelRequestHeaders$1(accessToken, wire) {
|
|
|
13926
13982
|
}
|
|
13927
13983
|
let catalogCache$1 = null;
|
|
13928
13984
|
/**
|
|
13985
|
+
* Bumped by every cache clear so a fetch already in flight cannot publish.
|
|
13986
|
+
*
|
|
13987
|
+
* A stale-while-revalidate refresh answers its caller from cache and keeps
|
|
13988
|
+
* fetching. Clearing the cache — a sign-out, a forced refresh, a test starting
|
|
13989
|
+
* over — must therefore also silence that refresh: without this it landed
|
|
13990
|
+
* afterwards and republished both the in-memory cache and the persisted
|
|
13991
|
+
* snapshot it was written to invalidate.
|
|
13992
|
+
*/
|
|
13993
|
+
let catalogCacheEpoch$1 = 0;
|
|
13994
|
+
/**
|
|
13929
13995
|
* Snapshot scopes already consulted in this process.
|
|
13930
13996
|
*
|
|
13931
13997
|
* One entry per region rather than a single flag: the persisted file is scoped
|
|
@@ -13935,6 +14001,7 @@ let catalogCache$1 = null;
|
|
|
13935
14001
|
*/
|
|
13936
14002
|
const catalogSnapshotScopesLoaded$1 = /* @__PURE__ */ new Set();
|
|
13937
14003
|
function clearCachedCatalog$2() {
|
|
14004
|
+
catalogCacheEpoch$1 += 1;
|
|
13938
14005
|
catalogCache$1 = null;
|
|
13939
14006
|
catalogSnapshotScopesLoaded$1.clear();
|
|
13940
14007
|
catalogActiveRegions.clear();
|
|
@@ -14043,6 +14110,7 @@ function refreshProviderModels(options) {
|
|
|
14043
14110
|
}
|
|
14044
14111
|
async function performListing(options) {
|
|
14045
14112
|
const region = options.region ?? await resolveRegion();
|
|
14113
|
+
const epoch = catalogCacheEpoch$1;
|
|
14046
14114
|
let accessToken = options.accessToken;
|
|
14047
14115
|
if (accessToken === void 0) {
|
|
14048
14116
|
if (options.store === void 0) return [];
|
|
@@ -14067,6 +14135,7 @@ async function performListing(options) {
|
|
|
14067
14135
|
const data = Array.isArray(payload) ? payload : asRecord$7(payload)?.data;
|
|
14068
14136
|
if (!Array.isArray(data)) throw new Error("Kimi Code model listing was not in the documented shape.");
|
|
14069
14137
|
const models = data.map(parseCatalogModel).filter((model) => model !== void 0);
|
|
14138
|
+
if (epoch !== catalogCacheEpoch$1) return models;
|
|
14070
14139
|
rememberCatalogCredential(region, accessToken);
|
|
14071
14140
|
catalogCache$1 = {
|
|
14072
14141
|
fetchedAt: Date.now(),
|
|
@@ -23894,6 +23963,8 @@ function parseConfigModels(payload, region) {
|
|
|
23894
23963
|
return models;
|
|
23895
23964
|
}
|
|
23896
23965
|
let cachedCatalog;
|
|
23966
|
+
/** Bumped by every cache clear so a fetch already in flight cannot publish. */
|
|
23967
|
+
let catalogCacheEpoch = 0;
|
|
23897
23968
|
let catalogInFlight$1 = null;
|
|
23898
23969
|
let catalogInFlightRegion = null;
|
|
23899
23970
|
const CATALOG_CACHE_TTL_MS$1 = 1800 * 1e3;
|
|
@@ -23908,6 +23979,7 @@ const CATALOG_CACHE_TTL_MS$1 = 1800 * 1e3;
|
|
|
23908
23979
|
const catalogSnapshotScopesLoaded = /* @__PURE__ */ new Set();
|
|
23909
23980
|
function clearCachedCatalog$1() {
|
|
23910
23981
|
cachedCatalog = void 0;
|
|
23982
|
+
catalogCacheEpoch += 1;
|
|
23911
23983
|
catalogInFlight$1 = null;
|
|
23912
23984
|
catalogInFlightRegion = null;
|
|
23913
23985
|
catalogSnapshotScopesLoaded.clear();
|
|
@@ -23972,8 +24044,10 @@ async function loadConfigCatalog(credentials, options = {}) {
|
|
|
23972
24044
|
/** One in-flight, single-flighted fetch of the live catalog. */
|
|
23973
24045
|
function refreshConfigCatalog(credentials, options = {}) {
|
|
23974
24046
|
if (catalogInFlight$1 && catalogInFlightRegion === credentials.region) return catalogInFlight$1;
|
|
24047
|
+
const epoch = catalogCacheEpoch;
|
|
23975
24048
|
const sameRegionCache = () => cachedCatalog?.region === credentials.region ? cachedCatalog.models : [];
|
|
23976
24049
|
const request = fetchConfigCatalog(credentials, options).then((models) => {
|
|
24050
|
+
if (epoch !== catalogCacheEpoch) return models.length > 0 ? models : sameRegionCache();
|
|
23977
24051
|
if (models.length > 0) {
|
|
23978
24052
|
const fetchedAt = Date.now();
|
|
23979
24053
|
cachedCatalog = {
|
|
@@ -26277,6 +26351,17 @@ const OAUTH_BETA = "oauth-2025-04-20";
|
|
|
26277
26351
|
const CLAUDE_CODE_BETA = "claude-code-20250219";
|
|
26278
26352
|
/** Lets thinking blocks interleave with tool use across a multi-turn loop. */
|
|
26279
26353
|
const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
|
|
26354
|
+
/**
|
|
26355
|
+
* Licenses `thinking.block_binding` in a request body.
|
|
26356
|
+
*
|
|
26357
|
+
* The field is not optional to license: the vendor documents that a body
|
|
26358
|
+
* carrying `block_binding` WITHOUT this beta is a 400 whose message ends
|
|
26359
|
+
* `block_binding: Extra inputs are not permitted` — every request, every turn.
|
|
26360
|
+
* The mid-convo thinking form always sends `block_binding`, so a request that
|
|
26361
|
+
* uses that form must carry this value. The reference sends it for exactly those
|
|
26362
|
+
* models (`THINKING_BINDING_CONTROLS_BETA`, pushed when `supportsMidConvoEffort`).
|
|
26363
|
+
*/
|
|
26364
|
+
const THINKING_BINDING_CONTROLS_BETA = "thinking-binding-controls-2026-08-01";
|
|
26280
26365
|
/** Anthropic protocol revision sent as `anthropic-version`. */
|
|
26281
26366
|
const ANTHROPIC_VERSION = "2023-06-01";
|
|
26282
26367
|
/**
|
|
@@ -26306,12 +26391,18 @@ const ANTHROPIC_VERSION = "2023-06-01";
|
|
|
26306
26391
|
* trades a clear refusal for an unpredictable one. Note that npm's `stable`
|
|
26307
26392
|
* dist-tag can itself sit below a model's floor, so `latest` is the tag to read.
|
|
26308
26393
|
*
|
|
26394
|
+
* It must also be at or above the release that FIRST SHIPPED each model in the
|
|
26395
|
+
* table, whether or not a floor for that model has been observed: upstream has
|
|
26396
|
+
* so far gated a new model on the release that added it (Opus 5.5 arrived in
|
|
26397
|
+
* 2.1.280 and is refused below 2.1.280). Claude Sonnet 5.5 arrived in 2.1.284,
|
|
26398
|
+
* so 2.1.283 could not be relied on to reach it; 2.1.285 is npm `latest`.
|
|
26399
|
+
*
|
|
26309
26400
|
* It is still not a literal that only moves when this package is republished: a
|
|
26310
26401
|
* user whose installed baseline has aged past the server's floor has to be able
|
|
26311
26402
|
* to raise it without waiting for a release. Read the effective value with
|
|
26312
26403
|
* {@link claudeCliVersion}, pin one with {@link setClaudeCliVersion}.
|
|
26313
26404
|
*/
|
|
26314
|
-
const CLAUDE_CLI_VERSION = "2.1.
|
|
26405
|
+
const CLAUDE_CLI_VERSION = "2.1.285";
|
|
26315
26406
|
/**
|
|
26316
26407
|
* Loopback callback port the subscription flow defaults to.
|
|
26317
26408
|
*
|
|
@@ -26669,8 +26760,10 @@ function loopbackRedirectUri(port = DEFAULT_CALLBACK_PORT) {
|
|
|
26669
26760
|
* LOCAL ADDITIONS — the rows the reference snapshot predates
|
|
26670
26761
|
* ---------------------------------------------------------------------------
|
|
26671
26762
|
*
|
|
26672
|
-
* `claude-opus-5-5`
|
|
26673
|
-
* `LOCALLY_CURATED_MODEL_IDS`; the two must be
|
|
26763
|
+
* `claude-opus-5-5` and `claude-sonnet-5-5` are the two such rows today. The
|
|
26764
|
+
* mirror list in the test is `LOCALLY_CURATED_MODEL_IDS`; the two must be
|
|
26765
|
+
* changed together. Sonnet 5.5's own reasons are recorded at its row; what
|
|
26766
|
+
* follows below is Opus 5.5's.
|
|
26674
26767
|
*
|
|
26675
26768
|
* It is CURATED rather than transcribed because the snapshot this table copies
|
|
26676
26769
|
* has no entry for it, and the one thing a transcription table must never do is
|
|
@@ -26722,8 +26815,8 @@ function loopbackRedirectUri(port = DEFAULT_CALLBACK_PORT) {
|
|
|
26722
26815
|
* the user has to reach.
|
|
26723
26816
|
*/
|
|
26724
26817
|
/**
|
|
26725
|
-
*
|
|
26726
|
-
* followed by the curated
|
|
26818
|
+
* Sixteen rows: the snapshot's own 14, in the snapshot's own declaration order,
|
|
26819
|
+
* followed by the curated rows the snapshot predates (see LOCAL ADDITIONS above).
|
|
26727
26820
|
*
|
|
26728
26821
|
* The order matters and is not cosmetic. The test asserts the snapshot's ids
|
|
26729
26822
|
* appear in the table as a SUBSEQUENCE, so a curated row may be appended or
|
|
@@ -26991,7 +27084,26 @@ const CLAUDE_MODELS = Object.freeze([
|
|
|
26991
27084
|
"max"
|
|
26992
27085
|
],
|
|
26993
27086
|
canDisableThinking: false,
|
|
26994
|
-
minCliVersion: "2.1.280"
|
|
27087
|
+
minCliVersion: "2.1.280",
|
|
27088
|
+
bindsThinkingToPrefix: true
|
|
27089
|
+
},
|
|
27090
|
+
{
|
|
27091
|
+
id: "claude-sonnet-5-5",
|
|
27092
|
+
name: "Claude Sonnet 5.5",
|
|
27093
|
+
contextWindow: 1e6,
|
|
27094
|
+
maxTokens: 128e3,
|
|
27095
|
+
supportsImage: true,
|
|
27096
|
+
supportsTemperature: false,
|
|
27097
|
+
thinkingMode: "mid-convo",
|
|
27098
|
+
reasoningEfforts: [
|
|
27099
|
+
"low",
|
|
27100
|
+
"medium",
|
|
27101
|
+
"high",
|
|
27102
|
+
"xhigh",
|
|
27103
|
+
"max"
|
|
27104
|
+
],
|
|
27105
|
+
canDisableThinking: false,
|
|
27106
|
+
bindsThinkingToPrefix: true
|
|
26995
27107
|
}
|
|
26996
27108
|
]);
|
|
26997
27109
|
/** Every id the fallback table knows, in declaration order. */
|
|
@@ -27091,6 +27203,10 @@ function claudeThinkingMode(modelId, catalog) {
|
|
|
27091
27203
|
function claudeModelSupportsTemperature(modelId, catalog) {
|
|
27092
27204
|
return resolveClaudeModel(modelId, catalog ?? CLAUDE_MODELS).supportsTemperature;
|
|
27093
27205
|
}
|
|
27206
|
+
/** Whether one model runs the documented preserved-thinking prefix check. */
|
|
27207
|
+
function claudeModelBindsThinkingToPrefix(modelId, catalog) {
|
|
27208
|
+
return resolveClaudeModel(modelId, catalog ?? CLAUDE_MODELS).bindsThinkingToPrefix === true;
|
|
27209
|
+
}
|
|
27094
27210
|
/** Whether thinking can be turned off for one model. */
|
|
27095
27211
|
function claudeModelCanDisableThinking(modelId, catalog) {
|
|
27096
27212
|
return resolveClaudeModel(modelId, catalog ?? CLAUDE_MODELS).canDisableThinking;
|
|
@@ -27916,6 +28032,7 @@ function claudeBetas(options = {}) {
|
|
|
27916
28032
|
if (options.model === void 0 || !isHaikuModel(options.model)) betas.push(CLAUDE_CODE_BETA);
|
|
27917
28033
|
betas.push(OAUTH_BETA);
|
|
27918
28034
|
if (options.thinking === true) betas.push(INTERLEAVED_THINKING_BETA);
|
|
28035
|
+
if (options.thinkingBinding === true) betas.push(THINKING_BINDING_CONTROLS_BETA);
|
|
27919
28036
|
return betas;
|
|
27920
28037
|
}
|
|
27921
28038
|
/** The `User-Agent` value this line reports for one surface. */
|
|
@@ -28062,7 +28179,7 @@ async function performCatalogLoad(credentials, options) {
|
|
|
28062
28179
|
let models;
|
|
28063
28180
|
let live = false;
|
|
28064
28181
|
try {
|
|
28065
|
-
const response = await fetchFn("https://api.anthropic.com/v1/models", {
|
|
28182
|
+
const response = await fetchFn("https://api.anthropic.com/v1/models?limit=1000", {
|
|
28066
28183
|
method: "GET",
|
|
28067
28184
|
headers: buildClaudeHeaders(credentials.accessToken, { method: "GET" }),
|
|
28068
28185
|
signal: timeoutSignal(options.signal, DISCOVERY_TIMEOUT_MS)
|
|
@@ -28529,8 +28646,15 @@ function classifyFailure(status, bodyText, headers = {}) {
|
|
|
28529
28646
|
retryAfterMs: retryAfter
|
|
28530
28647
|
};
|
|
28531
28648
|
switch (envelope.type) {
|
|
28532
|
-
case ERROR_TYPE.AUTHENTICATION:
|
|
28533
28649
|
case ERROR_TYPE.PERMISSION: return {
|
|
28650
|
+
...base,
|
|
28651
|
+
kind: "request",
|
|
28652
|
+
message: envelope.message ?? "This Claude account is not permitted to use the requested resource.",
|
|
28653
|
+
retryable: false,
|
|
28654
|
+
accountScoped: false,
|
|
28655
|
+
resetsAt: null
|
|
28656
|
+
};
|
|
28657
|
+
case ERROR_TYPE.AUTHENTICATION: return {
|
|
28534
28658
|
...base,
|
|
28535
28659
|
kind: "credential",
|
|
28536
28660
|
message: envelope.message ?? "Claude rejected the stored credential. Sign in again.",
|
|
@@ -28688,7 +28812,9 @@ function classifyFailure(status, bodyText, headers = {}) {
|
|
|
28688
28812
|
* everything, including a caller asking for thinking off: the
|
|
28689
28813
|
* block_binding exists so a prefix mismatch is dropped instead of
|
|
28690
28814
|
* surfacing as a persistent 400, and suppressing it is exactly
|
|
28691
|
-
* how that 400 comes back.
|
|
28815
|
+
* how that 400 comes back. The field is itself a 400 unless the
|
|
28816
|
+
* request carries the thinking-binding-controls beta, so the
|
|
28817
|
+
* adapter reads claudeBodyBindsThinking(body) to add it.
|
|
28692
28818
|
* adaptive -> { type: 'adaptive', display: 'summarized' }, plus
|
|
28693
28819
|
* output_config = { effort } when an effort was named. The budget
|
|
28694
28820
|
* form is rejected by these models.
|
|
@@ -28781,11 +28907,12 @@ function classifyFailure(status, bodyText, headers = {}) {
|
|
|
28781
28907
|
* 'error' event THROWS, because a severed reply flushed as a clean stop is a
|
|
28782
28908
|
* wrong answer rather than a visible failure.
|
|
28783
28909
|
*
|
|
28784
|
-
* Reasoning deltas are
|
|
28785
|
-
*
|
|
28786
|
-
* block may be replayed
|
|
28787
|
-
*
|
|
28788
|
-
*
|
|
28910
|
+
* Reasoning deltas are STREAMED as they arrive (see openThinkingBlock); only the
|
|
28911
|
+
* replay entry waits for content_block_stop, because the signature that decides
|
|
28912
|
+
* whether the block may be replayed arrives at the END of the block. They used
|
|
28913
|
+
* to be buffered until the stop, which made DSH start the step's first-token
|
|
28914
|
+
* clock after all thinking was done - so TPS divided every thinking token by the
|
|
28915
|
+
* answer's time alone, and TTFT absorbed the whole thinking phase.
|
|
28789
28916
|
*
|
|
28790
28917
|
* ---------------------------------------------------------------------------
|
|
28791
28918
|
* 7. PROMPT CACHING IS ON BY DEFAULT, AND IT IS THE REQUEST THAT TURNS IT ON
|
|
@@ -28884,9 +29011,17 @@ function jsonSafeValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
28884
29011
|
seen.delete(value);
|
|
28885
29012
|
}
|
|
28886
29013
|
}
|
|
28887
|
-
/**
|
|
29014
|
+
/**
|
|
29015
|
+
* Strip what the wire cannot carry: a NUL, which would truncate the string
|
|
29016
|
+
* inside a JSON encoder, and an UNPAIRED surrogate. JSON.stringify writes the
|
|
29017
|
+
* latter as a lone `\uD83D` escape, which upstream rejects as invalid JSON ("no
|
|
29018
|
+
* low surrogate in string") - and since the text sits in history, every later
|
|
29019
|
+
* request fails the same way. A tool output sliced mid-emoji is enough to
|
|
29020
|
+
* produce one. Paired surrogates (emoji) are untouched. Same rule as the
|
|
29021
|
+
* reference's sanitizeSurrogates.
|
|
29022
|
+
*/
|
|
28888
29023
|
function sanitizeText(text) {
|
|
28889
|
-
return text.replace(/\0/g, "");
|
|
29024
|
+
return text.replace(/\0|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
28890
29025
|
}
|
|
28891
29026
|
/**
|
|
28892
29027
|
* Whether one failure is a cancellation rather than a real read failure.
|
|
@@ -29101,7 +29236,8 @@ function claudeThinking(model, effort, level, maxTokens, customBudgets) {
|
|
|
29101
29236
|
if (mode === "adaptive") return {
|
|
29102
29237
|
thinking: {
|
|
29103
29238
|
type: "adaptive",
|
|
29104
|
-
display: "summarized"
|
|
29239
|
+
display: "summarized",
|
|
29240
|
+
...claudeModelBindsThinkingToPrefix(model) ? { block_binding: { prefix_mismatch_behavior: "drop_block" } } : {}
|
|
29105
29241
|
},
|
|
29106
29242
|
...namedEffort === void 0 ? {} : { outputConfig: { effort: namedEffort } }
|
|
29107
29243
|
};
|
|
@@ -29664,9 +29800,10 @@ function toolResultContent(blocks, images) {
|
|
|
29664
29800
|
for (const block of blocks) {
|
|
29665
29801
|
if (!isRecord$5(block)) continue;
|
|
29666
29802
|
if (block.type === "text" && typeof block.text === "string") {
|
|
29667
|
-
|
|
29803
|
+
const text = sanitizeText(block.text);
|
|
29804
|
+
if (text !== "") out.push({
|
|
29668
29805
|
type: "text",
|
|
29669
|
-
text
|
|
29806
|
+
text
|
|
29670
29807
|
});
|
|
29671
29808
|
continue;
|
|
29672
29809
|
}
|
|
@@ -29686,7 +29823,7 @@ function toolResultContent(blocks, images) {
|
|
|
29686
29823
|
if (!hasImage) return void 0;
|
|
29687
29824
|
if (out[0]?.type !== "text") out.unshift({
|
|
29688
29825
|
type: "text",
|
|
29689
|
-
text: ""
|
|
29826
|
+
text: "(see attached image)"
|
|
29690
29827
|
});
|
|
29691
29828
|
return out;
|
|
29692
29829
|
}
|
|
@@ -29815,6 +29952,17 @@ function buildClaudeRequestBody(options, images = NO_RESOLVED_IMAGES, request =
|
|
|
29815
29952
|
return body;
|
|
29816
29953
|
}
|
|
29817
29954
|
/**
|
|
29955
|
+
* Whether one built body carries `thinking.block_binding`.
|
|
29956
|
+
*
|
|
29957
|
+
* Read off the BODY rather than re-derived from the catalog, because the header
|
|
29958
|
+
* this answers for (THINKING_BINDING_CONTROLS_BETA) exists to license that one
|
|
29959
|
+
* field: sent without it the field is a 400 on every request, so the header must
|
|
29960
|
+
* follow the bytes, not a second copy of the rule that produced them.
|
|
29961
|
+
*/
|
|
29962
|
+
function claudeBodyBindsThinking(body) {
|
|
29963
|
+
return isRecord$5(body.thinking) && body.thinking.block_binding !== void 0;
|
|
29964
|
+
}
|
|
29965
|
+
/**
|
|
29818
29966
|
* Fresh stream state.
|
|
29819
29967
|
*
|
|
29820
29968
|
* @param toolNames - the SAME ClaudeToolNames the request was built with, so a
|
|
@@ -29839,6 +29987,7 @@ function createStreamState$1(toolNames) {
|
|
|
29839
29987
|
cacheReadTokens: 0,
|
|
29840
29988
|
cacheWriteTokens: 0,
|
|
29841
29989
|
reasoningTokens: 0,
|
|
29990
|
+
reportedReasoningTokens: void 0,
|
|
29842
29991
|
sawUsage: false,
|
|
29843
29992
|
finished: false
|
|
29844
29993
|
};
|
|
@@ -29935,14 +30084,15 @@ function closeToolCall(state, contentIndex) {
|
|
|
29935
30084
|
}];
|
|
29936
30085
|
}
|
|
29937
30086
|
/**
|
|
29938
|
-
*
|
|
30087
|
+
* Close one thinking block: end it for the caller and record its replay entry.
|
|
29939
30088
|
*
|
|
29940
|
-
*
|
|
29941
|
-
*
|
|
29942
|
-
*
|
|
29943
|
-
*
|
|
30089
|
+
* Its text already reached the caller as it streamed (see openThinkingBlock);
|
|
30090
|
+
* what waits for the close is the replay entry, because the signature arrives
|
|
30091
|
+
* last. A signed block is replayed verbatim afterwards. An unsigned one is
|
|
30092
|
+
* still shown (the model did think) but the request builder drops it on the
|
|
30093
|
+
* next turn, since it cannot be sent back.
|
|
29944
30094
|
*/
|
|
29945
|
-
function
|
|
30095
|
+
function closeThinkingBlock(state, contentIndex) {
|
|
29946
30096
|
const pending = state.pending.get(contentIndex);
|
|
29947
30097
|
if (pending === void 0) return [];
|
|
29948
30098
|
state.pending.delete(contentIndex);
|
|
@@ -29962,20 +30112,45 @@ function emitPendingThinking(state, contentIndex) {
|
|
|
29962
30112
|
...pending.signature === "" ? {} : { signature: pending.signature }
|
|
29963
30113
|
};
|
|
29964
30114
|
state.reasoningTokens += estimateReasoningTokens(text);
|
|
30115
|
+
return [{
|
|
30116
|
+
type: "block-end",
|
|
30117
|
+
index: pending.index,
|
|
30118
|
+
block
|
|
30119
|
+
}];
|
|
30120
|
+
}
|
|
30121
|
+
/**
|
|
30122
|
+
* Open one thinking block and stream whatever text its start already carries.
|
|
30123
|
+
*
|
|
30124
|
+
* STREAMED, not held to content_block_stop: DSH times a step's first token from
|
|
30125
|
+
* the first non-empty delta, and the usage it divides by that window counts every
|
|
30126
|
+
* thinking token. Holding the text until the block closed started the clock
|
|
30127
|
+
* after all the thinking was done - inflating TPS and TTFT alike - and kept the
|
|
30128
|
+
* UI blank for the whole thinking phase. Only the replay entry waits for the
|
|
30129
|
+
* close, because the signature arrives last.
|
|
30130
|
+
*/
|
|
30131
|
+
function openThinkingBlock(state, contentIndex, block, kind) {
|
|
30132
|
+
const index = state.blocks.length;
|
|
30133
|
+
state.blocks.push({
|
|
30134
|
+
type: "reasoning",
|
|
30135
|
+
text: ""
|
|
30136
|
+
});
|
|
30137
|
+
const seed = kind === "thinking" ? asString$2(block.thinking) ?? "" : "";
|
|
30138
|
+
state.pending.set(contentIndex, {
|
|
30139
|
+
index,
|
|
30140
|
+
kind,
|
|
30141
|
+
text: seed,
|
|
30142
|
+
signature: asString$2(block.signature) ?? "",
|
|
30143
|
+
data: asString$2(block.data) ?? ""
|
|
30144
|
+
});
|
|
29965
30145
|
const out = [{
|
|
29966
30146
|
type: "block-start",
|
|
29967
|
-
index
|
|
30147
|
+
index,
|
|
29968
30148
|
blockType: "reasoning"
|
|
29969
30149
|
}];
|
|
29970
|
-
if (
|
|
30150
|
+
if (seed !== "") out.push({
|
|
29971
30151
|
type: "reasoning-delta",
|
|
29972
|
-
index
|
|
29973
|
-
text
|
|
29974
|
-
});
|
|
29975
|
-
out.push({
|
|
29976
|
-
type: "block-end",
|
|
29977
|
-
index: pending.index,
|
|
29978
|
-
block
|
|
30152
|
+
index,
|
|
30153
|
+
text: seed
|
|
29979
30154
|
});
|
|
29980
30155
|
return out;
|
|
29981
30156
|
}
|
|
@@ -29992,7 +30167,7 @@ function flushOpenBlocks(state) {
|
|
|
29992
30167
|
});
|
|
29993
30168
|
for (const [contentIndex, pending] of state.pending) closers.push({
|
|
29994
30169
|
index: pending.index,
|
|
29995
|
-
close: () =>
|
|
30170
|
+
close: () => closeThinkingBlock(state, contentIndex)
|
|
29996
30171
|
});
|
|
29997
30172
|
closers.sort((left, right) => left.index - right.index);
|
|
29998
30173
|
const out = [];
|
|
@@ -30011,17 +30186,25 @@ function flushOpenBlocks(state) {
|
|
|
30011
30186
|
* the billed-input total unaccountable.
|
|
30012
30187
|
*/
|
|
30013
30188
|
function tokenUsage(state) {
|
|
30189
|
+
const reasoningTokens = Math.min(state.reportedReasoningTokens ?? state.reasoningTokens, state.outputTokens);
|
|
30014
30190
|
return {
|
|
30015
30191
|
inputTokens: state.inputTokens,
|
|
30016
30192
|
outputTokens: state.outputTokens,
|
|
30017
30193
|
...state.cacheReadTokens > 0 ? { cacheReadTokens: state.cacheReadTokens } : {},
|
|
30018
30194
|
...state.cacheWriteTokens > 0 ? { cacheWriteTokens: state.cacheWriteTokens } : {},
|
|
30019
|
-
...
|
|
30195
|
+
...reasoningTokens > 0 ? { reasoningTokens } : {}
|
|
30020
30196
|
};
|
|
30021
30197
|
}
|
|
30022
30198
|
function finishReasonFor(state) {
|
|
30023
30199
|
const reason = state.finishReason ?? "";
|
|
30024
|
-
if (reason === "
|
|
30200
|
+
if (reason === "refusal" || reason === "sensitive") return {
|
|
30201
|
+
kind: "error",
|
|
30202
|
+
failure: {
|
|
30203
|
+
message: state.stopExplanation ?? "Claude declined to respond (stop_reason: " + reason + ").",
|
|
30204
|
+
code: "PROVIDER_ERROR"
|
|
30205
|
+
}
|
|
30206
|
+
};
|
|
30207
|
+
if (reason === "max_tokens" || reason === "model_context_window_exceeded") return { kind: "max-tokens" };
|
|
30025
30208
|
if (state.hasToolCall || reason === "tool_use") return { kind: "tool-calls" };
|
|
30026
30209
|
return { kind: "stop" };
|
|
30027
30210
|
}
|
|
@@ -30069,6 +30252,8 @@ function applyUsage(state, usage, cumulativeOutput) {
|
|
|
30069
30252
|
if (usage.cache_creation_input_tokens !== void 0) state.cacheWriteTokens = numberOr(usage.cache_creation_input_tokens, state.cacheWriteTokens);
|
|
30070
30253
|
if (usage.cache_read_input_tokens !== void 0) state.cacheReadTokens = numberOr(usage.cache_read_input_tokens, state.cacheReadTokens);
|
|
30071
30254
|
if (usage.output_tokens !== void 0) state.outputTokens = cumulativeOutput ? numberOr(usage.output_tokens, state.outputTokens) : state.outputTokens + numberOr(usage.output_tokens, 0);
|
|
30255
|
+
const details = isRecord$5(usage.output_tokens_details) ? usage.output_tokens_details : void 0;
|
|
30256
|
+
if (details !== void 0 && details.thinking_tokens !== void 0) state.reportedReasoningTokens = numberOr(details.thinking_tokens, state.reportedReasoningTokens ?? 0);
|
|
30072
30257
|
}
|
|
30073
30258
|
/**
|
|
30074
30259
|
* Feed one SSE line of a Messages response into the state.
|
|
@@ -30139,19 +30324,8 @@ function processStreamLine(line, state) {
|
|
|
30139
30324
|
return out;
|
|
30140
30325
|
}
|
|
30141
30326
|
if (blockType === "thinking" || blockType === "redacted_thinking") {
|
|
30142
|
-
|
|
30143
|
-
|
|
30144
|
-
state.blocks.push({
|
|
30145
|
-
type: "reasoning",
|
|
30146
|
-
text: ""
|
|
30147
|
-
});
|
|
30148
|
-
state.pending.set(contentIndex, {
|
|
30149
|
-
index,
|
|
30150
|
-
kind: blockType === "redacted_thinking" ? "redacted_thinking" : "thinking",
|
|
30151
|
-
text: asString$2(block.thinking) ?? "",
|
|
30152
|
-
signature: asString$2(block.signature) ?? "",
|
|
30153
|
-
data: asString$2(block.data) ?? ""
|
|
30154
|
-
});
|
|
30327
|
+
out.push(...closeThinkingBlock(state, contentIndex));
|
|
30328
|
+
out.push(...openThinkingBlock(state, contentIndex, block, blockType));
|
|
30155
30329
|
return out;
|
|
30156
30330
|
}
|
|
30157
30331
|
out.push(...openTextAt(state, contentIndex).chunks);
|
|
@@ -30184,8 +30358,14 @@ function processStreamLine(line, state) {
|
|
|
30184
30358
|
}
|
|
30185
30359
|
if (deltaType === "thinking_delta") {
|
|
30186
30360
|
const pending = state.pending.get(contentIndex);
|
|
30187
|
-
const text = asString$2(delta.thinking) ?? "";
|
|
30188
|
-
if (pending
|
|
30361
|
+
const text = sanitizeText(asString$2(delta.thinking) ?? "");
|
|
30362
|
+
if (pending === void 0 || pending.kind !== "thinking" || text === "") return out;
|
|
30363
|
+
pending.text += text;
|
|
30364
|
+
out.push({
|
|
30365
|
+
type: "reasoning-delta",
|
|
30366
|
+
index: pending.index,
|
|
30367
|
+
text
|
|
30368
|
+
});
|
|
30189
30369
|
return out;
|
|
30190
30370
|
}
|
|
30191
30371
|
const text = asString$2(delta.text) ?? "";
|
|
@@ -30204,13 +30384,16 @@ function processStreamLine(line, state) {
|
|
|
30204
30384
|
if (type === "content_block_stop") {
|
|
30205
30385
|
const contentIndex = numberOr(event.index, -1);
|
|
30206
30386
|
if (state.toolCalls.has(contentIndex)) return closeToolCall(state, contentIndex);
|
|
30207
|
-
if (state.pending.has(contentIndex)) return
|
|
30387
|
+
if (state.pending.has(contentIndex)) return closeThinkingBlock(state, contentIndex);
|
|
30208
30388
|
return closeTextBlock(state, contentIndex);
|
|
30209
30389
|
}
|
|
30210
30390
|
if (type === "message_delta") {
|
|
30211
30391
|
const delta = isRecord$5(event.delta) ? event.delta : void 0;
|
|
30212
30392
|
const stop = delta === void 0 ? void 0 : asString$2(delta.stop_reason);
|
|
30213
30393
|
if (stop !== void 0 && stop !== "") state.finishReason = stop;
|
|
30394
|
+
const details = delta !== void 0 && isRecord$5(delta.stop_details) ? delta.stop_details : void 0;
|
|
30395
|
+
const explanation = details === void 0 ? void 0 : asString$2(details.explanation);
|
|
30396
|
+
if (explanation !== void 0 && explanation !== "") state.stopExplanation = explanation;
|
|
30214
30397
|
const usage = isRecord$5(event.usage) ? event.usage : void 0;
|
|
30215
30398
|
if (usage !== void 0) applyUsage(state, usage, true);
|
|
30216
30399
|
return out;
|
|
@@ -30645,7 +30828,7 @@ async function refreshAccessToken(refreshToken, options = {}) {
|
|
|
30645
30828
|
try {
|
|
30646
30829
|
return await postTokenGrant(body, options, false, [refreshToken]);
|
|
30647
30830
|
} catch (error) {
|
|
30648
|
-
if (error instanceof
|
|
30831
|
+
if (!(error instanceof ClaudeRetryableError)) throw error;
|
|
30649
30832
|
lastError = error;
|
|
30650
30833
|
}
|
|
30651
30834
|
if (attempt < REFRESH_MAX_ATTEMPTS - 1) await sleep(REFRESH_BACKOFF_BASE_MS * 2 ** attempt, options.signal);
|
|
@@ -31730,19 +31913,20 @@ var ClaudeAdapter = class extends LlmAdapter {
|
|
|
31730
31913
|
* no credentials were resolved for it.
|
|
31731
31914
|
*/
|
|
31732
31915
|
async attemptRequest(credentials, requestOptions, images, toolNames, thinking, signal, fetchFn) {
|
|
31733
|
-
const
|
|
31916
|
+
const payload = buildClaudeRequestBody(requestOptions, images, {
|
|
31734
31917
|
toolNames,
|
|
31735
31918
|
cacheControl: true
|
|
31736
|
-
})
|
|
31919
|
+
});
|
|
31737
31920
|
try {
|
|
31738
31921
|
return await fetchFn("https://api.anthropic.com/v1/messages?beta=true", {
|
|
31739
31922
|
method: "POST",
|
|
31740
31923
|
headers: buildClaudeHeaders(credentials.accessToken, {
|
|
31741
31924
|
model: requestOptions.model,
|
|
31742
31925
|
thinking,
|
|
31926
|
+
thinkingBinding: claudeBodyBindsThinking(payload),
|
|
31743
31927
|
method: "POST"
|
|
31744
31928
|
}),
|
|
31745
|
-
body,
|
|
31929
|
+
body: JSON.stringify(payload),
|
|
31746
31930
|
signal
|
|
31747
31931
|
});
|
|
31748
31932
|
} catch (error) {
|
|
@@ -33512,24 +33696,18 @@ function registerClaudeRoutes(ctx, store, modelSettings, preferences, options =
|
|
|
33512
33696
|
* is addressed at THAT account, which is what makes a re-login repair the row
|
|
33513
33697
|
* the user clicked instead of adding a second one.
|
|
33514
33698
|
*/
|
|
33515
|
-
const loginStore = (accountId) => {
|
|
33516
|
-
|
|
33517
|
-
|
|
33518
|
-
|
|
33519
|
-
|
|
33520
|
-
|
|
33521
|
-
|
|
33522
|
-
};
|
|
33523
|
-
return {
|
|
33524
|
-
read: () => base.read(),
|
|
33525
|
-
write: async (credentials) => {
|
|
33526
|
-
await base.write(credentials);
|
|
33527
|
-
if (pool !== void 0 && accountId !== void 0) await pool.clearAuthFailed(accountId).catch(() => void 0);
|
|
33528
|
-
clearCachedQuota$1();
|
|
33529
|
-
clearCachedCatalog();
|
|
33699
|
+
const loginStore = (accountId) => ({
|
|
33700
|
+
read: async () => pool !== void 0 && accountId !== void 0 ? pool.credentialStoreFor(accountId).read() : (await activeAccount()).credentials ?? null,
|
|
33701
|
+
write: async (credentials) => {
|
|
33702
|
+
if (pool === void 0) await store.saveAccount(credentials);
|
|
33703
|
+
else {
|
|
33704
|
+
const landed = await pool.addAccount(credentials);
|
|
33705
|
+
if (accountId !== void 0 && landed.id === accountId) await pool.clearAuthFailed(accountId).catch(() => void 0);
|
|
33530
33706
|
}
|
|
33531
|
-
|
|
33532
|
-
|
|
33707
|
+
clearCachedQuota$1();
|
|
33708
|
+
clearCachedCatalog();
|
|
33709
|
+
}
|
|
33710
|
+
});
|
|
33533
33711
|
const readStatus = (quotaError = null) => activeAccount().then((active) => getClaudeWebStatus(store, modelSettings, preferences, options, quotaError, active));
|
|
33534
33712
|
return ctx.webServer.register({
|
|
33535
33713
|
kind: "prefix",
|
|
@@ -35133,6 +35311,40 @@ function inheritedRouteOf(agent) {
|
|
|
35133
35311
|
model: asString$1(agent?.options?.model)
|
|
35134
35312
|
};
|
|
35135
35313
|
}
|
|
35314
|
+
/**
|
|
35315
|
+
* Resolve the provider/model pair a call actually names, tolerating a model
|
|
35316
|
+
* value that already carries its provider prefix.
|
|
35317
|
+
*
|
|
35318
|
+
* The discovery tool that hands a model to the model renders each route as
|
|
35319
|
+
* `provider/model` (`minimax-code/MiniMax-M3.1-Flash-Preview`), and the
|
|
35320
|
+
* `model` parameter is documented as "Model id interpreted by provider". A
|
|
35321
|
+
* model that copies that line into `model` while naming `provider` separately
|
|
35322
|
+
* therefore sends `provider` = `minimax-code` and `model` =
|
|
35323
|
+
* `minimax-code/MiniMax-M3.1-Flash-Preview`. That is the single most likely way
|
|
35324
|
+
* a correct-looking call is formed, and treating the doubled text as an opaque
|
|
35325
|
+
* model id both denies it and — had it been authorized — resolved a model the
|
|
35326
|
+
* provider does not have. The prefix is stripped only when the remainder is a
|
|
35327
|
+
* non-empty model id.
|
|
35328
|
+
*
|
|
35329
|
+
* @param provider - Provider the call named, when it named one.
|
|
35330
|
+
* @param model - Model the call named, when it named one.
|
|
35331
|
+
* @returns the exact pair, or undefined when the call named no complete pair.
|
|
35332
|
+
*/
|
|
35333
|
+
function explicitRoutePair(provider, model) {
|
|
35334
|
+
const providerId = asString$1(provider);
|
|
35335
|
+
const modelId = asString$1(model);
|
|
35336
|
+
if (providerId === void 0 || modelId === void 0) return void 0;
|
|
35337
|
+
if (providerId.length === 0 || modelId.length === 0) return void 0;
|
|
35338
|
+
const prefix = providerId + "/";
|
|
35339
|
+
if (modelId.startsWith(prefix) && modelId.length > prefix.length) return {
|
|
35340
|
+
provider: providerId,
|
|
35341
|
+
model: modelId.slice(prefix.length)
|
|
35342
|
+
};
|
|
35343
|
+
return {
|
|
35344
|
+
provider: providerId,
|
|
35345
|
+
model: modelId
|
|
35346
|
+
};
|
|
35347
|
+
}
|
|
35136
35348
|
/** Whether an exact route is authorized by a allowlist. */
|
|
35137
35349
|
function routesInclude(allowed, provider, model) {
|
|
35138
35350
|
return allowed.some((route) => route.provider === provider && route.model === model);
|
|
@@ -35177,6 +35389,32 @@ function unauthorizedRouteReason(provider, model, allowed, explicit) {
|
|
|
35177
35389
|
return `subagent model selection: ${explicit ? "the model this call selected is not on the Session allowlist" : "the route this call would inherit from the parent is not on the Session allowlist"} (${route}). Provide an authorized provider and model — ${authorizedRoutesText(allowed)} — using list_subagent_models to inspect their reasoning efforts.`;
|
|
35178
35390
|
}
|
|
35179
35391
|
/**
|
|
35392
|
+
* Build the denial reason for a call that spelled an authorized route the way
|
|
35393
|
+
* the discovery tool prints it.
|
|
35394
|
+
*
|
|
35395
|
+
* `list_subagent_models` renders every route as `provider/model`, and the
|
|
35396
|
+
* delegation tool documents `model` as "Model id interpreted by provider". A
|
|
35397
|
+
* model that copies the printed line into `model` while also naming `provider`
|
|
35398
|
+
* therefore sends a doubled id — the single most likely way a correct-looking
|
|
35399
|
+
* call is formed, and the reason a delegation is refused even though the route
|
|
35400
|
+
* the user picked is authorized.
|
|
35401
|
+
*
|
|
35402
|
+
* The id is deliberately NOT accepted here. The guard is the plugin plane, so
|
|
35403
|
+
* it cannot rewrite the arguments the delegation tool executes: that tool hands
|
|
35404
|
+
* `model` to the adapter verbatim, and the adapter matches it against its own
|
|
35405
|
+
* table, where `minimax-code/MiniMax-M3.1-Flash-Preview` is not a model. A
|
|
35406
|
+
* guard that accepted the doubled text would turn this clear refusal into a
|
|
35407
|
+
* downstream `NO_ADAPTER`/unknown-model failure. Naming the two fields
|
|
35408
|
+
* separately is what actually unblocks the call.
|
|
35409
|
+
*
|
|
35410
|
+
* @param pair - The authorized pair the call described as one string.
|
|
35411
|
+
* @param allowed - Routes the calling Session authorizes.
|
|
35412
|
+
* @returns the corrective reason handed back to the model.
|
|
35413
|
+
*/
|
|
35414
|
+
function prefixedModelReason(pair, allowed) {
|
|
35415
|
+
return `subagent model selection: this call passed a whole route as \`model\`. Pass \`provider\` and \`model\` as two separate fields — provider "${pair.provider}", model "${pair.model}" — because \`provider/model\` is only how list_subagent_models prints a route, not a model id. Authorized routes: ${authorizedRoutesText(allowed)}.`;
|
|
35416
|
+
}
|
|
35417
|
+
/**
|
|
35180
35418
|
* Build the denial reason for an inherit-mode delegation that named a route.
|
|
35181
35419
|
* The fork backend accepts no route parameters, so a named pair is not a
|
|
35182
35420
|
* partial override the backend would ignore — it is a request to change the
|
|
@@ -35250,6 +35488,8 @@ function delegationDenialReason(agent, toolName, args, preference, sessions, too
|
|
|
35250
35488
|
if (requestedProvider === void 0) return requestedModel === void 0 ? missingRouteReason(allowed) : partialRouteReason("provider", requestedModel, allowed);
|
|
35251
35489
|
if (requestedModel === void 0) return partialRouteReason("model", requestedProvider, allowed);
|
|
35252
35490
|
if (routesInclude(allowed, requestedProvider, requestedModel)) return void 0;
|
|
35491
|
+
const pair = explicitRoutePair(requestedProvider, requestedModel);
|
|
35492
|
+
if (pair !== void 0 && pair.model !== requestedModel && routesInclude(allowed, pair.provider, pair.model)) return prefixedModelReason(pair, allowed);
|
|
35253
35493
|
return unauthorizedRouteReason(requestedProvider, requestedModel, allowed, true);
|
|
35254
35494
|
}
|
|
35255
35495
|
/**
|