@eddyskywalker/dsh-chatgpt-subscription 0.57.0 → 0.59.0
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 +43 -0
- package/README.md +3 -3
- package/lib/client.js +136 -23
- package/lib/client.js.map +1 -1
- package/lib/codemode.js +1565 -0
- package/lib/index.js +1121 -530
- package/lib/tool-result-pruner.js +1 -1
- package/lib/types/client/CodexImageToolView.d.ts.map +1 -1
- package/lib/types/client/minimax-code/commands-contract.d.ts +1 -1
- package/lib/types/client/progress-updates/progress-row.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/compat.d.ts +10 -1
- package/lib/types/compat.d.ts.map +1 -1
- package/lib/types/entry-codemode.d.ts +37 -0
- package/lib/types/entry-codemode.d.ts.map +1 -0
- package/lib/types/entry-tool-result-pruner.d.ts +1 -1
- package/lib/types/host/agent-preset.d.ts +24 -2
- package/lib/types/host/agent-preset.d.ts.map +1 -1
- package/lib/types/host/antigravity/image-tool.d.ts +10 -2
- package/lib/types/host/antigravity/image-tool.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/install.d.ts +10 -4
- package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/projection.d.ts +17 -0
- package/lib/types/host/cache-warmup/projection.d.ts.map +1 -1
- package/lib/types/host/codemode/discovery.d.ts +33 -0
- package/lib/types/host/codemode/discovery.d.ts.map +1 -0
- package/lib/types/host/codemode/executor.d.ts +140 -0
- package/lib/types/host/codemode/executor.d.ts.map +1 -0
- package/lib/types/host/codemode/retention.d.ts +34 -0
- package/lib/types/host/codemode/retention.d.ts.map +1 -0
- package/lib/types/host/codemode/rules.d.ts +183 -0
- package/lib/types/host/codemode/rules.d.ts.map +1 -0
- package/lib/types/host/codemode/scope.d.ts +59 -0
- package/lib/types/host/codemode/scope.d.ts.map +1 -0
- package/lib/types/host/codemode/transport.d.ts +258 -0
- package/lib/types/host/codemode/transport.d.ts.map +1 -0
- package/lib/types/host/codex-account-pool.d.ts.map +1 -1
- package/lib/types/host/codex-adopt.d.ts.map +1 -1
- package/lib/types/host/codex-catalog.d.ts +5 -3
- package/lib/types/host/codex-catalog.d.ts.map +1 -1
- package/lib/types/host/codex-identity.d.ts +130 -0
- package/lib/types/host/codex-identity.d.ts.map +1 -0
- package/lib/types/host/codex-images.d.ts +12 -0
- package/lib/types/host/codex-images.d.ts.map +1 -1
- package/lib/types/host/kimi-code/repeat-breaker.d.ts +1 -1
- package/lib/types/host/oauth-service.d.ts +0 -16
- package/lib/types/host/oauth-service.d.ts.map +1 -1
- package/lib/types/host/token-store.d.ts +9 -0
- package/lib/types/host/token-store.d.ts.map +1 -1
- package/lib/types/host/usage-service.d.ts.map +1 -1
- package/lib/types/host/workbuddy/adapter.d.ts.map +1 -1
- package/lib/types/host/workbuddy/client.d.ts.map +1 -1
- package/lib/types/host/workbuddy/model-catalog.d.ts +56 -44
- package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -1
- package/lib/types/host/workbuddy/routes.d.ts.map +1 -1
- package/lib/types/shared/workbuddy-contracts.d.ts +8 -8
- package/package.json +54 -46
- package/presets/dispatch/README.md +12 -3
- package/presets/dispatch/agent.cordis.yml +10 -9
- package/presets/dispatch/preset.yml +1 -1
package/lib/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import z$1 from "@deepseek-ai/schemastery";
|
|
11
11
|
import fs, { chmod, lstat, mkdir, open, readFile, realpath, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
12
|
-
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
12
|
+
import path, { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
13
13
|
import os, { homedir, platform, userInfo } from "node:os";
|
|
14
14
|
import { constants, createCipheriv, createDecipheriv, createHash, createHmac, publicEncrypt, randomBytes, randomUUID } from "node:crypto";
|
|
15
15
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -807,11 +807,20 @@ const ROUTE_PREFIX$5 = "/api/dsh-chatgpt-subscription";
|
|
|
807
807
|
* `test/responses-client.test.ts` asserts the two agree, so a release that
|
|
808
808
|
* forgets this constant fails the suite instead of shipping quietly.
|
|
809
809
|
*/
|
|
810
|
-
const PLUGIN_VERSION = "0.
|
|
810
|
+
const PLUGIN_VERSION = "0.59.0";
|
|
811
811
|
const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
|
|
812
812
|
const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
|
|
813
813
|
const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
|
|
814
814
|
const CODEX_IMAGE_GENERATION_URL = `${CODEX_API_BASE}/images/generations`;
|
|
815
|
+
/**
|
|
816
|
+
* The edit route: the same image surface, addressed for reference-image input.
|
|
817
|
+
*
|
|
818
|
+
* A generation has nothing to modify and goes to `/images/generations`; a
|
|
819
|
+
* request that names reference images is an EDIT and goes here, with those
|
|
820
|
+
* images carried inline in a JSON `images` array. It is a separate route, not a
|
|
821
|
+
* flag on the generation body, so the two are stated as two constants.
|
|
822
|
+
*/
|
|
823
|
+
const CODEX_IMAGE_EDIT_URL = `${CODEX_API_BASE}/images/edits`;
|
|
815
824
|
const CODEX_SEARCH_URL = `${CODEX_API_BASE}/alpha/search`;
|
|
816
825
|
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
817
826
|
/**
|
|
@@ -1345,6 +1354,144 @@ async function rehydrateCatalogCache(name, parseModels, accept, currentFetchedAt
|
|
|
1345
1354
|
return snapshot.fetchedAt;
|
|
1346
1355
|
}
|
|
1347
1356
|
//#endregion
|
|
1357
|
+
//#region src/host/codex-identity.ts
|
|
1358
|
+
/**
|
|
1359
|
+
* Who one ChatGPT credential belongs to, and what makes two sign-ins the same
|
|
1360
|
+
* account.
|
|
1361
|
+
*
|
|
1362
|
+
* TWO IDENTITIES, AND THEY ARE NOT THE SAME THING.
|
|
1363
|
+
*
|
|
1364
|
+
* - The WORKSPACE identity is `chatgpt_account_id`, carried as
|
|
1365
|
+
* `credentials.accountId` and sent upstream as `chatgpt-account-id`. It
|
|
1366
|
+
* decides which workspace a request is billed to and what its listing serves,
|
|
1367
|
+
* and every member of that workspace shares it.
|
|
1368
|
+
* - The LOCAL LOGIN identity is (workspace, user): that same workspace id plus a
|
|
1369
|
+
* stable id for the person who signed in. Two members of one workspace are two
|
|
1370
|
+
* accounts here — separate tokens, separate rows, separately selectable — and
|
|
1371
|
+
* re-authorizing the same person updates the row that person already owns.
|
|
1372
|
+
*
|
|
1373
|
+
* Keying the account pool on the workspace alone is the bug this module exists to
|
|
1374
|
+
* prevent. Adding a second member of a workspace replaced the first: the card read
|
|
1375
|
+
* "1 account signed in", the earlier sign-in was gone from the list, and nothing in
|
|
1376
|
+
* the credential was wrong — the pool simply believed both sign-ins were one
|
|
1377
|
+
* account, because that is what the key said.
|
|
1378
|
+
*
|
|
1379
|
+
* The user id is read from the tokens themselves, most explicit claim first:
|
|
1380
|
+
* `chatgpt_user_id` (the field upstream states for exactly this purpose), then
|
|
1381
|
+
* the OIDC `sub` that an id token always carries, then the address. Only when none
|
|
1382
|
+
* of the three is present does the workspace stand in on its own, because nothing
|
|
1383
|
+
* better exists then and merging is what every row written before this module
|
|
1384
|
+
* already did. The workspace id alone is NEVER treated as a user identity while
|
|
1385
|
+
* some user claim is available.
|
|
1386
|
+
*/
|
|
1387
|
+
/** Namespace of the ChatGPT auth claims inside an id or access token. */
|
|
1388
|
+
const CODEX_AUTH_CLAIMS_KEY = "https://api.openai.com/auth";
|
|
1389
|
+
/**
|
|
1390
|
+
* The claims of a JWT, decoded in process, or nothing.
|
|
1391
|
+
*
|
|
1392
|
+
* The payload is the second base64url segment; the signature is never touched and
|
|
1393
|
+
* the result is used only for the non-secret facts above. Never sent anywhere: a
|
|
1394
|
+
* token handed to a decoder service is a token handed away.
|
|
1395
|
+
*
|
|
1396
|
+
* No verification is claimed and none is possible here. What this does instead is
|
|
1397
|
+
* refuse to invent: a token that does not decode is treated as stating nothing,
|
|
1398
|
+
* which leaves the caller on its own fallback rather than on a guessed identity.
|
|
1399
|
+
*/
|
|
1400
|
+
function parseJwtClaims(token) {
|
|
1401
|
+
if (token === void 0) return void 0;
|
|
1402
|
+
const parts = token.split(".");
|
|
1403
|
+
if (parts.length !== 3) return void 0;
|
|
1404
|
+
try {
|
|
1405
|
+
const value = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
1406
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
1407
|
+
} catch {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* What the tokens state about their owner.
|
|
1413
|
+
*
|
|
1414
|
+
* The id token is read before the access token, because it is the document that
|
|
1415
|
+
* describes the sign-in; the access token is the fallback for credentials stored
|
|
1416
|
+
* without one. First statement wins, so a value the id token states is never
|
|
1417
|
+
* overwritten by the access token's copy of it.
|
|
1418
|
+
*/
|
|
1419
|
+
function extractIdentity(credentials) {
|
|
1420
|
+
const result = {};
|
|
1421
|
+
let subject;
|
|
1422
|
+
for (const token of [credentials.idToken, credentials.accessToken]) {
|
|
1423
|
+
const claims = parseJwtClaims(token);
|
|
1424
|
+
if (claims === void 0) continue;
|
|
1425
|
+
const nested = claims[CODEX_AUTH_CLAIMS_KEY];
|
|
1426
|
+
result.email ??= stringClaim(claims.email);
|
|
1427
|
+
result.planType ??= stringClaim(claims.chatgpt_plan_type) ?? stringClaim(nested?.chatgpt_plan_type);
|
|
1428
|
+
result.userId ??= stringClaim(claims.chatgpt_user_id) ?? stringClaim(nested?.chatgpt_user_id);
|
|
1429
|
+
result.accountId ??= stringClaim(claims.chatgpt_account_id) ?? stringClaim(nested?.chatgpt_account_id) ?? stringClaim(claims.organizations?.[0]?.id) ?? stringClaim(nested?.organizations?.[0]?.id);
|
|
1430
|
+
subject ??= stringClaim(claims.sub);
|
|
1431
|
+
}
|
|
1432
|
+
result.userId ??= subject;
|
|
1433
|
+
return result;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* The stable id of the human behind a credential, or nothing.
|
|
1437
|
+
*
|
|
1438
|
+
* Explicit claim first, then `sub`, and deliberately NOT the email: an address is
|
|
1439
|
+
* the caller's fallback (see {@link codexAccountIdentity}), while this function
|
|
1440
|
+
* answers the narrower question "does this credential name its user outright?".
|
|
1441
|
+
* A persisted `userId` wins over anything re-derived from the tokens, so an opaque
|
|
1442
|
+
* access token handed back by a rotation cannot make an account look like a
|
|
1443
|
+
* different one.
|
|
1444
|
+
*/
|
|
1445
|
+
function codexUserId(credentials) {
|
|
1446
|
+
const stored = nonEmpty$3(credentials.userId);
|
|
1447
|
+
if (stored !== void 0) return stored;
|
|
1448
|
+
return extractIdentity(credentials).userId;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* One opaque key per local login: the pool's dedupe key, and the scope of every
|
|
1452
|
+
* per-account cache this line keeps.
|
|
1453
|
+
*
|
|
1454
|
+
* A digest of an EXPLICIT serialization rather than a readable join, for three
|
|
1455
|
+
* reasons that are really one: the string is a label, not data.
|
|
1456
|
+
*
|
|
1457
|
+
* - A separator can be forged. `workspace:user` joins two values that may
|
|
1458
|
+
* themselves contain the separator, so `a` + `b:c` and `a:b` + `c` would have to
|
|
1459
|
+
* be the same key. A JSON array has no such ambiguity: quotes and escapes are
|
|
1460
|
+
* the boundary, and they cannot be re-cut by a value.
|
|
1461
|
+
* - The key becomes a FILE NAME. `catalogSnapshotName` folds everything outside
|
|
1462
|
+
* `[a-z0-9]` to `_`, so a readable key would collide with its neighbours after
|
|
1463
|
+
* folding *and* leave an email address on disk. Hex survives the fold unchanged.
|
|
1464
|
+
* - It is compared for equality only, so nothing is lost by making it opaque.
|
|
1465
|
+
*
|
|
1466
|
+
* `v1` is part of the serialized tuple, not decoration: a future change to the
|
|
1467
|
+
* composition has to produce different keys, and a version tag is what lets that
|
|
1468
|
+
* change be told apart from a corrupted row instead of silently merging two
|
|
1469
|
+
* accounts that the new rule would separate.
|
|
1470
|
+
*
|
|
1471
|
+
* Returns `undefined` when the credential states nothing at all about who it is.
|
|
1472
|
+
* The pool then mints its own `acc_<random>` and ADDS the row, which is the honest
|
|
1473
|
+
* answer: a credential nothing identifies cannot be recognized again, and merging
|
|
1474
|
+
* it with an unrelated one would be worse than a duplicate.
|
|
1475
|
+
*/
|
|
1476
|
+
function codexAccountIdentity(credentials) {
|
|
1477
|
+
const workspace = nonEmpty$3(credentials.accountId);
|
|
1478
|
+
const user = codexUserId(credentials) ?? nonEmpty$3(credentials.email);
|
|
1479
|
+
if (workspace === void 0 && user === void 0) return void 0;
|
|
1480
|
+
const serialized = JSON.stringify([
|
|
1481
|
+
"v1",
|
|
1482
|
+
workspace ?? "",
|
|
1483
|
+
user ?? ""
|
|
1484
|
+
]);
|
|
1485
|
+
return "v1-" + createHash("sha256").update(serialized).digest("hex").slice(0, 32);
|
|
1486
|
+
}
|
|
1487
|
+
function stringClaim(value) {
|
|
1488
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1489
|
+
}
|
|
1490
|
+
function nonEmpty$3(value) {
|
|
1491
|
+
const trimmed = value?.trim();
|
|
1492
|
+
return trimmed === void 0 || trimmed === "" ? void 0 : trimmed;
|
|
1493
|
+
}
|
|
1494
|
+
//#endregion
|
|
1348
1495
|
//#region src/host/wire-auth.ts
|
|
1349
1496
|
/**
|
|
1350
1497
|
* Turn-state header the Codex backend hands back on a responses response.
|
|
@@ -1441,18 +1588,36 @@ const CATALOG_TTL_MS = 15 * 6e4;
|
|
|
1441
1588
|
* scope a rehydrated listing from one account would answer another's picker.
|
|
1442
1589
|
*/
|
|
1443
1590
|
const SNAPSHOT_SCOPE = "chatgpt-codex";
|
|
1444
|
-
|
|
1591
|
+
/**
|
|
1592
|
+
* How many accounts' listings are held at once.
|
|
1593
|
+
*
|
|
1594
|
+
* One slot was enough while a workspace id was mistaken for an account, because
|
|
1595
|
+
* every member of a workspace then shared one. It is not enough now: a pool
|
|
1596
|
+
* rotates between accounts, and a single slot would have each rotation evict the
|
|
1597
|
+
* other account's listing and re-fetch it, which turns the 15-minute TTL into
|
|
1598
|
+
* "no cache at all" for exactly the multi-account setup this line supports. The
|
|
1599
|
+
* bound is the pool's own order of magnitude, so a real composition always fits
|
|
1600
|
+
* and a pathological one only loses the coldest entry.
|
|
1601
|
+
*/
|
|
1602
|
+
const CATALOG_CACHE_ACCOUNTS = 8;
|
|
1603
|
+
/** Listings held per login identity; see {@link CATALOG_CACHE_ACCOUNTS}. */
|
|
1604
|
+
const catalogCache$2 = /* @__PURE__ */ new Map();
|
|
1605
|
+
/** The entry {@link getCachedCatalog} answers with: the one a load last wrote. */
|
|
1606
|
+
let latestKey = null;
|
|
1445
1607
|
const catalogInFlight$6 = /* @__PURE__ */ new Map();
|
|
1446
|
-
|
|
1608
|
+
/** Snapshots already read back from disk, per identity. */
|
|
1609
|
+
const snapshotRehydrated = /* @__PURE__ */ new Map();
|
|
1447
1610
|
/**
|
|
1448
1611
|
* The listing currently held, or undefined before the first load.
|
|
1449
1612
|
*
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
1613
|
+
* "Currently held" is the entry a load wrote last: a picker asks what the
|
|
1614
|
+
* account serving it can call, and that is the account whose listing was fetched
|
|
1615
|
+
* most recently. Callers that need a catalog with no credential read the shipped
|
|
1616
|
+
* table directly; returning it here under a different entry type would force
|
|
1617
|
+
* every one of them to convert it.
|
|
1453
1618
|
*/
|
|
1454
1619
|
function getCachedCatalog$3() {
|
|
1455
|
-
return catalogCache$2?.models;
|
|
1620
|
+
return latestKey === null ? void 0 : catalogCache$2.get(latestKey)?.models;
|
|
1456
1621
|
}
|
|
1457
1622
|
/**
|
|
1458
1623
|
* Read the models this subscription can call.
|
|
@@ -1470,8 +1635,11 @@ function getCachedCatalog$3() {
|
|
|
1470
1635
|
function loadCodexCatalog(credentials, options = {}) {
|
|
1471
1636
|
const key = accountKeyFor$1(credentials);
|
|
1472
1637
|
if (options.force !== true) {
|
|
1473
|
-
const cached = catalogCache$2;
|
|
1474
|
-
if (cached !==
|
|
1638
|
+
const cached = catalogCache$2.get(key);
|
|
1639
|
+
if (cached !== void 0 && Date.now() - cached.at < CATALOG_TTL_MS) {
|
|
1640
|
+
latestKey = key;
|
|
1641
|
+
return Promise.resolve(cached.models);
|
|
1642
|
+
}
|
|
1475
1643
|
}
|
|
1476
1644
|
const pending = catalogInFlight$6.get(key);
|
|
1477
1645
|
if (pending !== void 0) return pending;
|
|
@@ -1488,10 +1656,17 @@ function loadCodexCatalog(credentials, options = {}) {
|
|
|
1488
1656
|
async function performCatalogLoad$1(credentials, options) {
|
|
1489
1657
|
const key = accountKeyFor$1(credentials);
|
|
1490
1658
|
const fetchFn = options.fetchFn ?? fetch;
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1659
|
+
let rehydrated = snapshotRehydrated.get(key);
|
|
1660
|
+
if (rehydrated === void 0) {
|
|
1661
|
+
rehydrated = rehydrateSnapshot(key);
|
|
1662
|
+
snapshotRehydrated.set(key, rehydrated);
|
|
1663
|
+
}
|
|
1664
|
+
await rehydrated;
|
|
1665
|
+
const cached = catalogCache$2.get(key);
|
|
1666
|
+
if (cached !== void 0 && Date.now() - cached.at < CATALOG_TTL_MS) {
|
|
1667
|
+
latestKey = key;
|
|
1668
|
+
return cached.models;
|
|
1669
|
+
}
|
|
1495
1670
|
let models;
|
|
1496
1671
|
let live = false;
|
|
1497
1672
|
try {
|
|
@@ -1510,18 +1685,34 @@ async function performCatalogLoad$1(credentials, options) {
|
|
|
1510
1685
|
live = true;
|
|
1511
1686
|
writeCatalogSnapshot(catalogSnapshotName(SNAPSHOT_SCOPE, key), [...models], Date.now());
|
|
1512
1687
|
} catch {
|
|
1513
|
-
models =
|
|
1688
|
+
models = catalogCache$2.get(key)?.models ?? [];
|
|
1514
1689
|
live = false;
|
|
1515
1690
|
}
|
|
1516
|
-
|
|
1691
|
+
remember(key, {
|
|
1517
1692
|
at: Date.now(),
|
|
1518
1693
|
models,
|
|
1519
1694
|
key,
|
|
1520
1695
|
live
|
|
1521
|
-
};
|
|
1696
|
+
});
|
|
1522
1697
|
return models;
|
|
1523
1698
|
}
|
|
1524
1699
|
/**
|
|
1700
|
+
* File one listing under its identity, keeping the map bounded.
|
|
1701
|
+
*
|
|
1702
|
+
* Eviction is oldest-fetch-first, which is the entry a rotation is least likely to
|
|
1703
|
+
* ask for next; the bound exists so a composition that signs in far more accounts
|
|
1704
|
+
* than the pool holds cannot grow this map without end.
|
|
1705
|
+
*/
|
|
1706
|
+
function remember(key, entry) {
|
|
1707
|
+
catalogCache$2.set(key, entry);
|
|
1708
|
+
latestKey = key;
|
|
1709
|
+
while (catalogCache$2.size > CATALOG_CACHE_ACCOUNTS) {
|
|
1710
|
+
const oldest = [...catalogCache$2.entries()].filter(([candidate]) => candidate !== key).sort((left, right) => left[1].at - right[1].at)[0];
|
|
1711
|
+
if (oldest === void 0) break;
|
|
1712
|
+
catalogCache$2.delete(oldest[0]);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1525
1716
|
* Seed the in-memory cache from the last persisted listing.
|
|
1526
1717
|
*
|
|
1527
1718
|
* Best effort by construction: a missing or malformed snapshot leaves the cache
|
|
@@ -1530,13 +1721,13 @@ async function performCatalogLoad$1(credentials, options) {
|
|
|
1530
1721
|
async function rehydrateSnapshot(key) {
|
|
1531
1722
|
const snapshot = await readCatalogSnapshot(catalogSnapshotName(SNAPSHOT_SCOPE, key), (value) => Array.isArray(value) ? value.flatMap((item) => parseListingEntry$1(item)) : void 0).catch(() => void 0);
|
|
1532
1723
|
if (snapshot === void 0 || snapshot.models.length === 0) return;
|
|
1533
|
-
if (catalogCache$2
|
|
1534
|
-
|
|
1724
|
+
if (catalogCache$2.has(key)) return;
|
|
1725
|
+
remember(key, {
|
|
1535
1726
|
at: snapshot.fetchedAt,
|
|
1536
1727
|
models: snapshot.models,
|
|
1537
1728
|
key,
|
|
1538
1729
|
live: true
|
|
1539
|
-
};
|
|
1730
|
+
});
|
|
1540
1731
|
}
|
|
1541
1732
|
/**
|
|
1542
1733
|
* Read the listing payload.
|
|
@@ -1605,12 +1796,19 @@ function parseEfforts(value) {
|
|
|
1605
1796
|
/**
|
|
1606
1797
|
* Cache identity for one credential.
|
|
1607
1798
|
*
|
|
1608
|
-
* The
|
|
1609
|
-
*
|
|
1610
|
-
*
|
|
1799
|
+
* The listing is served per PLAN, and a plan belongs to the local login — the
|
|
1800
|
+
* workspace id alone is not an identity here either: two members of one workspace
|
|
1801
|
+
* can hold different plans, so filing both listings under the workspace would let
|
|
1802
|
+
* one member's picker answer with models the other member's plan does not serve.
|
|
1803
|
+
* The key is therefore the same composite identity the pool dedupes on, and its
|
|
1804
|
+
* digest form is what keeps the persisted snapshot's file name both distinct after
|
|
1805
|
+
* `catalogSnapshotName` folds it and free of an email address.
|
|
1806
|
+
*
|
|
1807
|
+
* `'default'` answers a credential that states nothing about itself, which is the
|
|
1808
|
+
* single pre-sign-in case this loader ever sees.
|
|
1611
1809
|
*/
|
|
1612
1810
|
function accountKeyFor$1(credentials) {
|
|
1613
|
-
return credentials
|
|
1811
|
+
return codexAccountIdentity(credentials) ?? "default";
|
|
1614
1812
|
}
|
|
1615
1813
|
function asRecord$20(value) {
|
|
1616
1814
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
@@ -1729,6 +1927,7 @@ function parseStoredCredentials(value) {
|
|
|
1729
1927
|
expiresAt: record.expiresAt,
|
|
1730
1928
|
idToken: optional("idToken"),
|
|
1731
1929
|
accountId: optional("accountId"),
|
|
1930
|
+
userId: optional("userId"),
|
|
1732
1931
|
email: optional("email"),
|
|
1733
1932
|
planType: optional("planType")
|
|
1734
1933
|
};
|
|
@@ -4498,17 +4697,6 @@ function buildAuthorizationUrl(verifier, state) {
|
|
|
4498
4697
|
originator: OAUTH_ORIGINATOR
|
|
4499
4698
|
}).toString()}`;
|
|
4500
4699
|
}
|
|
4501
|
-
function parseJwtClaims(token) {
|
|
4502
|
-
if (token === void 0) return void 0;
|
|
4503
|
-
const parts = token.split(".");
|
|
4504
|
-
if (parts.length !== 3) return void 0;
|
|
4505
|
-
try {
|
|
4506
|
-
const value = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
4507
|
-
return typeof value === "object" && value !== null ? value : void 0;
|
|
4508
|
-
} catch {
|
|
4509
|
-
return;
|
|
4510
|
-
}
|
|
4511
|
-
}
|
|
4512
4700
|
function publicError(error, fallback = "internal") {
|
|
4513
4701
|
if (error instanceof OAuthServiceError) return {
|
|
4514
4702
|
code: error.code,
|
|
@@ -4535,28 +4723,25 @@ function credentialsFromTokenResponse(response, now, previous) {
|
|
|
4535
4723
|
return {
|
|
4536
4724
|
...base,
|
|
4537
4725
|
accountId: identity.accountId ?? previous?.accountId,
|
|
4726
|
+
userId: identity.userId ?? previous?.userId,
|
|
4538
4727
|
email: identity.email ?? previous?.email,
|
|
4539
4728
|
planType: identity.planType ?? previous?.planType
|
|
4540
4729
|
};
|
|
4541
4730
|
}
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
/** Identity one refresh is keyed by, so a rotation is never redeemed twice. */
|
|
4731
|
+
/**
|
|
4732
|
+
* Identity one refresh is keyed by, so a rotation is never redeemed twice.
|
|
4733
|
+
*
|
|
4734
|
+
* The SAME key the pool dedupes on, and deliberately so: it has to separate two
|
|
4735
|
+
* members of one workspace, because their grants are unrelated and sharing one
|
|
4736
|
+
* in-flight refresh between them would hand one account's rotated token to the
|
|
4737
|
+
* other. A workspace id alone cannot do that — it is what both members have in
|
|
4738
|
+
* common — so this is the composite local login identity, with the rotating
|
|
4739
|
+
* refresh token as the fallback for a credential that states nothing else. That
|
|
4740
|
+
* fallback is the conservative direction: a token that differs always gets its
|
|
4741
|
+
* own flight, and a flight nobody joined simply runs by itself.
|
|
4742
|
+
*/
|
|
4555
4743
|
function refreshKey(credentials) {
|
|
4556
|
-
return credentials
|
|
4557
|
-
}
|
|
4558
|
-
function stringClaim(value) {
|
|
4559
|
-
return typeof value === "string" && value !== "" ? value : void 0;
|
|
4744
|
+
return codexAccountIdentity(credentials) ?? credentials.refreshToken;
|
|
4560
4745
|
}
|
|
4561
4746
|
function maskEmail(email) {
|
|
4562
4747
|
if (email === void 0) return null;
|
|
@@ -4851,8 +5036,6 @@ const CODEX_CLI_CREDENTIAL_FILE = "auth.json";
|
|
|
4851
5036
|
const CODEX_CLI_TOKENS_KEY = "tokens";
|
|
4852
5037
|
/** Where an adopted snapshot came from, for the card and for the pool record. */
|
|
4853
5038
|
const CODEX_CLI_CREDENTIAL_SOURCE = "codex";
|
|
4854
|
-
/** Namespace the id token carries the ChatGPT account claims under. */
|
|
4855
|
-
const CODEX_AUTH_CLAIMS_KEY = "https://api.openai.com/auth";
|
|
4856
5039
|
/**
|
|
4857
5040
|
* What the user is told when an adopted credential has aged out.
|
|
4858
5041
|
*
|
|
@@ -5256,7 +5439,7 @@ var CodexAccountPool = class extends AccountPoolCore {
|
|
|
5256
5439
|
poolFile: codexPoolPath(),
|
|
5257
5440
|
keychainService: "dsh-chatgpt-subscription-pool",
|
|
5258
5441
|
parsePoolData: parseCodexPoolData,
|
|
5259
|
-
dedupeKey: (credentials) => credentials
|
|
5442
|
+
dedupeKey: (credentials) => codexAccountIdentity(credentials),
|
|
5260
5443
|
defaultAlias: (credentials, position) => credentials.email ?? `账号 ${position}`,
|
|
5261
5444
|
createAccount: ({ id, alias, credentials, addedAt, isPrimary }) => {
|
|
5262
5445
|
const adopted = isAdoptedCodexPoolCredential(credentials);
|
|
@@ -5925,6 +6108,33 @@ function normalizeGenerateOptions(options) {
|
|
|
5925
6108
|
}
|
|
5926
6109
|
//#endregion
|
|
5927
6110
|
//#region src/host/codex-images.ts
|
|
6111
|
+
/**
|
|
6112
|
+
* Output canvases the Codex image endpoint accepts in `size`.
|
|
6113
|
+
*
|
|
6114
|
+
* These are the official client's own three canvases plus the default `auto`,
|
|
6115
|
+
* which lets the service pick. A size outside this set is not negotiated: the
|
|
6116
|
+
* backend answers it with a 400, so the tool refuses it before the request.
|
|
6117
|
+
*/
|
|
6118
|
+
const CODEX_IMAGE_SIZES = [
|
|
6119
|
+
"auto",
|
|
6120
|
+
"1024x1024",
|
|
6121
|
+
"1536x1024",
|
|
6122
|
+
"1024x1536"
|
|
6123
|
+
];
|
|
6124
|
+
/** Render effort the endpoint accepts in `quality`; `auto` is the service's own default. */
|
|
6125
|
+
const CODEX_IMAGE_QUALITIES = [
|
|
6126
|
+
"auto",
|
|
6127
|
+
"low",
|
|
6128
|
+
"medium",
|
|
6129
|
+
"high"
|
|
6130
|
+
];
|
|
6131
|
+
/** Frame budget of an animated GIF the endpoint accepts. */
|
|
6132
|
+
const GIF_MIN_FRAMES = 2;
|
|
6133
|
+
const GIF_MAX_FRAMES = 16;
|
|
6134
|
+
const GIF_MIN_FRAME_MS = 20;
|
|
6135
|
+
const GIF_MAX_FRAME_MS = 2e3;
|
|
6136
|
+
/** How much of an upstream error body is quoted back to the caller. */
|
|
6137
|
+
const MAX_UPSTREAM_EXCERPT_CHARS = 300;
|
|
5928
6138
|
const PNG_SIGNATURE = [
|
|
5929
6139
|
137,
|
|
5930
6140
|
80,
|
|
@@ -5935,16 +6145,78 @@ const PNG_SIGNATURE = [
|
|
|
5935
6145
|
26,
|
|
5936
6146
|
10
|
|
5937
6147
|
];
|
|
6148
|
+
/**
|
|
6149
|
+
* Magic-byte checks per media type.
|
|
6150
|
+
*
|
|
6151
|
+
* An extension is a claim about a file's name; the bytes are the claim the file
|
|
6152
|
+
* itself makes. Both are checked, because sending a file whose declared type the
|
|
6153
|
+
* service cannot decode returns a plain upstream error rather than an image.
|
|
6154
|
+
*/
|
|
6155
|
+
const SIGNATURES$1 = {
|
|
6156
|
+
"image/png": (bytes) => bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71,
|
|
6157
|
+
"image/jpeg": (bytes) => bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255,
|
|
6158
|
+
"image/webp": (bytes) => bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80,
|
|
6159
|
+
"image/gif": (bytes) => bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70
|
|
6160
|
+
};
|
|
6161
|
+
/**
|
|
6162
|
+
* Extensions a reference image may carry.
|
|
6163
|
+
*
|
|
6164
|
+
* Deliberately narrower than {@link SIGNATURES}: an edit accepts stills only.
|
|
6165
|
+
* Animated GIF input is not part of the edit wire contract, so it is refused
|
|
6166
|
+
* here instead of being sent to an endpoint that does not take it.
|
|
6167
|
+
*/
|
|
6168
|
+
const REFERENCE_EXTENSIONS = {
|
|
6169
|
+
".png": "image/png",
|
|
6170
|
+
".jpg": "image/jpeg",
|
|
6171
|
+
".jpeg": "image/jpeg",
|
|
6172
|
+
".webp": "image/webp"
|
|
6173
|
+
};
|
|
5938
6174
|
function createCodexImageTool(oauth, attachments, options = {}) {
|
|
5939
6175
|
const fetchFn = options.fetchFn ?? fetch;
|
|
5940
6176
|
return defineTool({
|
|
5941
6177
|
name: CODEX_IMAGE_TOOL_NAME,
|
|
5942
|
-
description: "Generate a PNG image
|
|
5943
|
-
parameters: {
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
6178
|
+
description: "Generate or edit a PNG or GIF image with the signed-in ChatGPT subscription-backed Codex image endpoint. Describe the subject, style, composition and any text to render in the prompt; pass referenceImagePaths to edit local PNG/JPEG/WebP images, and gif to animate the result. Scheduling: call this tool yourself for a single generation, an edit, or a retry - never create a subagent just to generate one image. Delegate only when the creative task needs several candidate images or is large enough to be a multi-step task of its own.",
|
|
6179
|
+
parameters: {
|
|
6180
|
+
prompt: {
|
|
6181
|
+
type: "string",
|
|
6182
|
+
required: true,
|
|
6183
|
+
description: "A detailed image generation prompt."
|
|
6184
|
+
},
|
|
6185
|
+
size: {
|
|
6186
|
+
type: "string",
|
|
6187
|
+
enum: CODEX_IMAGE_SIZES,
|
|
6188
|
+
description: "Output canvas. Omit or pass auto to let the service choose. 1024x1024 is square, 1536x1024 landscape and 1024x1536 portrait."
|
|
6189
|
+
},
|
|
6190
|
+
quality: {
|
|
6191
|
+
type: "string",
|
|
6192
|
+
enum: CODEX_IMAGE_QUALITIES,
|
|
6193
|
+
description: "Render effort. Omit or pass auto for the service default; low is fastest and high is most detailed."
|
|
6194
|
+
},
|
|
6195
|
+
transparentBackground: {
|
|
6196
|
+
type: "boolean",
|
|
6197
|
+
description: "true for a transparent background (PNG or GIF alpha), false for an explicitly opaque one. Omit it to leave the background decision to the service."
|
|
6198
|
+
},
|
|
6199
|
+
referenceImagePaths: {
|
|
6200
|
+
type: "array",
|
|
6201
|
+
items: { type: "string" },
|
|
6202
|
+
description: "Up to 5 local PNG, JPEG or WebP files to edit. Absolute paths recommended. The request becomes an edit against those images; nothing else on disk is read."
|
|
6203
|
+
},
|
|
6204
|
+
gif: {
|
|
6205
|
+
type: "object",
|
|
6206
|
+
additionalProperties: false,
|
|
6207
|
+
description: "Ask for an animated GIF. An empty object is allowed and lets the service choose the frames.",
|
|
6208
|
+
properties: {
|
|
6209
|
+
frameCount: {
|
|
6210
|
+
type: "integer",
|
|
6211
|
+
description: "Number of frames, 2 to 16."
|
|
6212
|
+
},
|
|
6213
|
+
frameDurationMs: {
|
|
6214
|
+
type: "integer",
|
|
6215
|
+
description: "Milliseconds each frame is shown, 20 to 2000."
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
},
|
|
5948
6220
|
output: {
|
|
5949
6221
|
schema: {
|
|
5950
6222
|
type: "object",
|
|
@@ -5969,7 +6241,7 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
5969
6241
|
},
|
|
5970
6242
|
mediaType: {
|
|
5971
6243
|
type: "string",
|
|
5972
|
-
enum: ["image/png"],
|
|
6244
|
+
enum: ["image/png", "image/gif"],
|
|
5973
6245
|
required: true
|
|
5974
6246
|
},
|
|
5975
6247
|
bytes: {
|
|
@@ -6016,34 +6288,52 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
6016
6288
|
async execute(args, exec) {
|
|
6017
6289
|
const prompt = args.prompt.trim();
|
|
6018
6290
|
if (prompt === "") throw new HarnessError("Image prompt cannot be empty.", "CODEX_IMAGE_INVALID_PROMPT");
|
|
6019
|
-
|
|
6291
|
+
const gif = readGifOptions(args.gif);
|
|
6292
|
+
const references = await readReferenceImages(args.referenceImagePaths ?? [], attachments);
|
|
6293
|
+
assertOutputTypeSupported(attachments, gif === void 0 ? "image/png" : "image/gif");
|
|
6294
|
+
const body = buildImageBody({
|
|
6295
|
+
prompt,
|
|
6296
|
+
size: args.size ?? "auto",
|
|
6297
|
+
quality: args.quality ?? "auto",
|
|
6298
|
+
...args.transparentBackground !== void 0 ? { transparentBackground: args.transparentBackground } : {},
|
|
6299
|
+
...gif !== void 0 ? { gif } : {},
|
|
6300
|
+
references
|
|
6301
|
+
});
|
|
6302
|
+
const url = references.length > 0 ? CODEX_IMAGE_EDIT_URL : CODEX_IMAGE_GENERATION_URL;
|
|
6303
|
+
const turnId = String(exec.callId);
|
|
6020
6304
|
let credentials = await imageCredentials(oauth);
|
|
6021
|
-
let response = await requestImage$1(fetchFn, credentials,
|
|
6305
|
+
let response = await requestImage$1(fetchFn, credentials, url, body, turnId, exec.signal);
|
|
6022
6306
|
if (response.status === 401) {
|
|
6023
6307
|
await response.body?.cancel().catch(() => void 0);
|
|
6024
6308
|
credentials = await imageCredentials(oauth, true);
|
|
6025
|
-
response = await requestImage$1(fetchFn, credentials,
|
|
6309
|
+
response = await requestImage$1(fetchFn, credentials, url, body, turnId, exec.signal);
|
|
6026
6310
|
}
|
|
6027
6311
|
if (response.status === 429) {
|
|
6028
|
-
await response.
|
|
6029
|
-
throw new HarnessError(
|
|
6312
|
+
const detail = upstreamExcerpt(await response.text().catch(() => ""));
|
|
6313
|
+
throw new HarnessError(`Codex image generation was rate limited (429).${detail === "" ? "" : ` ${detail}`}`, "CODEX_IMAGE_RATE_LIMITED");
|
|
6314
|
+
}
|
|
6315
|
+
if (response.status === 400) {
|
|
6316
|
+
const detail = upstreamExcerpt(await response.text().catch(() => ""));
|
|
6317
|
+
throw new HarnessError(`Codex rejected the image request (400). The parameters this call stated were: ${statedParameterSummary(args, gif, references.length)}. One of them is not accepted as given, and no field was dropped to retry.` + (detail === "" ? "" : ` Upstream said: ${detail}`), "CODEX_IMAGE_REQUEST_REJECTED");
|
|
6030
6318
|
}
|
|
6031
6319
|
if (!response.ok) {
|
|
6032
|
-
await response.
|
|
6033
|
-
throw new HarnessError(`Codex image generation failed (${response.status})
|
|
6320
|
+
const detail = upstreamExcerpt(await response.text().catch(() => ""));
|
|
6321
|
+
throw new HarnessError(`Codex image generation failed (${response.status}).${detail === "" ? "" : ` ${detail}`}`, "CODEX_IMAGE_FAILED");
|
|
6034
6322
|
}
|
|
6035
|
-
const
|
|
6323
|
+
const decoded = decodeImageBytes(readBase64Image(await response.json()), maxGeneratedImageBytes(attachments));
|
|
6324
|
+
if (gif !== void 0 && decoded.mediaType !== "image/gif") throw new HarnessError(`Codex was asked for an animated GIF but returned ${decoded.mediaType}. Nothing was saved: the answer is not the animation that was requested. Omit the gif parameter to accept a still image.`, "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6325
|
+
if (!attachments.imageLimits.mediaTypes.includes(decoded.mediaType)) throw new HarnessError(`Codex returned ${decoded.mediaType} but this DSH environment does not accept that attachment type.`, "CODEX_IMAGE_ATTACHMENT_UNSUPPORTED");
|
|
6036
6326
|
const image = await attachments.saveImage({
|
|
6037
|
-
data:
|
|
6038
|
-
mediaType:
|
|
6039
|
-
name:
|
|
6327
|
+
data: decoded.data,
|
|
6328
|
+
mediaType: decoded.mediaType,
|
|
6329
|
+
name: `codex-generated-image.${decoded.mediaType === "image/gif" ? "gif" : "png"}`
|
|
6040
6330
|
});
|
|
6041
6331
|
const output = {
|
|
6042
6332
|
prompt,
|
|
6043
6333
|
model: CODEX_IMAGE_MODEL,
|
|
6044
6334
|
image: {
|
|
6045
6335
|
attachmentId: image.attachmentId,
|
|
6046
|
-
mediaType:
|
|
6336
|
+
mediaType: decoded.mediaType,
|
|
6047
6337
|
bytes: image.bytes,
|
|
6048
6338
|
width: image.width,
|
|
6049
6339
|
height: image.height,
|
|
@@ -6065,6 +6355,124 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
6065
6355
|
}
|
|
6066
6356
|
});
|
|
6067
6357
|
}
|
|
6358
|
+
/**
|
|
6359
|
+
* Validate the frame options of a GIF request.
|
|
6360
|
+
*
|
|
6361
|
+
* An empty object is legal: it asks for an animation and leaves both numbers to
|
|
6362
|
+
* the service. A stated number is checked here rather than by the parameter
|
|
6363
|
+
* schema because the schema DSL carries no numeric bounds, and because
|
|
6364
|
+
* `execute` is reachable directly.
|
|
6365
|
+
*/
|
|
6366
|
+
function readGifOptions(gif) {
|
|
6367
|
+
if (gif === void 0) return void 0;
|
|
6368
|
+
const result = {};
|
|
6369
|
+
const frameCount = gif.frameCount;
|
|
6370
|
+
if (frameCount !== void 0) {
|
|
6371
|
+
if (!Number.isInteger(frameCount) || frameCount < GIF_MIN_FRAMES || frameCount > GIF_MAX_FRAMES) throw new HarnessError(`gif.frameCount must be a whole number from ${GIF_MIN_FRAMES} to ${GIF_MAX_FRAMES} (got ${String(frameCount)}).`, "CODEX_IMAGE_GIF_INVALID");
|
|
6372
|
+
result.frameCount = frameCount;
|
|
6373
|
+
}
|
|
6374
|
+
const frameDurationMs = gif.frameDurationMs;
|
|
6375
|
+
if (frameDurationMs !== void 0) {
|
|
6376
|
+
if (!Number.isInteger(frameDurationMs) || frameDurationMs < GIF_MIN_FRAME_MS || frameDurationMs > GIF_MAX_FRAME_MS) throw new HarnessError(`gif.frameDurationMs must be a whole number of milliseconds from ${GIF_MIN_FRAME_MS} to ${GIF_MAX_FRAME_MS} (got ${String(frameDurationMs)}).`, "CODEX_IMAGE_GIF_INVALID");
|
|
6377
|
+
result.frameDurationMs = frameDurationMs;
|
|
6378
|
+
}
|
|
6379
|
+
return result;
|
|
6380
|
+
}
|
|
6381
|
+
/** Refuse an output type this deployment cannot store, before anything is requested. */
|
|
6382
|
+
function assertOutputTypeSupported(attachments, outputType) {
|
|
6383
|
+
if (attachments.imageLimits.mediaTypes.includes(outputType)) return;
|
|
6384
|
+
throw new HarnessError(outputType === "image/gif" ? "GIF output was requested, but this DSH environment does not accept GIF attachments, so nothing was requested. Omit the gif parameter to generate a PNG instead." : "PNG image attachments are not enabled in this DSH environment.", "CODEX_IMAGE_ATTACHMENT_UNSUPPORTED");
|
|
6385
|
+
}
|
|
6386
|
+
/**
|
|
6387
|
+
* Build the wire body of one request.
|
|
6388
|
+
*
|
|
6389
|
+
* Field order and the defaults are deliberate: a call with no new options
|
|
6390
|
+
* produces exactly `prompt/background auto/model/quality auto/size auto`, which
|
|
6391
|
+
* is the body this tool has always sent. Every new field appears only when the
|
|
6392
|
+
* caller stated it, so an old call site keeps its old bytes on the wire.
|
|
6393
|
+
*/
|
|
6394
|
+
function buildImageBody(request) {
|
|
6395
|
+
const body = {
|
|
6396
|
+
prompt: request.prompt,
|
|
6397
|
+
background: request.transparentBackground === true ? "transparent" : request.transparentBackground === false ? "opaque" : "auto",
|
|
6398
|
+
model: CODEX_IMAGE_MODEL,
|
|
6399
|
+
quality: request.quality,
|
|
6400
|
+
size: request.size
|
|
6401
|
+
};
|
|
6402
|
+
if (request.gif !== void 0) {
|
|
6403
|
+
body.output_format = "gif";
|
|
6404
|
+
if (request.gif.frameCount !== void 0) body.frame_count = request.gif.frameCount;
|
|
6405
|
+
if (request.gif.frameDurationMs !== void 0) body.frame_duration_ms = request.gif.frameDurationMs;
|
|
6406
|
+
}
|
|
6407
|
+
if (request.references.length > 0) body.images = request.references.map((image) => ({ image_url: `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}` }));
|
|
6408
|
+
return body;
|
|
6409
|
+
}
|
|
6410
|
+
/**
|
|
6411
|
+
* The optional parameters one request actually stated, in words.
|
|
6412
|
+
*
|
|
6413
|
+
* A 400 says only that the request was invalid, which is useless on a tool with
|
|
6414
|
+
* seven parameters: the caller has to be told WHICH ones it sent, or it has no
|
|
6415
|
+
* way to know what to change. Only stated options are named - a default that the
|
|
6416
|
+
* tool has always sent cannot be the field to fix - and each GIF sub-field is
|
|
6417
|
+
* named separately, because `gif: {}` is a legal call that stated neither number.
|
|
6418
|
+
*/
|
|
6419
|
+
function statedParameterSummary(args, gif, referenceCount) {
|
|
6420
|
+
const stated = [];
|
|
6421
|
+
if (args.size !== void 0) stated.push(`size=${args.size}`);
|
|
6422
|
+
if (args.quality !== void 0) stated.push(`quality=${args.quality}`);
|
|
6423
|
+
if (args.transparentBackground !== void 0) stated.push(`transparentBackground=${String(args.transparentBackground)}`);
|
|
6424
|
+
if (referenceCount > 0) stated.push(`referenceImagePaths (${referenceCount} image${referenceCount === 1 ? "" : "s"})`);
|
|
6425
|
+
if (gif !== void 0) {
|
|
6426
|
+
const frames = [];
|
|
6427
|
+
if (gif.frameCount !== void 0) frames.push(`frameCount=${String(gif.frameCount)}`);
|
|
6428
|
+
if (gif.frameDurationMs !== void 0) frames.push(`frameDurationMs=${String(gif.frameDurationMs)}`);
|
|
6429
|
+
stated.push(frames.length === 0 ? "gif (frame numbers left to the service)" : `gif (${frames.join(", ")})`);
|
|
6430
|
+
}
|
|
6431
|
+
return stated.length === 0 ? "no optional parameter was stated" : stated.join(", ");
|
|
6432
|
+
}
|
|
6433
|
+
/**
|
|
6434
|
+
* Read and validate the reference images one edit request names.
|
|
6435
|
+
*
|
|
6436
|
+
* Every named file is checked as a local still image: the path must not be a
|
|
6437
|
+
* URL, the extension must be PNG/JPEG/WebP, the file must be a regular non-empty
|
|
6438
|
+
* file inside the attachment byte limits, and its leading bytes must match the
|
|
6439
|
+
* declared type. Nothing else on disk is read, and the returned bytes are the
|
|
6440
|
+
* ONLY image data that reaches the request.
|
|
6441
|
+
*/
|
|
6442
|
+
async function readReferenceImages(files, attachments) {
|
|
6443
|
+
if (files.length === 0) return [];
|
|
6444
|
+
const limit = Math.min(5, attachments.imageLimits.maxImagesPerMessage);
|
|
6445
|
+
if (files.length > limit) throw new HarnessError(`At most ${limit} reference images are accepted; ${files.length} were given.`, "CODEX_IMAGE_REFERENCE_LIMIT");
|
|
6446
|
+
const perImageBytes = attachments.imageLimits.maxImageBytes;
|
|
6447
|
+
const totalBytes = attachments.imageLimits.maxMessageImageBytes;
|
|
6448
|
+
const images = [];
|
|
6449
|
+
let sentBytes = 0;
|
|
6450
|
+
for (const [index, raw] of files.entries()) {
|
|
6451
|
+
const label = `referenceImagePaths[${index}]`;
|
|
6452
|
+
const value = raw.trim();
|
|
6453
|
+
if (value === "") throw new HarnessError(`${label} is empty; every reference image must name a local file.`, "CODEX_IMAGE_REFERENCE_INVALID_PATH");
|
|
6454
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value) || /^data:/i.test(value)) throw new HarnessError(`${label} is a URL (${value}). Reference images must be local PNG, JPEG or WebP files; nothing is fetched.`, "CODEX_IMAGE_REFERENCE_INVALID_PATH");
|
|
6455
|
+
const extension = extname(value).toLowerCase();
|
|
6456
|
+
const mediaType = REFERENCE_EXTENSIONS[extension];
|
|
6457
|
+
if (mediaType === void 0) throw new HarnessError(extension === ".gif" ? `${label} (${value}) is a GIF. Reference images must be still PNG, JPEG or WebP files; animated input is not part of the edit request.` : `${label} (${value}) is not a PNG, JPEG or WebP file (extension ${extension === "" ? "(none)" : `"${extension}"`}).`, "CODEX_IMAGE_REFERENCE_UNSUPPORTED");
|
|
6458
|
+
if (!attachments.imageLimits.mediaTypes.includes(mediaType)) throw new HarnessError(`${label} is ${mediaType}, which this DSH environment does not accept as an attachment.`, "CODEX_IMAGE_ATTACHMENT_UNSUPPORTED");
|
|
6459
|
+
const absolute = resolve(value);
|
|
6460
|
+
const stats = await stat(absolute).catch(() => void 0);
|
|
6461
|
+
if (stats === void 0) throw new HarnessError(`${label} (${value}) does not exist or cannot be read.`, "CODEX_IMAGE_REFERENCE_NOT_FOUND");
|
|
6462
|
+
if (!stats.isFile()) throw new HarnessError(`${label} (${value}) is not a regular file; pass a path to one PNG, JPEG or WebP file.`, "CODEX_IMAGE_REFERENCE_NOT_A_FILE");
|
|
6463
|
+
if (stats.size === 0) throw new HarnessError(`${label} (${value}) is empty.`, "CODEX_IMAGE_REFERENCE_EMPTY");
|
|
6464
|
+
if (stats.size > perImageBytes) throw new HarnessError(`${label} (${value}) is ${stats.size} bytes, above the ${perImageBytes} byte limit for one attachment.`, "CODEX_IMAGE_REFERENCE_TOO_LARGE");
|
|
6465
|
+
const data = new Uint8Array(await readFile(absolute));
|
|
6466
|
+
if (!SIGNATURES$1[mediaType](data)) throw new HarnessError(`${label} (${value}) does not start like a ${mediaType} file, so its bytes contradict its extension.`, "CODEX_IMAGE_REFERENCE_INVALID_CONTENT");
|
|
6467
|
+
sentBytes += data.byteLength;
|
|
6468
|
+
if (sentBytes > totalBytes) throw new HarnessError(`The reference images total ${sentBytes} bytes, above the ${totalBytes} byte limit for one message.`, "CODEX_IMAGE_REFERENCE_TOO_LARGE");
|
|
6469
|
+
images.push({
|
|
6470
|
+
mediaType,
|
|
6471
|
+
data
|
|
6472
|
+
});
|
|
6473
|
+
}
|
|
6474
|
+
return images;
|
|
6475
|
+
}
|
|
6068
6476
|
async function imageCredentials(oauth, force = false) {
|
|
6069
6477
|
try {
|
|
6070
6478
|
return await oauth.credentials(force, { purpose: "tool" });
|
|
@@ -6072,8 +6480,8 @@ async function imageCredentials(oauth, force = false) {
|
|
|
6072
6480
|
throw new HarnessError(`ChatGPT subscription credentials are required for Codex image generation.${error instanceof Error && error.message !== "" ? ` (${error.message})` : ""}`, "CODEX_IMAGE_CREDENTIAL_MISSING", { cause: error });
|
|
6073
6481
|
}
|
|
6074
6482
|
}
|
|
6075
|
-
function requestImage$1(fetchFn, credentials,
|
|
6076
|
-
return fetchFn(
|
|
6483
|
+
function requestImage$1(fetchFn, credentials, url, body, turnId, signal) {
|
|
6484
|
+
return fetchFn(url, {
|
|
6077
6485
|
method: "POST",
|
|
6078
6486
|
headers: {
|
|
6079
6487
|
...codexHeaders(credentials),
|
|
@@ -6081,16 +6489,19 @@ function requestImage$1(fetchFn, credentials, prompt, turnId, signal) {
|
|
|
6081
6489
|
"content-type": "application/json",
|
|
6082
6490
|
"x-codex-image-turn-id": turnId
|
|
6083
6491
|
},
|
|
6084
|
-
body: JSON.stringify(
|
|
6085
|
-
prompt,
|
|
6086
|
-
background: "auto",
|
|
6087
|
-
model: CODEX_IMAGE_MODEL,
|
|
6088
|
-
quality: "auto",
|
|
6089
|
-
size: "auto"
|
|
6090
|
-
}),
|
|
6492
|
+
body: JSON.stringify(body),
|
|
6091
6493
|
signal
|
|
6092
6494
|
});
|
|
6093
6495
|
}
|
|
6496
|
+
/**
|
|
6497
|
+
* The upstream error body, short enough to read in a message and stripped of
|
|
6498
|
+
* anything that must never be echoed: bearer credentials, base64 image payloads
|
|
6499
|
+
* and any long base64-shaped run.
|
|
6500
|
+
*/
|
|
6501
|
+
function upstreamExcerpt(text) {
|
|
6502
|
+
const collapsed = text.replace(/data:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]*/gi, "data:<redacted>").replace(/Bearer\s+[A-Za-z0-9._~+/=-]{6,}/gi, "Bearer <redacted>").replace(/[A-Za-z0-9+/]{200,}={0,2}/g, "<redacted>").replace(/\s+/g, " ").trim();
|
|
6503
|
+
return collapsed.length > MAX_UPSTREAM_EXCERPT_CHARS ? `${collapsed.slice(0, MAX_UPSTREAM_EXCERPT_CHARS)}…` : collapsed;
|
|
6504
|
+
}
|
|
6094
6505
|
function readBase64Image(value) {
|
|
6095
6506
|
const root = record$7(value);
|
|
6096
6507
|
const data = Array.isArray(root?.data) ? root.data[0] : null;
|
|
@@ -6098,12 +6509,27 @@ function readBase64Image(value) {
|
|
|
6098
6509
|
if (image === null) throw new HarnessError("Codex image response did not include image data.", "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6099
6510
|
return image;
|
|
6100
6511
|
}
|
|
6512
|
+
/**
|
|
6513
|
+
* Decode one returned image and identify it by its own magic bytes.
|
|
6514
|
+
*
|
|
6515
|
+
* The endpoint answers a requested GIF with GIF bytes in the same `b64_json`
|
|
6516
|
+
* field it uses for PNG, and states nothing about which one it produced, so the
|
|
6517
|
+
* bytes are the only reliable source for the media type – and storing a GIF
|
|
6518
|
+
* under `image/png` would fail at save time or, worse, mislabel the file.
|
|
6519
|
+
*/
|
|
6101
6520
|
function decodeImageBytes(base64, maxBytes) {
|
|
6102
6521
|
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(base64) || base64.length % 4 !== 0) throw new HarnessError("Codex image response was not valid base64.", "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6103
6522
|
const buffer = Buffer.from(base64, "base64");
|
|
6104
6523
|
if (buffer.length > maxBytes) throw new HarnessError("Generated image exceeds the configured attachment size limit.", "CODEX_IMAGE_TOO_LARGE");
|
|
6105
|
-
|
|
6106
|
-
|
|
6524
|
+
if (SIGNATURES$1["image/gif"](buffer)) return {
|
|
6525
|
+
mediaType: "image/gif",
|
|
6526
|
+
data: buffer
|
|
6527
|
+
};
|
|
6528
|
+
for (const [index, byte] of PNG_SIGNATURE.entries()) if (buffer[index] !== byte) throw new HarnessError("Codex image response was neither PNG nor GIF image data.", "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6529
|
+
return {
|
|
6530
|
+
mediaType: "image/png",
|
|
6531
|
+
data: buffer
|
|
6532
|
+
};
|
|
6107
6533
|
}
|
|
6108
6534
|
function maxGeneratedImageBytes(attachments) {
|
|
6109
6535
|
return Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
|
|
@@ -10277,6 +10703,9 @@ var AntigravityAdapter = class extends LlmAdapter {
|
|
|
10277
10703
|
/**
|
|
10278
10704
|
* Aspect ratios Gemini image models accept in `imageConfig.aspectRatio`.
|
|
10279
10705
|
* Omitted, the model picks one itself, which is what the request then does.
|
|
10706
|
+
*
|
|
10707
|
+
* The last four are the extreme strips: a wider or taller frame than the square
|
|
10708
|
+
* family covers, useful for banners, headers and phone wallpapers.
|
|
10280
10709
|
*/
|
|
10281
10710
|
const ANTIGRAVITY_IMAGE_ASPECT_RATIOS = [
|
|
10282
10711
|
"1:1",
|
|
@@ -10288,7 +10717,21 @@ const ANTIGRAVITY_IMAGE_ASPECT_RATIOS = [
|
|
|
10288
10717
|
"5:4",
|
|
10289
10718
|
"9:16",
|
|
10290
10719
|
"16:9",
|
|
10291
|
-
"21:9"
|
|
10720
|
+
"21:9",
|
|
10721
|
+
"1:4",
|
|
10722
|
+
"4:1",
|
|
10723
|
+
"1:8",
|
|
10724
|
+
"8:1"
|
|
10725
|
+
];
|
|
10726
|
+
/**
|
|
10727
|
+
* Image sizes Gemini image models accept in `imageConfig.imageSize`.
|
|
10728
|
+
* Omitted, the model picks one itself, which is what the request then does.
|
|
10729
|
+
*/
|
|
10730
|
+
const ANTIGRAVITY_IMAGE_SIZES = [
|
|
10731
|
+
"0.5K",
|
|
10732
|
+
"1K",
|
|
10733
|
+
"2K",
|
|
10734
|
+
"4K"
|
|
10292
10735
|
];
|
|
10293
10736
|
/** Images one call keeps; the model normally returns one. */
|
|
10294
10737
|
const MAX_IMAGES_PER_CALL = 4;
|
|
@@ -10302,7 +10745,11 @@ const MAX_IMAGES_PER_CALL = 4;
|
|
|
10302
10745
|
* Antigravity clients send for image runtimes; it has not been captured from
|
|
10303
10746
|
* the official client.
|
|
10304
10747
|
*/
|
|
10305
|
-
function buildImageRequest(prompt, projectId, aspectRatio) {
|
|
10748
|
+
function buildImageRequest(prompt, projectId, aspectRatio, imageSize) {
|
|
10749
|
+
const imageConfig = {
|
|
10750
|
+
...aspectRatio ? { aspectRatio } : {},
|
|
10751
|
+
...imageSize ? { imageSize } : {}
|
|
10752
|
+
};
|
|
10306
10753
|
return {
|
|
10307
10754
|
project: projectId,
|
|
10308
10755
|
model: ANTIGRAVITY_IMAGE_MODEL,
|
|
@@ -10313,7 +10760,7 @@ function buildImageRequest(prompt, projectId, aspectRatio) {
|
|
|
10313
10760
|
}],
|
|
10314
10761
|
generationConfig: {
|
|
10315
10762
|
responseModalities: ["TEXT", "IMAGE"],
|
|
10316
|
-
...
|
|
10763
|
+
...Object.keys(imageConfig).length > 0 ? { imageConfig } : {}
|
|
10317
10764
|
}
|
|
10318
10765
|
},
|
|
10319
10766
|
requestType: "image_gen",
|
|
@@ -10425,7 +10872,7 @@ function createAntigravityImageTool(accountPool, store, attachments, options = {
|
|
|
10425
10872
|
const fetchFn = options.fetchFn ?? fetch;
|
|
10426
10873
|
return defineTool({
|
|
10427
10874
|
name: ANTIGRAVITY_IMAGE_TOOL_NAME,
|
|
10428
|
-
description: "Generate an image with Gemini image generation through the signed-in Antigravity account. Describe the subject, style, composition and any text to render in the prompt.",
|
|
10875
|
+
description: "Generate an image with Gemini image generation through the signed-in Antigravity account. Describe the subject, style, composition and any text to render in the prompt. Scheduling: a single generation, a change to an image already generated, and a retry of a failed attempt are direct calls from the agent that needs the image — do not create a subagent just to generate one image. Delegate only a task that genuinely needs several alternative directions or complex creative planning, and pass the prompt that comes back to this tool.",
|
|
10429
10876
|
parameters: {
|
|
10430
10877
|
prompt: {
|
|
10431
10878
|
type: "string",
|
|
@@ -10435,7 +10882,12 @@ function createAntigravityImageTool(accountPool, store, attachments, options = {
|
|
|
10435
10882
|
aspectRatio: {
|
|
10436
10883
|
type: "string",
|
|
10437
10884
|
enum: ANTIGRAVITY_IMAGE_ASPECT_RATIOS,
|
|
10438
|
-
description: "Optional aspect ratio. Omit it to let the model choose."
|
|
10885
|
+
description: "Optional aspect ratio, one of the listed values. Omit it to let the model choose. If the upstream rejects the value, re-issue the call with another value from the list rather than the same one."
|
|
10886
|
+
},
|
|
10887
|
+
imageSize: {
|
|
10888
|
+
type: "string",
|
|
10889
|
+
enum: ANTIGRAVITY_IMAGE_SIZES,
|
|
10890
|
+
description: "Optional output size, one of 0.5K, 1K, 2K or 4K. Omit it to let the model choose. It is sent only when given, so it never overrides an aspect ratio the caller did not set."
|
|
10439
10891
|
}
|
|
10440
10892
|
},
|
|
10441
10893
|
output: {
|
|
@@ -10519,7 +10971,7 @@ function createAntigravityImageTool(accountPool, store, attachments, options = {
|
|
|
10519
10971
|
const prompt = args.prompt.trim();
|
|
10520
10972
|
if (prompt === "") throw new HarnessError("Image prompt cannot be empty.", "ANTIGRAVITY_IMAGE_INVALID_PROMPT");
|
|
10521
10973
|
if (options.isEnabled?.() === false) throw new HarnessError("The Antigravity line is switched off; enable it in Settings → Antigravity to generate images.", "ANTIGRAVITY_IMAGE_DISABLED");
|
|
10522
|
-
const response = await requestImage(accountPool, store, fetchFn, prompt, args.aspectRatio, exec.signal);
|
|
10974
|
+
const response = await requestImage(accountPool, store, fetchFn, prompt, args.aspectRatio, args.imageSize, exec.signal);
|
|
10523
10975
|
if (response.images.length === 0) throw new HarnessError(noImageMessage(response), "ANTIGRAVITY_IMAGE_EMPTY");
|
|
10524
10976
|
const keep = Math.min(MAX_IMAGES_PER_CALL, attachments.imageLimits.maxImagesPerMessage, response.images.length);
|
|
10525
10977
|
const saved = [];
|
|
@@ -10578,12 +11030,37 @@ async function acquireAccount(accountPool, store, tried, fetchFn) {
|
|
|
10578
11030
|
}
|
|
10579
11031
|
}
|
|
10580
11032
|
}
|
|
10581
|
-
|
|
11033
|
+
/**
|
|
11034
|
+
* Whether a status means the upstream refused the SHAPE of the request rather
|
|
11035
|
+
* than the account, the allowance or the transient state of the service.
|
|
11036
|
+
*/
|
|
11037
|
+
const PARAMETER_REJECTION_STATUSES = /* @__PURE__ */ new Set([400, 422]);
|
|
11038
|
+
/**
|
|
11039
|
+
* What a failed image request reports: the status, a bounded excerpt of what
|
|
11040
|
+
* the upstream said, and — when the refusal was about the request — which
|
|
11041
|
+
* optional fields were sent.
|
|
11042
|
+
*
|
|
11043
|
+
* A parameter rejection is never retried without the field it names. Dropping
|
|
11044
|
+
* `aspectRatio` or `imageSize` would answer with an image that silently has a
|
|
11045
|
+
* shape nobody asked for, so the caller is told which fields to change and
|
|
11046
|
+
* makes that call itself.
|
|
11047
|
+
*/
|
|
11048
|
+
function imageFailureMessage(status, errorText, sent) {
|
|
11049
|
+
const parts = [`Antigravity image generation failed (${status ?? "no response"}).`];
|
|
11050
|
+
if (status !== void 0 && PARAMETER_REJECTION_STATUSES.has(status)) {
|
|
11051
|
+
const fields = [sent.aspectRatio === void 0 ? void 0 : `aspectRatio "${sent.aspectRatio}"`, sent.imageSize === void 0 ? void 0 : `imageSize "${sent.imageSize}"`].filter((field) => field !== void 0);
|
|
11052
|
+
parts.push(fields.length > 0 ? `The request itself was refused, and these optional fields were what it carried: ${fields.join(", ")}. It is not retried without them: re-issue the call with a different candidate value, or omit the field, because the upstream refused this request shape.` : "The request itself was refused while no aspectRatio or imageSize was set, so a different value for either cannot fix it; change the prompt or check the account before retrying.");
|
|
11053
|
+
}
|
|
11054
|
+
const excerpt = upstreamBodyExcerpt(errorText);
|
|
11055
|
+
if (excerpt !== "") parts.push(`Upstream said: ${excerpt}`);
|
|
11056
|
+
return parts.join(" ");
|
|
11057
|
+
}
|
|
11058
|
+
async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, imageSize, signal) {
|
|
10582
11059
|
const tried = /* @__PURE__ */ new Set();
|
|
10583
11060
|
while (true) {
|
|
10584
11061
|
const { account, token, projectId } = await acquireAccount(accountPool, store, tried, fetchFn);
|
|
10585
11062
|
tried.add(account.id);
|
|
10586
|
-
const body = JSON.stringify(buildImageRequest(prompt, projectId || "antigravity-default", aspectRatio));
|
|
11063
|
+
const body = JSON.stringify(buildImageRequest(prompt, projectId || "antigravity-default", aspectRatio, imageSize));
|
|
10587
11064
|
let response;
|
|
10588
11065
|
let firstResponse;
|
|
10589
11066
|
for (const endpoint of endpointCandidates()) try {
|
|
@@ -10615,7 +11092,10 @@ async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, si
|
|
|
10615
11092
|
}
|
|
10616
11093
|
throw new HarnessError(`Antigravity image generation was rate limited (429) without a stated reset time, so it is handled as a transient rate limit rather than a spent quota. ${upstreamBodyExcerpt(errorText)}`.trim(), "ANTIGRAVITY_IMAGE_RATE_LIMITED");
|
|
10617
11094
|
}
|
|
10618
|
-
throw new HarnessError(
|
|
11095
|
+
throw new HarnessError(imageFailureMessage(status, errorText, {
|
|
11096
|
+
aspectRatio,
|
|
11097
|
+
imageSize
|
|
11098
|
+
}), "ANTIGRAVITY_IMAGE_FAILED");
|
|
10619
11099
|
}
|
|
10620
11100
|
}
|
|
10621
11101
|
//#endregion
|
|
@@ -15123,8 +15603,17 @@ function readableLimitName(value) {
|
|
|
15123
15603
|
function isWindow(value) {
|
|
15124
15604
|
return value !== null;
|
|
15125
15605
|
}
|
|
15606
|
+
/**
|
|
15607
|
+
* The identity a reading is filed under when no pool row names one.
|
|
15608
|
+
*
|
|
15609
|
+
* The composite local login identity, for the same reason the pool dedupes on it:
|
|
15610
|
+
* with several accounts in one composition, a workspace id alone would make two
|
|
15611
|
+
* members of that workspace share a slot — each showing the other's numbers. The
|
|
15612
|
+
* trailing fallbacks are for a credential that states nothing about itself, which
|
|
15613
|
+
* is the single-account case, where there is nobody to mix up.
|
|
15614
|
+
*/
|
|
15126
15615
|
function identityKey(credentials) {
|
|
15127
|
-
return credentials
|
|
15616
|
+
return codexAccountIdentity(credentials) ?? credentials.planType ?? "signed-in";
|
|
15128
15617
|
}
|
|
15129
15618
|
//#endregion
|
|
15130
15619
|
//#region src/host/routes.ts
|
|
@@ -27788,6 +28277,25 @@ const TOOL_SOURCE = /(?:^|[-_/])(?:read|view|cat|open|edit|write|patch|apply_pat
|
|
|
27788
28277
|
/** 独立开关覆盖的检索工具;只有明确导航任务才允许进入新投影。 */
|
|
27789
28278
|
const TOOL_RETRIEVAL = /^(?:read|web_fetch|grep|glob)$/;
|
|
27790
28279
|
/**
|
|
28280
|
+
* PTC 家族的**程序传输**工具名(一个集合,两个名字)。
|
|
28281
|
+
*
|
|
28282
|
+
* 「模型写一段程序、工具调用发生在程序内部」是同一种传输,只是不同预设给主工具起
|
|
28283
|
+
* 了不同名字:PTC 预设叫 `run_code`,Code Mode 预设叫 `code_run`。凡是按工具名判断
|
|
28284
|
+
* 「这条结果来自程序传输」的地方都必须认这**整个集合**——名字只写一个,预设一换,
|
|
28285
|
+
* 大结果的 `ref://` 预览与精确补读就会静默失效。
|
|
28286
|
+
*/
|
|
28287
|
+
const CACHE_WARMUP_PTC_TRANSPORT_TOOLS = /* @__PURE__ */ new Set(["run_code", "code_run"]);
|
|
28288
|
+
/**
|
|
28289
|
+
* 这个名字是否属于 PTC 家族的程序传输工具。
|
|
28290
|
+
*
|
|
28291
|
+
* 只做**精确**匹配:`code_runner` 之类的近似名不算,宁可少投影也不能误伤。
|
|
28292
|
+
* @param name - 工具名。
|
|
28293
|
+
* @returns 属于 {@link CACHE_WARMUP_PTC_TRANSPORT_TOOLS} 时为 true。
|
|
28294
|
+
*/
|
|
28295
|
+
function isPtcTransportName(name) {
|
|
28296
|
+
return CACHE_WARMUP_PTC_TRANSPORT_TOOLS.has(name);
|
|
28297
|
+
}
|
|
28298
|
+
/**
|
|
27791
28299
|
* 多节/多读输出进入新投影的最小长度门槛(字符)。
|
|
27792
28300
|
*
|
|
27793
28301
|
* 短于这个长度、又没有 spill 的普通源码与契约读一律逐字保留:
|
|
@@ -29083,7 +29591,7 @@ function planProjection(text, toolName, navigation, retrievalOnly, spilled = fal
|
|
|
29083
29591
|
kind: "verbatim",
|
|
29084
29592
|
strategy: "verbatim-contract"
|
|
29085
29593
|
};
|
|
29086
|
-
if (toolName
|
|
29594
|
+
if (isPtcTransportName(toolName) && navigation === true) {
|
|
29087
29595
|
const outline = planRetrievalOutline(text, "read");
|
|
29088
29596
|
if (outline !== void 0) return outline;
|
|
29089
29597
|
if (retrievalOnly === true) return {
|
|
@@ -92344,6 +92852,20 @@ const PRESET_REGISTRY_PACKAGE = "@deepseek-ai/dsh-agent-preset";
|
|
|
92344
92852
|
const WORKING_DIRECTORY_PACKAGE = "@deepseek-ai/dsh-working-directory";
|
|
92345
92853
|
/** The standalone PTC executor introduced when tool presentation split into executor plugins. */
|
|
92346
92854
|
const PTC_EXECUTOR_PACKAGE = "@deepseek-ai/dsh-tool-executor-ptc";
|
|
92855
|
+
/**
|
|
92856
|
+
* The native executor this package delegates Code Mode execution to.
|
|
92857
|
+
*
|
|
92858
|
+
* A program sub-call must run through the harness's own policy, approval, and
|
|
92859
|
+
* post-execute pipeline; re-implementing that pipeline is how a plugin silently
|
|
92860
|
+
* drops policy. A profile plugin cannot resolve the host's kernel packages, so
|
|
92861
|
+
* this package carries the executor as a real dependency and drives the host
|
|
92862
|
+
* registry with it.
|
|
92863
|
+
*/
|
|
92864
|
+
const NATIVE_EXECUTOR_PACKAGE = "@deepseek-ai/dsh-tool-executor-native";
|
|
92865
|
+
/** This package's Code Mode row: the preset's own tools stay native, the rest is program-only. */
|
|
92866
|
+
const CODEMODE_PACKAGE = "@eddyskywalker/dsh-chatgpt-subscription/codemode";
|
|
92867
|
+
/** The deprecated presentation row generations without a native executor keep. */
|
|
92868
|
+
const AGENT_TOOL_PRESENTATION_PACKAGE = "@deepseek-ai/dsh-agent-tool-presentation";
|
|
92347
92869
|
/** The id the `dispatch` preset is selectable under. */
|
|
92348
92870
|
const DISPATCH_PRESET_ID = "dispatch";
|
|
92349
92871
|
/**
|
|
@@ -92359,7 +92881,7 @@ const DISPATCH_PRESET_ID = "dispatch";
|
|
|
92359
92881
|
const DISPATCH_PRESET = {
|
|
92360
92882
|
id: DISPATCH_PRESET_ID,
|
|
92361
92883
|
name: "调度模式",
|
|
92362
|
-
description: "基于
|
|
92884
|
+
description: "基于 Code Mode 的调度 Agent:先分诊任务复杂度,L2 任务在规划前先做澄清访谈,再由主代理规划、派发子代理(显式选择子代理模型)、审查与验收。",
|
|
92363
92885
|
order: 5,
|
|
92364
92886
|
plugins: [
|
|
92365
92887
|
{
|
|
@@ -92406,6 +92928,7 @@ L0/L1: execute directly (or 1 subagent for L1). L2: R-G, R9, and R6 are mandator
|
|
|
92406
92928
|
- Lead-Core: tightly coupled cross-module/contract changes or sequential chains where splitting duplicates reads or causes hand_back loops. -> Implement directly in continuous steps (track in todo_write, no self-briefs) and run R6 verification.
|
|
92407
92929
|
- Hybrid: unsettled shared contracts/seams plus orthogonal downstream work. -> Implement & freeze core seams yourself first, then delegate orthogonal adapters/tests/noisy exploration per R2/R5 (or subagent_fork for warm-context isolation).
|
|
92408
92930
|
- Fan-out: >=3 orthogonal subtasks with disjoint files, settled interfaces, and clear parallel/routing gain. -> Coordinate (plan, R2/R5 dispatch, R6 review/acceptance, docs, non-delegable or disjoint parallel modules); never edit an active child's Boundary.
|
|
92931
|
+
- [Media generation] Generate/revise/retry images directly; delegate complex creative work, never a bare call.
|
|
92409
92932
|
|
|
92410
92933
|
## R2 · Model Routing (Check Availability First, Then Decide How to Dispatch)
|
|
92411
92934
|
Check whether list_subagent_models exists (in PTC mode: tools.list_subagent_models). The tool row and provider/model/reasoning_effort parameters are mounted statically, so they are present even in a session that authorized no route at all. Read that fact instead of inferring it from the tool's output:
|
|
@@ -92650,9 +93173,8 @@ Fallback is a task-scoped, non-session-wide exception for delegated briefs: it c
|
|
|
92650
93173
|
}
|
|
92651
93174
|
},
|
|
92652
93175
|
{
|
|
92653
|
-
id: "
|
|
92654
|
-
name:
|
|
92655
|
-
config: { mode: "ptc" }
|
|
93176
|
+
id: "code-mode",
|
|
93177
|
+
name: CODEMODE_PACKAGE
|
|
92656
93178
|
},
|
|
92657
93179
|
{
|
|
92658
93180
|
id: "present",
|
|
@@ -92676,8 +93198,16 @@ Fallback is a task-scoped, non-session-wide exception for delegated briefs: it c
|
|
|
92676
93198
|
* the export is absent; older harnesses keep it because their main entry does not
|
|
92677
93199
|
* register the tool.
|
|
92678
93200
|
*
|
|
92679
|
-
*
|
|
92680
|
-
*
|
|
93201
|
+
* Code Mode needs the native tool executor (it is the pipeline the program's
|
|
93202
|
+
* sub-calls are delegated to), so that package's presence decides the
|
|
93203
|
+
* presentation rows:
|
|
93204
|
+
*
|
|
93205
|
+
* - resolvable: the `code-mode` row mounts as-is and no PTC executor row exists
|
|
93206
|
+
* beside it (one executor per scope);
|
|
93207
|
+
* - absent: the row falls back to the deprecated agent-tool-presentation row and,
|
|
93208
|
+
* when the standalone PTC executor exists, to that executor instead — the
|
|
93209
|
+
* behaviour of every generation before Code Mode.
|
|
93210
|
+
*
|
|
92681
93211
|
* @param resolves - whether a bare specifier resolves in this installation.
|
|
92682
93212
|
* @returns the preset definition for the running harness generation.
|
|
92683
93213
|
*/
|
|
@@ -92685,7 +93215,8 @@ function resolveDispatchPreset(resolves = resolvesFromHere) {
|
|
|
92685
93215
|
const hasWorkingDirectory = resolves(WORKING_DIRECTORY_PACKAGE);
|
|
92686
93216
|
const hasLegacyListAgents = resolves("@deepseek-ai/dsh-tool-subagent-control/list-agents");
|
|
92687
93217
|
const hasPtcExecutor = resolves(PTC_EXECUTOR_PACKAGE);
|
|
92688
|
-
|
|
93218
|
+
const hasNativeExecutor = resolves(NATIVE_EXECUTOR_PACKAGE);
|
|
93219
|
+
if (!hasWorkingDirectory && hasLegacyListAgents && !hasPtcExecutor && hasNativeExecutor) return DISPATCH_PRESET;
|
|
92689
93220
|
return {
|
|
92690
93221
|
...DISPATCH_PRESET,
|
|
92691
93222
|
plugins: DISPATCH_PRESET.plugins.map((row) => {
|
|
@@ -92693,14 +93224,14 @@ function resolveDispatchPreset(resolves = resolvesFromHere) {
|
|
|
92693
93224
|
...row,
|
|
92694
93225
|
config: row.config.filter((nested) => nested.id !== "tool-subagent-list-agents")
|
|
92695
93226
|
};
|
|
92696
|
-
if (row.id === "
|
|
92697
|
-
|
|
92698
|
-
|
|
92699
|
-
|
|
92700
|
-
|
|
92701
|
-
|
|
92702
|
-
}
|
|
92703
|
-
}
|
|
93227
|
+
if (row.id === "code-mode" && !hasNativeExecutor) return hasPtcExecutor ? {
|
|
93228
|
+
id: "tool-executor-ptc",
|
|
93229
|
+
name: PTC_EXECUTOR_PACKAGE
|
|
93230
|
+
} : {
|
|
93231
|
+
id: "tool-presentation",
|
|
93232
|
+
name: AGENT_TOOL_PRESENTATION_PACKAGE,
|
|
93233
|
+
config: { mode: "ptc" }
|
|
93234
|
+
};
|
|
92704
93235
|
if (row.id !== "persona" || !hasWorkingDirectory || typeof row.config !== "object" || row.config === null) return row;
|
|
92705
93236
|
const { suffix: _omitted, ...rest } = row.config;
|
|
92706
93237
|
return {
|
|
@@ -94577,12 +95108,13 @@ const CACHE_WARMUP_PROVIDER = "kimi-code";
|
|
|
94577
95108
|
/**
|
|
94578
95109
|
* 允许进入结果投影的工具集合。
|
|
94579
95110
|
*
|
|
94580
|
-
* 除了 PTC 的 `
|
|
94581
|
-
*
|
|
94582
|
-
*
|
|
95111
|
+
* 除了 PTC 家族的程序传输(`run_code` 与 Code Mode 的 `code_run`),宿主的独立
|
|
95112
|
+
* Shell 与后台任务输出工具(`win_bash`、`bash`、`pwsh`、`job_output`)同样经常
|
|
95113
|
+
* 吐出整屏测试/构建日志。源码读取/编辑类工具(`read`、`edit`、`grep`、`glob`、
|
|
95114
|
+
* CodeGraph 等)不在此列,保持 100% 原样。
|
|
94583
95115
|
*/
|
|
94584
95116
|
const CACHE_WARMUP_SOURCE_TOOLS = /* @__PURE__ */ new Set([
|
|
94585
|
-
|
|
95117
|
+
...CACHE_WARMUP_PTC_TRANSPORT_TOOLS,
|
|
94586
95118
|
"win_bash",
|
|
94587
95119
|
"bash",
|
|
94588
95120
|
"pwsh",
|
|
@@ -94599,7 +95131,7 @@ const CACHE_WARMUP_SECTION_NAME = "plugin:cache-warmup";
|
|
|
94599
95131
|
const CACHE_WARMUP_GUIDANCE = [
|
|
94600
95132
|
"## Result previews",
|
|
94601
95133
|
"",
|
|
94602
|
-
"On large `run_code` results this line may replace the output with a preview: a short description plus one or more `ref://<session>/<sha256>` handles.",
|
|
95134
|
+
"On large `run_code`/`code_run` results this line may replace the output with a preview: a short description plus one or more `ref://<session>/<sha256>` handles.",
|
|
94603
95135
|
"",
|
|
94604
95136
|
"- A preview is **not** the full result. It states `complete`, `omitted` and `nextOffset` explicitly; treat an incomplete preview as incomplete data, not as the whole output.",
|
|
94605
95137
|
"- To read the original, call `cache_warmup_result` with `action: \"page\"` and that `refId`, then follow `nextOffset` until `complete` is true.",
|
|
@@ -94818,7 +95350,8 @@ function installCacheWarmup(ctx, options) {
|
|
|
94818
95350
|
*
|
|
94819
95351
|
* 决策取舍写在代码里,因为它全是「什么时候**不动**」:
|
|
94820
95352
|
* - 不是 `accept`(`block` 的 feedback 是别人的东西);
|
|
94821
|
-
* -
|
|
95353
|
+
* - 工具名不在源工具集合里(`run_code`/`code_run`/Shell/`job_output`)、线路不是
|
|
95354
|
+
* 本线、会话拿不到;
|
|
94822
95355
|
* - 没有存储、内容为空。
|
|
94823
95356
|
* 上面任何一条成立就原样返回同一个决策对象。
|
|
94824
95357
|
*
|
|
@@ -94836,7 +95369,7 @@ function installCacheWarmup(ctx, options) {
|
|
|
94836
95369
|
const retrievalTool = CACHE_WARMUP_RETRIEVAL_TOOLS.has(exec.name);
|
|
94837
95370
|
if (!sourceTool && !retrievalTool) return decision;
|
|
94838
95371
|
const settings = options.settings();
|
|
94839
|
-
const ptcRetrieval = exec.name
|
|
95372
|
+
const ptcRetrieval = isPtcTransportName(exec.name) && settings.retrievalEnabled === true;
|
|
94840
95373
|
if (sourceTool && !settings.enabled && !ptcRetrieval) return decision;
|
|
94841
95374
|
if (retrievalTool && settings.retrievalEnabled !== true) return decision;
|
|
94842
95375
|
const navigation = retrievalTool || ptcRetrieval ? cacheWarmupNavigationIntentOf(exec.agent) : void 0;
|
|
@@ -95345,14 +95878,14 @@ const WORKBUDDY_STANDARD_EFFORTS = [
|
|
|
95345
95878
|
* Snap a level onto the nearest rung a model's ladder actually exposes.
|
|
95346
95879
|
*
|
|
95347
95880
|
* The gateway names a default from a wider vocabulary than the ladder it
|
|
95348
|
-
* publishes for the same model: an effort-only entry
|
|
95349
|
-
*
|
|
95350
|
-
*
|
|
95351
|
-
*
|
|
95352
|
-
*
|
|
95353
|
-
*
|
|
95354
|
-
* samples, versus 282-659 with the field). Ties resolve upward, which is
|
|
95355
|
-
* mapping the sibling Kimi line documents for `medium`. A ladder that names
|
|
95881
|
+
* publishes for the same model: an effort-only entry — the shape `kimi-k3`
|
|
95882
|
+
* publishes — names a single default while exposing only `low`/`high`/`max`.
|
|
95883
|
+
* Letting an unlisted value through is not cosmetic — a default outside the
|
|
95884
|
+
* ladder is discarded by the level-resolution step, the request then carries no
|
|
95885
|
+
* `reasoning_effort`, and the model returns an EMPTY `reasoning_content`
|
|
95886
|
+
* (measured on `minimax-m3` while that id was still served: 0 characters across
|
|
95887
|
+
* three samples, versus 282-659 with the field). Ties resolve upward, which is
|
|
95888
|
+
* the mapping the sibling Kimi line documents for `medium`. A ladder that names
|
|
95356
95889
|
* no level (a text-only model) yields nothing to converge to.
|
|
95357
95890
|
*/
|
|
95358
95891
|
function convergeWorkBuddyEffort(effort, ladder) {
|
|
@@ -95635,96 +96168,27 @@ function refreshSourceForDomain(domain) {
|
|
|
95635
96168
|
/**
|
|
95636
96169
|
* Models the gateway serves but does not publish in `/v3/config`.
|
|
95637
96170
|
*
|
|
95638
|
-
* The catalog is a published list, not the served one: measured on
|
|
95639
|
-
* international backend, `gpt-6-sol`, `gpt-6-luna` and `gemini-3.8-flash`
|
|
95640
|
-
*
|
|
96171
|
+
* The catalog is a published list, not the served one: measured 2026-10-09 on
|
|
96172
|
+
* the international backend, `gpt-6-sol`, `gpt-6-luna` and `gemini-3.8-flash`
|
|
96173
|
+
* all answered a normal streaming completion with 200 while being absent from
|
|
95641
96174
|
* `/v3/config` entirely, and a reachable gateway *replaces* the shipped table
|
|
95642
|
-
* with that list
|
|
95643
|
-
*
|
|
95644
|
-
*
|
|
95645
|
-
*
|
|
95646
|
-
*
|
|
95647
|
-
*
|
|
95648
|
-
*
|
|
95649
|
-
*
|
|
95650
|
-
*
|
|
95651
|
-
|
|
95652
|
-
|
|
95653
|
-
* - **Context window, output cap and `canDisableThinking` are inherited from the
|
|
95654
|
-
* published sibling in the same family** — `gpt-6-astra` for `gpt-6-sol` and
|
|
95655
|
-
* `gpt-6-luna`, `gemini-3.5-flash` for `gemini-3.8-flash`. They are marked
|
|
95656
|
-
* here rather than measured because the gateway publishes nothing for these
|
|
95657
|
-
* ids and offers no cheap way to read the real figure: it accepts a
|
|
95658
|
-
* `max_tokens` larger than any published window (even for `gpt-6-astra`, whose
|
|
95659
|
-
* own catalog caps at 128000), so the number is neither enforced nor
|
|
95660
|
-
* discoverable from the request side.
|
|
95661
|
-
*
|
|
95662
|
-
* Every field stays overridable per model from the settings card, and an
|
|
95663
|
-
* inherited window errs low rather than high: over-declaring overflows the
|
|
95664
|
-
* gateway mid-session, while under-declaring only compresses earlier.
|
|
95665
|
-
*/
|
|
95666
|
-
const UNPUBLISHED_MODELS = [
|
|
95667
|
-
{
|
|
95668
|
-
id: "gpt-6-sol",
|
|
95669
|
-
name: "GPT-6-Sol",
|
|
95670
|
-
contextWindow: 4e5,
|
|
95671
|
-
maxContextWindow: 1e6,
|
|
95672
|
-
maxTokens: 128e3,
|
|
95673
|
-
regions: ["intl"],
|
|
95674
|
-
supportsImage: true,
|
|
95675
|
-
reasoningEfforts: [
|
|
95676
|
-
"low",
|
|
95677
|
-
"medium",
|
|
95678
|
-
"high",
|
|
95679
|
-
"xhigh",
|
|
95680
|
-
"max"
|
|
95681
|
-
],
|
|
95682
|
-
defaultReasoningEffort: "high",
|
|
95683
|
-
canDisableThinking: true,
|
|
95684
|
-
description: "OpenAI 旗舰模型,擅长复杂推理与长程任务"
|
|
95685
|
-
},
|
|
95686
|
-
{
|
|
95687
|
-
id: "gpt-6-luna",
|
|
95688
|
-
name: "GPT-6-Luna",
|
|
95689
|
-
contextWindow: 4e5,
|
|
95690
|
-
maxContextWindow: 1e6,
|
|
95691
|
-
maxTokens: 128e3,
|
|
95692
|
-
regions: ["intl"],
|
|
95693
|
-
supportsImage: true,
|
|
95694
|
-
reasoningEfforts: [
|
|
95695
|
-
"low",
|
|
95696
|
-
"medium",
|
|
95697
|
-
"high",
|
|
95698
|
-
"xhigh",
|
|
95699
|
-
"max"
|
|
95700
|
-
],
|
|
95701
|
-
defaultReasoningEffort: "high",
|
|
95702
|
-
canDisableThinking: true,
|
|
95703
|
-
description: "OpenAI 轻量模型,响应快速,适合日常任务"
|
|
95704
|
-
},
|
|
95705
|
-
{
|
|
95706
|
-
id: "gemini-3.8-flash",
|
|
95707
|
-
name: "Gemini-3.8-Flash",
|
|
95708
|
-
contextWindow: 1e6,
|
|
95709
|
-
maxContextWindow: 1e6,
|
|
95710
|
-
maxTokens: 65536,
|
|
95711
|
-
regions: ["intl"],
|
|
95712
|
-
supportsImage: true,
|
|
95713
|
-
reasoningEfforts: [
|
|
95714
|
-
"low",
|
|
95715
|
-
"high",
|
|
95716
|
-
"max"
|
|
95717
|
-
],
|
|
95718
|
-
defaultReasoningEffort: "medium",
|
|
95719
|
-
canDisableThinking: false,
|
|
95720
|
-
description: "能力均衡,适合日常使用"
|
|
95721
|
-
}
|
|
95722
|
-
];
|
|
96175
|
+
* with that list — so a row living only in the fallback would drop out of the
|
|
96176
|
+
* picker the moment the catalog loaded.
|
|
96177
|
+
*
|
|
96178
|
+
* The list is empty as of the 2026-11-04 read: all three ids are published now,
|
|
96179
|
+
* with figures measured on the wire, and their rows therefore live in
|
|
96180
|
+
* {@link FALLBACK_MODELS} like any other. The seam stays rather than being
|
|
96181
|
+
* deleted because the gap it covers belongs to the gateway, not to those three
|
|
96182
|
+
* models: the next id it serves without listing belongs here, and
|
|
96183
|
+
* {@link withUnpublishedModels} is what keeps such a row offered.
|
|
96184
|
+
*/
|
|
96185
|
+
const UNPUBLISHED_MODELS = [];
|
|
95723
96186
|
/**
|
|
95724
96187
|
* Offline fallback catalog, transcribed from a live `/v3/config` read.
|
|
95725
96188
|
*
|
|
95726
|
-
*
|
|
95727
|
-
* network failure); a reachable
|
|
96189
|
+
* Last transcribed 2026-11-04 from both backends. Used when the gateway cannot
|
|
96190
|
+
* be reached (first run before sign-in, or a network failure); a reachable
|
|
96191
|
+
* gateway always wins.
|
|
95728
96192
|
*/
|
|
95729
96193
|
const FALLBACK_MODELS$1 = [
|
|
95730
96194
|
{
|
|
@@ -95740,20 +96204,6 @@ const FALLBACK_MODELS$1 = [
|
|
|
95740
96204
|
canDisableThinking: false,
|
|
95741
96205
|
description: "优秀的编码模型,适合日常使用"
|
|
95742
96206
|
},
|
|
95743
|
-
{
|
|
95744
|
-
id: "default",
|
|
95745
|
-
name: "Default",
|
|
95746
|
-
contextWindow: 56e3,
|
|
95747
|
-
maxContextWindow: 56e3,
|
|
95748
|
-
maxTokens: 24e3,
|
|
95749
|
-
regions: ["cn"],
|
|
95750
|
-
credits: { cn: "2.00" },
|
|
95751
|
-
supportsImage: false,
|
|
95752
|
-
reasoningEfforts: [],
|
|
95753
|
-
defaultReasoningEffort: null,
|
|
95754
|
-
canDisableThinking: false,
|
|
95755
|
-
description: ""
|
|
95756
|
-
},
|
|
95757
96207
|
{
|
|
95758
96208
|
id: "fast-model",
|
|
95759
96209
|
name: "Fast",
|
|
@@ -95768,7 +96218,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
95768
96218
|
"high",
|
|
95769
96219
|
"max"
|
|
95770
96220
|
],
|
|
95771
|
-
defaultReasoningEffort: "
|
|
96221
|
+
defaultReasoningEffort: "high",
|
|
95772
96222
|
canDisableThinking: false,
|
|
95773
96223
|
description: "响应快,适合简单任务"
|
|
95774
96224
|
},
|
|
@@ -95786,7 +96236,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
95786
96236
|
"high",
|
|
95787
96237
|
"max"
|
|
95788
96238
|
],
|
|
95789
|
-
defaultReasoningEffort: "
|
|
96239
|
+
defaultReasoningEffort: "high",
|
|
95790
96240
|
canDisableThinking: false,
|
|
95791
96241
|
description: "速度与质量兼顾,日常工作首选"
|
|
95792
96242
|
},
|
|
@@ -95809,20 +96259,65 @@ const FALLBACK_MODELS$1 = [
|
|
|
95809
96259
|
description: "高质量输出,胜任复杂任务"
|
|
95810
96260
|
},
|
|
95811
96261
|
{
|
|
95812
|
-
id: "
|
|
95813
|
-
name: "
|
|
95814
|
-
contextWindow:
|
|
95815
|
-
maxContextWindow:
|
|
95816
|
-
maxTokens:
|
|
96262
|
+
id: "deepseek-v4.1-flash",
|
|
96263
|
+
name: "Deepseek-V4.1-Flash",
|
|
96264
|
+
contextWindow: 3e5,
|
|
96265
|
+
maxContextWindow: 1e6,
|
|
96266
|
+
maxTokens: 128e3,
|
|
96267
|
+
regions: ["intl", "cn"],
|
|
96268
|
+
credits: {
|
|
96269
|
+
cn: "0.11",
|
|
96270
|
+
intl: "0.00"
|
|
96271
|
+
},
|
|
96272
|
+
supportsImage: true,
|
|
96273
|
+
reasoningEfforts: [
|
|
96274
|
+
"low",
|
|
96275
|
+
"high",
|
|
96276
|
+
"max"
|
|
96277
|
+
],
|
|
96278
|
+
defaultReasoningEffort: "high",
|
|
96279
|
+
canDisableThinking: false,
|
|
96280
|
+
description: "DeepSeek 旗舰模型,支持 1M 上下文窗口,原生多模态"
|
|
96281
|
+
},
|
|
96282
|
+
{
|
|
96283
|
+
id: "deepseek-v4.1-flash-sg",
|
|
96284
|
+
name: "Deepseek-V4.1-Flash",
|
|
96285
|
+
contextWindow: 3e5,
|
|
96286
|
+
maxContextWindow: 1e6,
|
|
96287
|
+
maxTokens: 128e3,
|
|
95817
96288
|
regions: ["intl"],
|
|
95818
|
-
credits: { intl: "
|
|
96289
|
+
credits: { intl: "0.03" },
|
|
95819
96290
|
supportsImage: true,
|
|
95820
|
-
reasoningEfforts: [
|
|
95821
|
-
|
|
96291
|
+
reasoningEfforts: [
|
|
96292
|
+
"low",
|
|
96293
|
+
"high",
|
|
96294
|
+
"max"
|
|
96295
|
+
],
|
|
96296
|
+
defaultReasoningEffort: "high",
|
|
95822
96297
|
canDisableThinking: false,
|
|
95823
|
-
description: "
|
|
96298
|
+
description: "DeepSeek 旗舰模型,支持 1M 上下文窗口,原生多模态"
|
|
96299
|
+
},
|
|
96300
|
+
{
|
|
96301
|
+
id: "glm-5.3-flash",
|
|
96302
|
+
name: "GLM-5.3-Flash",
|
|
96303
|
+
contextWindow: 3e5,
|
|
96304
|
+
maxContextWindow: 1e6,
|
|
96305
|
+
maxTokens: 32e3,
|
|
96306
|
+
regions: ["intl", "cn"],
|
|
96307
|
+
credits: {
|
|
96308
|
+
cn: "0.08",
|
|
96309
|
+
intl: "0.08"
|
|
96310
|
+
},
|
|
96311
|
+
supportsImage: true,
|
|
96312
|
+
reasoningEfforts: [
|
|
96313
|
+
"low",
|
|
96314
|
+
"high",
|
|
96315
|
+
"max"
|
|
96316
|
+
],
|
|
96317
|
+
defaultReasoningEffort: "high",
|
|
96318
|
+
canDisableThinking: false,
|
|
96319
|
+
description: "原生多模态模型,擅长视觉理解与专业任务"
|
|
95824
96320
|
},
|
|
95825
|
-
...UNPUBLISHED_MODELS.filter((model) => model.id === "gpt-6-sol" || model.id === "gpt-6-luna"),
|
|
95826
96321
|
{
|
|
95827
96322
|
id: "gpt-6-astra",
|
|
95828
96323
|
name: "GPT-6-Astra",
|
|
@@ -95843,6 +96338,37 @@ const FALLBACK_MODELS$1 = [
|
|
|
95843
96338
|
canDisableThinking: true,
|
|
95844
96339
|
description: "OpenAI 旗舰模型,擅长复杂推理与长程任务"
|
|
95845
96340
|
},
|
|
96341
|
+
{
|
|
96342
|
+
id: "hy4-preview",
|
|
96343
|
+
name: "Hy4 Preview",
|
|
96344
|
+
contextWindow: 2e5,
|
|
96345
|
+
maxContextWindow: 1e6,
|
|
96346
|
+
maxTokens: 64e3,
|
|
96347
|
+
regions: ["intl"],
|
|
96348
|
+
credits: { intl: "0.29" },
|
|
96349
|
+
supportsImage: true,
|
|
96350
|
+
reasoningEfforts: ["high"],
|
|
96351
|
+
defaultReasoningEffort: "high",
|
|
96352
|
+
canDisableThinking: false,
|
|
96353
|
+
description: "混元思考模型,具有增强的推理能力"
|
|
96354
|
+
},
|
|
96355
|
+
{
|
|
96356
|
+
id: "hy3",
|
|
96357
|
+
name: "Hy3",
|
|
96358
|
+
contextWindow: 192e3,
|
|
96359
|
+
maxContextWindow: 192e3,
|
|
96360
|
+
maxTokens: 64e3,
|
|
96361
|
+
regions: ["intl", "cn"],
|
|
96362
|
+
credits: {
|
|
96363
|
+
cn: "0.00",
|
|
96364
|
+
intl: "0.00"
|
|
96365
|
+
},
|
|
96366
|
+
supportsImage: true,
|
|
96367
|
+
reasoningEfforts: ["low", "high"],
|
|
96368
|
+
defaultReasoningEffort: "high",
|
|
96369
|
+
canDisableThinking: false,
|
|
96370
|
+
description: "混元思考模型,具有增强的推理能力"
|
|
96371
|
+
},
|
|
95846
96372
|
{
|
|
95847
96373
|
id: "gpt-5.6-sol",
|
|
95848
96374
|
name: "GPT-5.6-Sol",
|
|
@@ -95949,8 +96475,8 @@ const FALLBACK_MODELS$1 = [
|
|
|
95949
96475
|
maxTokens: 48e3,
|
|
95950
96476
|
regions: ["intl", "cn"],
|
|
95951
96477
|
credits: {
|
|
95952
|
-
cn: "0.
|
|
95953
|
-
intl: "0.
|
|
96478
|
+
cn: "0.69",
|
|
96479
|
+
intl: "0.69"
|
|
95954
96480
|
},
|
|
95955
96481
|
supportsImage: true,
|
|
95956
96482
|
reasoningEfforts: [
|
|
@@ -95959,30 +96485,9 @@ const FALLBACK_MODELS$1 = [
|
|
|
95959
96485
|
"max"
|
|
95960
96486
|
],
|
|
95961
96487
|
defaultReasoningEffort: "high",
|
|
95962
|
-
canDisableThinking:
|
|
96488
|
+
canDisableThinking: false,
|
|
95963
96489
|
description: "能力均衡,适合日常使用"
|
|
95964
96490
|
},
|
|
95965
|
-
{
|
|
95966
|
-
id: "glm-5.3-flash",
|
|
95967
|
-
name: "GLM-5.3-Flash",
|
|
95968
|
-
contextWindow: 3e5,
|
|
95969
|
-
maxContextWindow: 1e6,
|
|
95970
|
-
maxTokens: 32e3,
|
|
95971
|
-
regions: ["intl", "cn"],
|
|
95972
|
-
credits: {
|
|
95973
|
-
cn: "0.06",
|
|
95974
|
-
intl: "0.06"
|
|
95975
|
-
},
|
|
95976
|
-
supportsImage: true,
|
|
95977
|
-
reasoningEfforts: [
|
|
95978
|
-
"low",
|
|
95979
|
-
"high",
|
|
95980
|
-
"max"
|
|
95981
|
-
],
|
|
95982
|
-
defaultReasoningEffort: "high",
|
|
95983
|
-
canDisableThinking: true,
|
|
95984
|
-
description: "原生多模态模型,擅长视觉理解与专业任务"
|
|
95985
|
-
},
|
|
95986
96491
|
{
|
|
95987
96492
|
id: "glm-5.2",
|
|
95988
96493
|
name: "GLM-5.2",
|
|
@@ -95995,101 +96500,63 @@ const FALLBACK_MODELS$1 = [
|
|
|
95995
96500
|
intl: "0.79"
|
|
95996
96501
|
},
|
|
95997
96502
|
supportsImage: true,
|
|
95998
|
-
reasoningEfforts: ["high"
|
|
96503
|
+
reasoningEfforts: ["high"],
|
|
95999
96504
|
defaultReasoningEffort: "high",
|
|
96000
|
-
canDisableThinking:
|
|
96505
|
+
canDisableThinking: false,
|
|
96001
96506
|
description: "1M 上下文,擅长长程任务"
|
|
96002
96507
|
},
|
|
96003
96508
|
{
|
|
96004
|
-
id: "
|
|
96005
|
-
name: "
|
|
96006
|
-
contextWindow:
|
|
96007
|
-
maxContextWindow:
|
|
96008
|
-
maxTokens:
|
|
96009
|
-
regions: ["cn"],
|
|
96010
|
-
credits: {
|
|
96509
|
+
id: "minimax-m3.1-flash",
|
|
96510
|
+
name: "MiniMax-M3.1-Flash",
|
|
96511
|
+
contextWindow: 3e5,
|
|
96512
|
+
maxContextWindow: 1e6,
|
|
96513
|
+
maxTokens: 128e3,
|
|
96514
|
+
regions: ["intl", "cn"],
|
|
96515
|
+
credits: {
|
|
96516
|
+
cn: "0.08",
|
|
96517
|
+
intl: "0.08"
|
|
96518
|
+
},
|
|
96011
96519
|
supportsImage: true,
|
|
96012
96520
|
reasoningEfforts: [
|
|
96013
96521
|
"low",
|
|
96522
|
+
"medium",
|
|
96014
96523
|
"high",
|
|
96524
|
+
"xhigh",
|
|
96015
96525
|
"max"
|
|
96016
96526
|
],
|
|
96017
|
-
defaultReasoningEffort: "
|
|
96527
|
+
defaultReasoningEffort: "max",
|
|
96018
96528
|
canDisableThinking: false,
|
|
96019
|
-
description: ""
|
|
96529
|
+
description: "原生多模态,擅长office、agentic、编程任务,推理速度优异"
|
|
96020
96530
|
},
|
|
96021
96531
|
{
|
|
96022
|
-
id: "
|
|
96023
|
-
name: "
|
|
96024
|
-
contextWindow:
|
|
96025
|
-
maxContextWindow:
|
|
96026
|
-
maxTokens:
|
|
96027
|
-
regions: ["cn"],
|
|
96028
|
-
credits: {
|
|
96532
|
+
id: "kimi-k3.1-pro",
|
|
96533
|
+
name: "Kimi-K3.1-Pro",
|
|
96534
|
+
contextWindow: 3e5,
|
|
96535
|
+
maxContextWindow: 1e6,
|
|
96536
|
+
maxTokens: 32e3,
|
|
96537
|
+
regions: ["intl", "cn"],
|
|
96538
|
+
credits: {
|
|
96539
|
+
cn: "0.97",
|
|
96540
|
+
intl: "0.97"
|
|
96541
|
+
},
|
|
96029
96542
|
supportsImage: true,
|
|
96030
96543
|
reasoningEfforts: [
|
|
96031
96544
|
"low",
|
|
96032
96545
|
"high",
|
|
96033
96546
|
"max"
|
|
96034
96547
|
],
|
|
96035
|
-
defaultReasoningEffort: "
|
|
96036
|
-
canDisableThinking: false,
|
|
96037
|
-
description: ""
|
|
96038
|
-
},
|
|
96039
|
-
{
|
|
96040
|
-
id: "glm-5.0",
|
|
96041
|
-
name: "GLM-5.0",
|
|
96042
|
-
contextWindow: 2e5,
|
|
96043
|
-
maxContextWindow: 2e5,
|
|
96044
|
-
maxTokens: 48e3,
|
|
96045
|
-
regions: ["cn"],
|
|
96046
|
-
credits: { cn: "0.80" },
|
|
96047
|
-
supportsImage: false,
|
|
96048
|
-
reasoningEfforts: [],
|
|
96049
|
-
defaultReasoningEffort: null,
|
|
96050
|
-
canDisableThinking: false,
|
|
96051
|
-
description: ""
|
|
96052
|
-
},
|
|
96053
|
-
{
|
|
96054
|
-
id: "glm-5.0-turbo",
|
|
96055
|
-
name: "GLM-5.0-Turbo",
|
|
96056
|
-
contextWindow: 2e5,
|
|
96057
|
-
maxContextWindow: 2e5,
|
|
96058
|
-
maxTokens: 48e3,
|
|
96059
|
-
regions: ["cn"],
|
|
96060
|
-
credits: { cn: "0.95" },
|
|
96061
|
-
supportsImage: false,
|
|
96062
|
-
reasoningEfforts: [
|
|
96063
|
-
"low",
|
|
96064
|
-
"high",
|
|
96065
|
-
"max"
|
|
96066
|
-
],
|
|
96067
|
-
defaultReasoningEffort: "medium",
|
|
96068
|
-
canDisableThinking: false,
|
|
96069
|
-
description: ""
|
|
96070
|
-
},
|
|
96071
|
-
{
|
|
96072
|
-
id: "glm-4.7",
|
|
96073
|
-
name: "GLM-4.7",
|
|
96074
|
-
contextWindow: 2e5,
|
|
96075
|
-
maxContextWindow: 2e5,
|
|
96076
|
-
maxTokens: 48e3,
|
|
96077
|
-
regions: ["cn"],
|
|
96078
|
-
credits: { cn: "0.23" },
|
|
96079
|
-
supportsImage: false,
|
|
96080
|
-
reasoningEfforts: [],
|
|
96081
|
-
defaultReasoningEffort: null,
|
|
96548
|
+
defaultReasoningEffort: "high",
|
|
96082
96549
|
canDisableThinking: false,
|
|
96083
|
-
description: ""
|
|
96550
|
+
description: "Kimi 全新旗舰模型,3万亿级参数。办公、代码、3D 创作与长程 Agent 能力全面升级。"
|
|
96084
96551
|
},
|
|
96085
96552
|
{
|
|
96086
|
-
id: "
|
|
96087
|
-
name: "
|
|
96088
|
-
contextWindow:
|
|
96089
|
-
maxContextWindow:
|
|
96553
|
+
id: "kimi-k3.1-pro-highspeed",
|
|
96554
|
+
name: "Kimi-K3.1-Pro-Highspeed",
|
|
96555
|
+
contextWindow: 3e5,
|
|
96556
|
+
maxContextWindow: 1e6,
|
|
96090
96557
|
maxTokens: 32e3,
|
|
96091
|
-
regions: ["
|
|
96092
|
-
credits: {
|
|
96558
|
+
regions: ["intl"],
|
|
96559
|
+
credits: { intl: "1.56" },
|
|
96093
96560
|
supportsImage: true,
|
|
96094
96561
|
reasoningEfforts: [
|
|
96095
96562
|
"low",
|
|
@@ -96098,21 +96565,7 @@ const FALLBACK_MODELS$1 = [
|
|
|
96098
96565
|
],
|
|
96099
96566
|
defaultReasoningEffort: "high",
|
|
96100
96567
|
canDisableThinking: false,
|
|
96101
|
-
description: ""
|
|
96102
|
-
},
|
|
96103
|
-
{
|
|
96104
|
-
id: "glm-4.6",
|
|
96105
|
-
name: "GLM-4.6",
|
|
96106
|
-
contextWindow: 168e3,
|
|
96107
|
-
maxContextWindow: 168e3,
|
|
96108
|
-
maxTokens: 32e3,
|
|
96109
|
-
regions: ["cn"],
|
|
96110
|
-
credits: { cn: "0.23" },
|
|
96111
|
-
supportsImage: false,
|
|
96112
|
-
reasoningEfforts: [],
|
|
96113
|
-
defaultReasoningEffort: null,
|
|
96114
|
-
canDisableThinking: false,
|
|
96115
|
-
description: ""
|
|
96568
|
+
description: "K3.1 Pro 高速版本,响应速度提升至 K3.1 Pro 的 2–3 倍,交互更流畅。"
|
|
96116
96569
|
},
|
|
96117
96570
|
{
|
|
96118
96571
|
id: "kimi-k3",
|
|
@@ -96128,166 +96581,121 @@ const FALLBACK_MODELS$1 = [
|
|
|
96128
96581
|
"high",
|
|
96129
96582
|
"max"
|
|
96130
96583
|
],
|
|
96131
|
-
defaultReasoningEffort: "
|
|
96584
|
+
defaultReasoningEffort: "high",
|
|
96132
96585
|
canDisableThinking: false,
|
|
96133
96586
|
description: "擅长处理复杂的长程自主任务,前端开发能力突出,同时在知识工作与科研推理上表现出色。"
|
|
96134
96587
|
},
|
|
96135
96588
|
{
|
|
96136
|
-
id: "
|
|
96137
|
-
name: "
|
|
96138
|
-
contextWindow:
|
|
96139
|
-
maxContextWindow:
|
|
96140
|
-
maxTokens:
|
|
96141
|
-
regions: ["
|
|
96142
|
-
credits: {
|
|
96589
|
+
id: "grok-4.7",
|
|
96590
|
+
name: "Grok-4.7",
|
|
96591
|
+
contextWindow: 2e5,
|
|
96592
|
+
maxContextWindow: 5e5,
|
|
96593
|
+
maxTokens: 128e3,
|
|
96594
|
+
regions: ["intl"],
|
|
96595
|
+
credits: { intl: "1.90" },
|
|
96143
96596
|
supportsImage: true,
|
|
96144
96597
|
reasoningEfforts: [
|
|
96145
96598
|
"low",
|
|
96599
|
+
"medium",
|
|
96146
96600
|
"high",
|
|
96147
|
-
"
|
|
96601
|
+
"xhigh"
|
|
96148
96602
|
],
|
|
96149
|
-
defaultReasoningEffort: "
|
|
96603
|
+
defaultReasoningEffort: "high",
|
|
96150
96604
|
canDisableThinking: false,
|
|
96151
|
-
description: "
|
|
96605
|
+
description: "SpaceXAI 打造的前沿模型,专为编程、智能体任务及知识型工作而设计。"
|
|
96152
96606
|
},
|
|
96153
96607
|
{
|
|
96154
|
-
id: "
|
|
96155
|
-
name: "
|
|
96608
|
+
id: "gemini-3.8-flash",
|
|
96609
|
+
name: "Gemini-3.8-Flash",
|
|
96156
96610
|
contextWindow: 3e5,
|
|
96157
96611
|
maxContextWindow: 1e6,
|
|
96158
|
-
maxTokens:
|
|
96159
|
-
regions: ["intl"
|
|
96160
|
-
credits: {
|
|
96161
|
-
cn: "0.77",
|
|
96162
|
-
intl: "0.77"
|
|
96163
|
-
},
|
|
96164
|
-
supportsImage: true,
|
|
96165
|
-
reasoningEfforts: [
|
|
96166
|
-
"low",
|
|
96167
|
-
"high",
|
|
96168
|
-
"max"
|
|
96169
|
-
],
|
|
96170
|
-
defaultReasoningEffort: "high",
|
|
96171
|
-
canDisableThinking: true,
|
|
96172
|
-
description: "擅长处理复杂的长程自主任务,前端开发能力突出,同时在知识工作与科研推理上表现出色。"
|
|
96173
|
-
},
|
|
96174
|
-
{
|
|
96175
|
-
id: "kimi-k2.7",
|
|
96176
|
-
name: "Kimi-K2.7-Code",
|
|
96177
|
-
contextWindow: 256e3,
|
|
96178
|
-
maxContextWindow: 256e3,
|
|
96179
|
-
maxTokens: 32e3,
|
|
96180
|
-
regions: ["cn"],
|
|
96181
|
-
credits: { cn: "0.57" },
|
|
96182
|
-
supportsImage: true,
|
|
96183
|
-
reasoningEfforts: [
|
|
96184
|
-
"low",
|
|
96185
|
-
"high",
|
|
96186
|
-
"max"
|
|
96187
|
-
],
|
|
96188
|
-
defaultReasoningEffort: "medium",
|
|
96189
|
-
canDisableThinking: false,
|
|
96190
|
-
description: ""
|
|
96191
|
-
},
|
|
96192
|
-
{
|
|
96193
|
-
id: "kimi-k2.6",
|
|
96194
|
-
name: "Kimi-K2.6",
|
|
96195
|
-
contextWindow: 256e3,
|
|
96196
|
-
maxContextWindow: 256e3,
|
|
96197
|
-
maxTokens: 32e3,
|
|
96198
|
-
regions: ["intl", "cn"],
|
|
96199
|
-
credits: {
|
|
96200
|
-
cn: "0.52",
|
|
96201
|
-
intl: "0.52"
|
|
96202
|
-
},
|
|
96612
|
+
maxTokens: 65536,
|
|
96613
|
+
regions: ["intl"],
|
|
96614
|
+
credits: { intl: "0.95" },
|
|
96203
96615
|
supportsImage: true,
|
|
96204
96616
|
reasoningEfforts: [
|
|
96205
96617
|
"low",
|
|
96206
|
-
"
|
|
96207
|
-
"
|
|
96618
|
+
"medium",
|
|
96619
|
+
"high"
|
|
96208
96620
|
],
|
|
96209
96621
|
defaultReasoningEffort: "medium",
|
|
96210
96622
|
canDisableThinking: false,
|
|
96211
|
-
description: "
|
|
96623
|
+
description: "能力均衡,适合日常使用"
|
|
96212
96624
|
},
|
|
96213
96625
|
{
|
|
96214
|
-
id: "
|
|
96215
|
-
name: "
|
|
96216
|
-
contextWindow:
|
|
96217
|
-
maxContextWindow:
|
|
96218
|
-
maxTokens:
|
|
96219
|
-
regions: ["
|
|
96220
|
-
credits: {
|
|
96626
|
+
id: "gpt-6-sol",
|
|
96627
|
+
name: "GPT-6-Sol",
|
|
96628
|
+
contextWindow: 1e6,
|
|
96629
|
+
maxContextWindow: 1e6,
|
|
96630
|
+
maxTokens: 128e3,
|
|
96631
|
+
regions: ["intl"],
|
|
96632
|
+
credits: { intl: "1.33" },
|
|
96221
96633
|
supportsImage: true,
|
|
96222
96634
|
reasoningEfforts: [
|
|
96223
96635
|
"low",
|
|
96636
|
+
"medium",
|
|
96224
96637
|
"high",
|
|
96225
|
-
"
|
|
96226
|
-
],
|
|
96227
|
-
defaultReasoningEffort: "high",
|
|
96228
|
-
canDisableThinking: false,
|
|
96229
|
-
description: ""
|
|
96230
|
-
},
|
|
96231
|
-
{
|
|
96232
|
-
id: "kimi-k2-thinking",
|
|
96233
|
-
name: "Kimi-K2-Thinking",
|
|
96234
|
-
contextWindow: 164e3,
|
|
96235
|
-
maxContextWindow: 164e3,
|
|
96236
|
-
maxTokens: 32e3,
|
|
96237
|
-
regions: ["cn"],
|
|
96238
|
-
credits: { cn: "0.54" },
|
|
96239
|
-
supportsImage: false,
|
|
96240
|
-
reasoningEfforts: [
|
|
96241
|
-
"low",
|
|
96242
|
-
"high",
|
|
96243
|
-
"max"
|
|
96638
|
+
"xhigh"
|
|
96244
96639
|
],
|
|
96245
96640
|
defaultReasoningEffort: "high",
|
|
96246
96641
|
canDisableThinking: false,
|
|
96247
|
-
description: ""
|
|
96642
|
+
description: "专为复杂编码和智能体工作流构建的模型"
|
|
96248
96643
|
},
|
|
96249
96644
|
{
|
|
96250
|
-
id: "
|
|
96251
|
-
name: "
|
|
96252
|
-
contextWindow:
|
|
96645
|
+
id: "gpt-6-luna",
|
|
96646
|
+
name: "GPT-6-Luna",
|
|
96647
|
+
contextWindow: 1e6,
|
|
96253
96648
|
maxContextWindow: 1e6,
|
|
96254
96649
|
maxTokens: 128e3,
|
|
96255
|
-
regions: ["intl"
|
|
96256
|
-
credits: {
|
|
96257
|
-
cn: "0.11",
|
|
96258
|
-
intl: "0.00"
|
|
96259
|
-
},
|
|
96650
|
+
regions: ["intl"],
|
|
96651
|
+
credits: { intl: "0.07" },
|
|
96260
96652
|
supportsImage: true,
|
|
96261
96653
|
reasoningEfforts: [
|
|
96262
96654
|
"low",
|
|
96655
|
+
"medium",
|
|
96263
96656
|
"high",
|
|
96264
|
-
"
|
|
96657
|
+
"xhigh"
|
|
96265
96658
|
],
|
|
96266
96659
|
defaultReasoningEffort: "high",
|
|
96267
96660
|
canDisableThinking: false,
|
|
96268
|
-
description: "
|
|
96661
|
+
description: "专为单一聚焦、高吞吐量任务打造的高效模型"
|
|
96269
96662
|
},
|
|
96270
96663
|
{
|
|
96271
|
-
id: "
|
|
96272
|
-
name: "
|
|
96273
|
-
contextWindow:
|
|
96664
|
+
id: "gpt-6.1-sol",
|
|
96665
|
+
name: "GPT-6.1-Sol",
|
|
96666
|
+
contextWindow: 272e3,
|
|
96274
96667
|
maxContextWindow: 1e6,
|
|
96275
96668
|
maxTokens: 128e3,
|
|
96276
96669
|
regions: ["intl"],
|
|
96277
|
-
credits: { intl: "
|
|
96670
|
+
credits: { intl: "1.33" },
|
|
96278
96671
|
supportsImage: true,
|
|
96279
96672
|
reasoningEfforts: [
|
|
96280
96673
|
"low",
|
|
96674
|
+
"medium",
|
|
96281
96675
|
"high",
|
|
96282
|
-
"
|
|
96676
|
+
"xhigh"
|
|
96283
96677
|
],
|
|
96284
96678
|
defaultReasoningEffort: "high",
|
|
96285
96679
|
canDisableThinking: false,
|
|
96286
|
-
description: "
|
|
96680
|
+
description: "专为复杂编码和智能体工作流构建的模型"
|
|
96681
|
+
},
|
|
96682
|
+
{
|
|
96683
|
+
id: "default",
|
|
96684
|
+
name: "Default",
|
|
96685
|
+
contextWindow: 56e3,
|
|
96686
|
+
maxContextWindow: 56e3,
|
|
96687
|
+
maxTokens: 24e3,
|
|
96688
|
+
regions: ["cn"],
|
|
96689
|
+
credits: { cn: "2.00" },
|
|
96690
|
+
supportsImage: false,
|
|
96691
|
+
reasoningEfforts: [],
|
|
96692
|
+
defaultReasoningEffort: null,
|
|
96693
|
+
canDisableThinking: false,
|
|
96694
|
+
description: ""
|
|
96287
96695
|
},
|
|
96288
96696
|
{
|
|
96289
96697
|
id: "deepseek-v4-pro",
|
|
96290
|
-
name: "
|
|
96698
|
+
name: "DeepSeek-V4-Pro",
|
|
96291
96699
|
contextWindow: 1e6,
|
|
96292
96700
|
maxContextWindow: 1e6,
|
|
96293
96701
|
maxTokens: 128e3,
|
|
@@ -96340,52 +96748,190 @@ const FALLBACK_MODELS$1 = [
|
|
|
96340
96748
|
description: ""
|
|
96341
96749
|
},
|
|
96342
96750
|
{
|
|
96343
|
-
id: "
|
|
96344
|
-
name: "
|
|
96751
|
+
id: "minimax-m2.5",
|
|
96752
|
+
name: "MiniMax-M2.5",
|
|
96345
96753
|
contextWindow: 2e5,
|
|
96346
|
-
maxContextWindow:
|
|
96347
|
-
maxTokens:
|
|
96348
|
-
regions: ["
|
|
96349
|
-
credits: {
|
|
96350
|
-
|
|
96351
|
-
|
|
96352
|
-
|
|
96754
|
+
maxContextWindow: 2e5,
|
|
96755
|
+
maxTokens: 48e3,
|
|
96756
|
+
regions: ["cn"],
|
|
96757
|
+
credits: { cn: "0.18" },
|
|
96758
|
+
supportsImage: false,
|
|
96759
|
+
reasoningEfforts: [
|
|
96760
|
+
"low",
|
|
96761
|
+
"high",
|
|
96762
|
+
"max"
|
|
96763
|
+
],
|
|
96764
|
+
defaultReasoningEffort: "high",
|
|
96765
|
+
canDisableThinking: false,
|
|
96766
|
+
description: ""
|
|
96767
|
+
},
|
|
96768
|
+
{
|
|
96769
|
+
id: "minimax-m2.7",
|
|
96770
|
+
name: "MiniMax-M2.7",
|
|
96771
|
+
contextWindow: 2e5,
|
|
96772
|
+
maxContextWindow: 2e5,
|
|
96773
|
+
maxTokens: 48e3,
|
|
96774
|
+
regions: ["cn"],
|
|
96775
|
+
credits: { cn: "0.19" },
|
|
96353
96776
|
supportsImage: true,
|
|
96354
|
-
reasoningEfforts: [
|
|
96777
|
+
reasoningEfforts: [
|
|
96778
|
+
"low",
|
|
96779
|
+
"high",
|
|
96780
|
+
"max"
|
|
96781
|
+
],
|
|
96355
96782
|
defaultReasoningEffort: "high",
|
|
96356
96783
|
canDisableThinking: false,
|
|
96357
|
-
description: "
|
|
96784
|
+
description: ""
|
|
96358
96785
|
},
|
|
96359
96786
|
{
|
|
96360
|
-
id: "
|
|
96361
|
-
name: "
|
|
96787
|
+
id: "glm-5.0",
|
|
96788
|
+
name: "GLM-5.0",
|
|
96789
|
+
contextWindow: 2e5,
|
|
96790
|
+
maxContextWindow: 2e5,
|
|
96791
|
+
maxTokens: 48e3,
|
|
96792
|
+
regions: ["cn"],
|
|
96793
|
+
credits: { cn: "0.80" },
|
|
96794
|
+
supportsImage: false,
|
|
96795
|
+
reasoningEfforts: [],
|
|
96796
|
+
defaultReasoningEffort: null,
|
|
96797
|
+
canDisableThinking: false,
|
|
96798
|
+
description: ""
|
|
96799
|
+
},
|
|
96800
|
+
{
|
|
96801
|
+
id: "glm-5.0-turbo",
|
|
96802
|
+
name: "GLM-5.0-Turbo",
|
|
96803
|
+
contextWindow: 2e5,
|
|
96804
|
+
maxContextWindow: 2e5,
|
|
96805
|
+
maxTokens: 48e3,
|
|
96806
|
+
regions: ["cn"],
|
|
96807
|
+
credits: { cn: "0.95" },
|
|
96808
|
+
supportsImage: false,
|
|
96809
|
+
reasoningEfforts: [
|
|
96810
|
+
"low",
|
|
96811
|
+
"high",
|
|
96812
|
+
"max"
|
|
96813
|
+
],
|
|
96814
|
+
defaultReasoningEffort: "high",
|
|
96815
|
+
canDisableThinking: false,
|
|
96816
|
+
description: ""
|
|
96817
|
+
},
|
|
96818
|
+
{
|
|
96819
|
+
id: "glm-4.7",
|
|
96820
|
+
name: "GLM-4.7",
|
|
96821
|
+
contextWindow: 2e5,
|
|
96822
|
+
maxContextWindow: 2e5,
|
|
96823
|
+
maxTokens: 48e3,
|
|
96824
|
+
regions: ["cn"],
|
|
96825
|
+
credits: { cn: "0.23" },
|
|
96826
|
+
supportsImage: false,
|
|
96827
|
+
reasoningEfforts: [],
|
|
96828
|
+
defaultReasoningEffort: null,
|
|
96829
|
+
canDisableThinking: false,
|
|
96830
|
+
description: ""
|
|
96831
|
+
},
|
|
96832
|
+
{
|
|
96833
|
+
id: "glm-4.6",
|
|
96834
|
+
name: "GLM-4.6",
|
|
96835
|
+
contextWindow: 168e3,
|
|
96836
|
+
maxContextWindow: 168e3,
|
|
96837
|
+
maxTokens: 32e3,
|
|
96838
|
+
regions: ["cn"],
|
|
96839
|
+
credits: { cn: "0.23" },
|
|
96840
|
+
supportsImage: false,
|
|
96841
|
+
reasoningEfforts: [],
|
|
96842
|
+
defaultReasoningEffort: null,
|
|
96843
|
+
canDisableThinking: false,
|
|
96844
|
+
description: ""
|
|
96845
|
+
},
|
|
96846
|
+
{
|
|
96847
|
+
id: "glm-4.6v",
|
|
96848
|
+
name: "GLM-4.6V",
|
|
96849
|
+
contextWindow: 128e3,
|
|
96850
|
+
maxContextWindow: 128e3,
|
|
96851
|
+
maxTokens: 32e3,
|
|
96852
|
+
regions: ["cn"],
|
|
96853
|
+
credits: { cn: "0.11" },
|
|
96854
|
+
supportsImage: true,
|
|
96855
|
+
reasoningEfforts: [
|
|
96856
|
+
"low",
|
|
96857
|
+
"high",
|
|
96858
|
+
"max"
|
|
96859
|
+
],
|
|
96860
|
+
defaultReasoningEffort: "high",
|
|
96861
|
+
canDisableThinking: false,
|
|
96862
|
+
description: ""
|
|
96863
|
+
},
|
|
96864
|
+
{
|
|
96865
|
+
id: "kimi-k3-1",
|
|
96866
|
+
name: "Kimi-K3",
|
|
96362
96867
|
contextWindow: 1e6,
|
|
96363
96868
|
maxContextWindow: 1e6,
|
|
96364
|
-
maxTokens:
|
|
96869
|
+
maxTokens: 32e3,
|
|
96365
96870
|
regions: ["cn"],
|
|
96366
|
-
credits: { cn: "
|
|
96871
|
+
credits: { cn: "1.62" },
|
|
96367
96872
|
supportsImage: true,
|
|
96368
|
-
reasoningEfforts: [
|
|
96873
|
+
reasoningEfforts: [
|
|
96874
|
+
"low",
|
|
96875
|
+
"high",
|
|
96876
|
+
"max"
|
|
96877
|
+
],
|
|
96369
96878
|
defaultReasoningEffort: "high",
|
|
96370
96879
|
canDisableThinking: false,
|
|
96371
|
-
description: "
|
|
96880
|
+
description: "擅长处理复杂的长程自主任务,前端开发能力突出,同时在知识工作与科研推理上表现出色。"
|
|
96372
96881
|
},
|
|
96373
96882
|
{
|
|
96374
|
-
id: "
|
|
96375
|
-
name: "
|
|
96376
|
-
contextWindow:
|
|
96377
|
-
maxContextWindow:
|
|
96883
|
+
id: "kimi-k2.8-preview",
|
|
96884
|
+
name: "Kimi-K2.8-Preview",
|
|
96885
|
+
contextWindow: 1e6,
|
|
96886
|
+
maxContextWindow: 1e6,
|
|
96378
96887
|
maxTokens: 64e3,
|
|
96379
|
-
regions: ["
|
|
96380
|
-
credits: {
|
|
96381
|
-
cn: "0.00",
|
|
96382
|
-
intl: "0.00"
|
|
96383
|
-
},
|
|
96888
|
+
regions: ["cn"],
|
|
96889
|
+
credits: { cn: "0.77" },
|
|
96384
96890
|
supportsImage: true,
|
|
96385
|
-
reasoningEfforts: [
|
|
96891
|
+
reasoningEfforts: [
|
|
96892
|
+
"low",
|
|
96893
|
+
"high",
|
|
96894
|
+
"max"
|
|
96895
|
+
],
|
|
96896
|
+
defaultReasoningEffort: "high",
|
|
96897
|
+
canDisableThinking: true,
|
|
96898
|
+
description: "Moonshot AI · 擅长复杂长程自主任务、知识工作与科研推理,前端开发突出"
|
|
96899
|
+
},
|
|
96900
|
+
{
|
|
96901
|
+
id: "kimi-k2.5",
|
|
96902
|
+
name: "Kimi-K2.5",
|
|
96903
|
+
contextWindow: 164e3,
|
|
96904
|
+
maxContextWindow: 164e3,
|
|
96905
|
+
maxTokens: 32e3,
|
|
96906
|
+
regions: ["cn"],
|
|
96907
|
+
credits: { cn: "0.45" },
|
|
96908
|
+
supportsImage: true,
|
|
96909
|
+
reasoningEfforts: [
|
|
96910
|
+
"low",
|
|
96911
|
+
"high",
|
|
96912
|
+
"max"
|
|
96913
|
+
],
|
|
96386
96914
|
defaultReasoningEffort: "high",
|
|
96387
96915
|
canDisableThinking: false,
|
|
96388
|
-
description: "
|
|
96916
|
+
description: ""
|
|
96917
|
+
},
|
|
96918
|
+
{
|
|
96919
|
+
id: "kimi-k2-thinking",
|
|
96920
|
+
name: "Kimi-K2-Thinking",
|
|
96921
|
+
contextWindow: 164e3,
|
|
96922
|
+
maxContextWindow: 164e3,
|
|
96923
|
+
maxTokens: 32e3,
|
|
96924
|
+
regions: ["cn"],
|
|
96925
|
+
credits: { cn: "0.54" },
|
|
96926
|
+
supportsImage: false,
|
|
96927
|
+
reasoningEfforts: [
|
|
96928
|
+
"low",
|
|
96929
|
+
"high",
|
|
96930
|
+
"max"
|
|
96931
|
+
],
|
|
96932
|
+
defaultReasoningEffort: "high",
|
|
96933
|
+
canDisableThinking: false,
|
|
96934
|
+
description: ""
|
|
96389
96935
|
},
|
|
96390
96936
|
{
|
|
96391
96937
|
id: "hy3-x",
|
|
@@ -96405,6 +96951,20 @@ const FALLBACK_MODELS$1 = [
|
|
|
96405
96951
|
canDisableThinking: false,
|
|
96406
96952
|
description: "混元思考模型,具有增强的推理能力"
|
|
96407
96953
|
},
|
|
96954
|
+
{
|
|
96955
|
+
id: "hy4-preview-x",
|
|
96956
|
+
name: "Hy4 preview",
|
|
96957
|
+
contextWindow: 1e6,
|
|
96958
|
+
maxContextWindow: 1e6,
|
|
96959
|
+
maxTokens: 64e3,
|
|
96960
|
+
regions: ["cn"],
|
|
96961
|
+
credits: { cn: "0.29" },
|
|
96962
|
+
supportsImage: true,
|
|
96963
|
+
reasoningEfforts: ["high"],
|
|
96964
|
+
defaultReasoningEffort: "high",
|
|
96965
|
+
canDisableThinking: false,
|
|
96966
|
+
description: "混元思考模型,具有增强的推理能力"
|
|
96967
|
+
},
|
|
96408
96968
|
{
|
|
96409
96969
|
id: "hunyuan-chat",
|
|
96410
96970
|
name: "Hunyuan-Turbos",
|
|
@@ -96440,77 +97000,68 @@ const FALLBACK_MODELS$1 = [
|
|
|
96440
97000
|
description: "推理速度极快,编码能力强劲,并支持原生多模态输入的匿名大模型"
|
|
96441
97001
|
},
|
|
96442
97002
|
{
|
|
96443
|
-
id: "
|
|
96444
|
-
name: "
|
|
96445
|
-
contextWindow:
|
|
96446
|
-
maxContextWindow:
|
|
97003
|
+
id: "step-5-preview",
|
|
97004
|
+
name: "Step-5-Preview",
|
|
97005
|
+
contextWindow: 1e6,
|
|
97006
|
+
maxContextWindow: 1e6,
|
|
96447
97007
|
maxTokens: 64e3,
|
|
96448
97008
|
regions: ["cn"],
|
|
96449
|
-
credits: { cn: "0.
|
|
97009
|
+
credits: { cn: "0.33" },
|
|
96450
97010
|
supportsImage: true,
|
|
96451
97011
|
reasoningEfforts: [
|
|
96452
97012
|
"low",
|
|
96453
|
-
"
|
|
96454
|
-
"
|
|
97013
|
+
"medium",
|
|
97014
|
+
"high"
|
|
96455
97015
|
],
|
|
96456
|
-
defaultReasoningEffort: "
|
|
97016
|
+
defaultReasoningEffort: "high",
|
|
96457
97017
|
canDisableThinking: false,
|
|
96458
|
-
description: "
|
|
97018
|
+
description: "阶跃 StepFun 面向真实任务的新一代旗舰基模"
|
|
96459
97019
|
},
|
|
96460
97020
|
{
|
|
96461
|
-
id: "
|
|
96462
|
-
name: "
|
|
96463
|
-
contextWindow:
|
|
96464
|
-
maxContextWindow:
|
|
96465
|
-
maxTokens:
|
|
97021
|
+
id: "mimo-v2.6-pro",
|
|
97022
|
+
name: "Xiaomi MiMo-V2.6-Pro",
|
|
97023
|
+
contextWindow: 1e6,
|
|
97024
|
+
maxContextWindow: 1e6,
|
|
97025
|
+
maxTokens: 128e3,
|
|
96466
97026
|
regions: ["cn"],
|
|
96467
|
-
credits: { cn: "0.
|
|
97027
|
+
credits: { cn: "0.13" },
|
|
96468
97028
|
supportsImage: true,
|
|
96469
97029
|
reasoningEfforts: [
|
|
96470
97030
|
"low",
|
|
96471
97031
|
"high",
|
|
96472
97032
|
"max"
|
|
96473
97033
|
],
|
|
96474
|
-
defaultReasoningEffort: "
|
|
97034
|
+
defaultReasoningEffort: "high",
|
|
96475
97035
|
canDisableThinking: false,
|
|
96476
|
-
description: ""
|
|
97036
|
+
description: "Xiaomi · 旗舰推理模型,面向专业、复杂长程工作流,兼顾高阶科研与网络安全"
|
|
96477
97037
|
},
|
|
96478
97038
|
{
|
|
96479
|
-
id: "
|
|
96480
|
-
name: "
|
|
96481
|
-
contextWindow:
|
|
96482
|
-
maxContextWindow:
|
|
96483
|
-
maxTokens:
|
|
97039
|
+
id: "hy4free",
|
|
97040
|
+
name: "Hy4 Free",
|
|
97041
|
+
contextWindow: 1e6,
|
|
97042
|
+
maxContextWindow: 1e6,
|
|
97043
|
+
maxTokens: 64e3,
|
|
96484
97044
|
regions: ["cn"],
|
|
96485
|
-
credits: { cn: "0.
|
|
96486
|
-
supportsImage:
|
|
96487
|
-
reasoningEfforts: [
|
|
96488
|
-
"low",
|
|
96489
|
-
"high",
|
|
96490
|
-
"max"
|
|
96491
|
-
],
|
|
97045
|
+
credits: { cn: "0.00" },
|
|
97046
|
+
supportsImage: true,
|
|
97047
|
+
reasoningEfforts: ["high"],
|
|
96492
97048
|
defaultReasoningEffort: "high",
|
|
96493
97049
|
canDisableThinking: false,
|
|
96494
|
-
description: ""
|
|
97050
|
+
description: "最新旗舰模型,Agent与多模专长"
|
|
96495
97051
|
},
|
|
96496
|
-
...UNPUBLISHED_MODELS.filter((model) => model.id === "gemini-3.8-flash"),
|
|
96497
97052
|
{
|
|
96498
|
-
id: "
|
|
96499
|
-
name: "
|
|
97053
|
+
id: "hy4std",
|
|
97054
|
+
name: "Hy4",
|
|
96500
97055
|
contextWindow: 1e6,
|
|
96501
97056
|
maxContextWindow: 1e6,
|
|
96502
|
-
maxTokens:
|
|
96503
|
-
regions: ["
|
|
96504
|
-
credits: {
|
|
97057
|
+
maxTokens: 64e3,
|
|
97058
|
+
regions: ["cn"],
|
|
97059
|
+
credits: { cn: "0.29" },
|
|
96505
97060
|
supportsImage: true,
|
|
96506
|
-
reasoningEfforts: [
|
|
96507
|
-
|
|
96508
|
-
"high",
|
|
96509
|
-
"max"
|
|
96510
|
-
],
|
|
96511
|
-
defaultReasoningEffort: "medium",
|
|
97061
|
+
reasoningEfforts: ["high"],
|
|
97062
|
+
defaultReasoningEffort: "high",
|
|
96512
97063
|
canDisableThinking: false,
|
|
96513
|
-
description: "
|
|
97064
|
+
description: "最新旗舰模型,Agent与多模专长"
|
|
96514
97065
|
}
|
|
96515
97066
|
];
|
|
96516
97067
|
/**
|
|
@@ -96522,11 +97073,41 @@ const FALLBACK_MODELS$1 = [
|
|
|
96522
97073
|
* the account's region can actually call.
|
|
96523
97074
|
*/
|
|
96524
97075
|
const DEFAULT_VISIBLE_MODEL_IDS$1 = [
|
|
97076
|
+
"glm-5.3",
|
|
97077
|
+
"deepseek-v4.1-flash",
|
|
97078
|
+
"hy4std",
|
|
97079
|
+
"kimi-k3.1-pro"
|
|
97080
|
+
];
|
|
97081
|
+
/**
|
|
97082
|
+
* Default selections this table used to ship.
|
|
97083
|
+
*
|
|
97084
|
+
* "Never edited" is recognised by matching the CURRENT shipped set, so replacing
|
|
97085
|
+
* the set outright would turn every existing install into what looks like a user
|
|
97086
|
+
* who hand-picked a short list — and the picker would quietly narrow to it,
|
|
97087
|
+
* because the 2026-11-04 refresh dropped two of the retired ids outright
|
|
97088
|
+
* (`kimi-k2.6` is gone from both regions, `hy4-preview` is intl-only now).
|
|
97089
|
+
* Keeping the retired sets here is what lets the default move without that.
|
|
97090
|
+
*/
|
|
97091
|
+
const RETIRED_DEFAULT_SELECTIONS = [[
|
|
96525
97092
|
"glm-5.3",
|
|
96526
97093
|
"deepseek-v4.1-flash",
|
|
96527
97094
|
"hy4-preview",
|
|
96528
97095
|
"kimi-k2.6"
|
|
96529
|
-
];
|
|
97096
|
+
]];
|
|
97097
|
+
/**
|
|
97098
|
+
* Whether a stored selection is still a shipped default, i.e. the user never
|
|
97099
|
+
* touched the picker.
|
|
97100
|
+
*
|
|
97101
|
+
* Same size and same members, not order-sensitive: the card writes the list in
|
|
97102
|
+
* catalog order, which one default can spell two ways.
|
|
97103
|
+
*/
|
|
97104
|
+
function isUntouchedModelSelection(stored) {
|
|
97105
|
+
if (stored.length === 0) return false;
|
|
97106
|
+
return [DEFAULT_VISIBLE_MODEL_IDS$1, ...RETIRED_DEFAULT_SELECTIONS].some((shipped) => {
|
|
97107
|
+
const known = new Set(shipped);
|
|
97108
|
+
return stored.length === known.size && stored.every((id) => known.has(id));
|
|
97109
|
+
});
|
|
97110
|
+
}
|
|
96530
97111
|
/** Every id the fallback knows, in declaration order. */
|
|
96531
97112
|
const WORKBUDDY_MODEL_IDS = FALLBACK_MODELS$1.map((model) => model.id);
|
|
96532
97113
|
/**
|
|
@@ -96631,6 +97212,18 @@ const WORKBUDDY_VENDOR_RULES = [
|
|
|
96631
97212
|
re: /^minimax-/,
|
|
96632
97213
|
family: "minimax"
|
|
96633
97214
|
},
|
|
97215
|
+
{
|
|
97216
|
+
re: /^mimo-/,
|
|
97217
|
+
family: "xiaomi"
|
|
97218
|
+
},
|
|
97219
|
+
{
|
|
97220
|
+
re: /^step-/,
|
|
97221
|
+
family: "stepfun"
|
|
97222
|
+
},
|
|
97223
|
+
{
|
|
97224
|
+
re: /^grok-/,
|
|
97225
|
+
family: "xai"
|
|
97226
|
+
},
|
|
96634
97227
|
{
|
|
96635
97228
|
re: /^space-bunny$/,
|
|
96636
97229
|
family: "space-bunny"
|
|
@@ -100552,8 +101145,7 @@ function isPoolPausedError(error) {
|
|
|
100552
101145
|
*/
|
|
100553
101146
|
function resolveEnabledModelIds$2(stored, available, enabled = true) {
|
|
100554
101147
|
if (!enabled) return [];
|
|
100555
|
-
|
|
100556
|
-
if (stored.length > 0 && stored.length === shippedDefaults.size && stored.every((id) => shippedDefaults.has(id))) return [...available];
|
|
101148
|
+
if (isUntouchedModelSelection(stored)) return [...available];
|
|
100557
101149
|
const known = new Set(available);
|
|
100558
101150
|
return stored.filter((id) => known.has(id));
|
|
100559
101151
|
}
|
|
@@ -102220,8 +102812,7 @@ async function readRequestJson(request) {
|
|
|
102220
102812
|
*/
|
|
102221
102813
|
function resolveEnabledModelIds$1(stored, available, enabled = true) {
|
|
102222
102814
|
if (!enabled) return [];
|
|
102223
|
-
|
|
102224
|
-
if (stored.length > 0 && stored.length === shippedDefaults.size && stored.every((id) => shippedDefaults.has(id))) return [...available];
|
|
102815
|
+
if (isUntouchedModelSelection(stored)) return [...available];
|
|
102225
102816
|
const known = new Set(available);
|
|
102226
102817
|
return stored.filter((id) => known.has(id));
|
|
102227
102818
|
}
|