zelari-code 1.37.0 → 1.41.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.
@@ -794,16 +794,19 @@ __export(chatgptOAuth_exports, {
794
794
  CHATGPT_DEVICE_CODE_URL: () => CHATGPT_DEVICE_CODE_URL,
795
795
  CHATGPT_DEVICE_REDIRECT_URI: () => CHATGPT_DEVICE_REDIRECT_URI,
796
796
  CHATGPT_DEVICE_TOKEN_URL: () => CHATGPT_DEVICE_TOKEN_URL,
797
+ CHATGPT_DEVICE_VERIFY_URL: () => CHATGPT_DEVICE_VERIFY_URL,
797
798
  CHATGPT_SCOPE: () => CHATGPT_SCOPE,
798
799
  CHATGPT_TOKEN_URL: () => CHATGPT_TOKEN_URL,
799
800
  ChatgptOAuthError: () => ChatgptOAuthError,
800
801
  DEFAULT_CHATGPT_CLIENT_ID: () => DEFAULT_CHATGPT_CLIENT_ID,
801
802
  decodeJwtClaims: () => decodeJwtClaims,
802
803
  extractChatgptAccountId: () => extractChatgptAccountId,
804
+ pollChatgptDeviceAuth: () => pollChatgptDeviceAuth,
803
805
  refreshChatgptToken: () => refreshChatgptToken,
804
806
  runChatgptBrowserFlow: () => runChatgptBrowserFlow,
805
807
  runChatgptDeviceFlow: () => runChatgptDeviceFlow,
806
- runChatgptOAuthFlow: () => runChatgptOAuthFlow
808
+ runChatgptOAuthFlow: () => runChatgptOAuthFlow,
809
+ startChatgptDeviceAuth: () => startChatgptDeviceAuth
807
810
  });
808
811
  function decodeJwtClaims(token) {
809
812
  const parts = token.split(".");
@@ -845,6 +848,29 @@ function parseTokenPayload(obj, fallbackRefresh) {
845
848
  }
846
849
  return result;
847
850
  }
851
+ function errorSnippet(body) {
852
+ const err = body.error;
853
+ if (typeof err === "string" && err.trim()) return err.trim().slice(0, 160);
854
+ if (err && typeof err === "object") {
855
+ const msg2 = err.message;
856
+ if (typeof msg2 === "string" && msg2.trim()) return msg2.trim().slice(0, 160);
857
+ }
858
+ const msg = body.message;
859
+ if (typeof msg === "string" && msg.trim()) return msg.trim().slice(0, 160);
860
+ return "";
861
+ }
862
+ function formatHttpError(prefix, status, body) {
863
+ const detail = errorSnippet(body);
864
+ return detail ? `${prefix} HTTP ${status}: ${detail}` : `${prefix} HTTP ${status}`;
865
+ }
866
+ async function readJsonBody(response) {
867
+ try {
868
+ const parsed = await response.json();
869
+ if (parsed && typeof parsed === "object") return parsed;
870
+ } catch {
871
+ }
872
+ return {};
873
+ }
848
874
  async function postForm(url2, data, fetchImpl) {
849
875
  let response;
850
876
  try {
@@ -862,13 +888,34 @@ async function postForm(url2, data, fetchImpl) {
862
888
  "network_error"
863
889
  );
864
890
  }
865
- let body = {};
891
+ return { status: response.status, body: await readJsonBody(response) };
892
+ }
893
+ async function postJson(url2, data, fetchImpl) {
894
+ let response;
866
895
  try {
867
- const parsed = await response.json();
868
- if (parsed && typeof parsed === "object") body = parsed;
869
- } catch {
896
+ response = await fetchImpl(url2, {
897
+ method: "POST",
898
+ headers: {
899
+ "Content-Type": "application/json",
900
+ Accept: "application/json"
901
+ },
902
+ body: JSON.stringify(data)
903
+ });
904
+ } catch (err) {
905
+ throw new ChatgptOAuthError(
906
+ `ChatGPT OAuth network error: ${err instanceof Error ? err.message : String(err)}`,
907
+ "network_error"
908
+ );
870
909
  }
871
- return { status: response.status, body };
910
+ return { status: response.status, body: await readJsonBody(response) };
911
+ }
912
+ function parsePollInterval(raw, fallback = 5) {
913
+ if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) return raw;
914
+ if (typeof raw === "string") {
915
+ const n = Number.parseInt(raw, 10);
916
+ if (Number.isFinite(n) && n > 0) return n;
917
+ }
918
+ return fallback;
872
919
  }
873
920
  async function refreshChatgptToken(options) {
874
921
  if (!options.refreshToken.trim()) {
@@ -887,77 +934,82 @@ async function refreshChatgptToken(options) {
887
934
  );
888
935
  if (status >= 400) {
889
936
  const code = status === 400 || status === 401 ? "invalid_grant" : `http_${status}`;
890
- throw new ChatgptOAuthError(
891
- `ChatGPT token refresh HTTP ${status}: ${String(body.error ?? "").slice(0, 160)}`,
892
- code
893
- );
937
+ throw new ChatgptOAuthError(formatHttpError("ChatGPT token refresh", status, body), code);
894
938
  }
895
939
  return parseTokenPayload(body, options.refreshToken);
896
940
  }
897
- async function runChatgptDeviceFlow(options) {
941
+ async function startChatgptDeviceAuth(options = {}) {
898
942
  const clientId2 = options.clientId || process.env.CHATGPT_OAUTH_CLIENT_ID || DEFAULT_CHATGPT_CLIENT_ID;
899
943
  const fetchImpl = options.fetchImpl ?? fetch;
900
- const sleep = options.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
901
- const { verifier, challenge } = generatePkcePair();
902
- const start = await postForm(
903
- CHATGPT_DEVICE_CODE_URL,
904
- {
905
- client_id: clientId2,
906
- scope: CHATGPT_SCOPE,
907
- code_challenge: challenge,
908
- code_challenge_method: "S256"
909
- },
910
- fetchImpl
911
- );
944
+ const start = await postJson(CHATGPT_DEVICE_CODE_URL, { client_id: clientId2 }, fetchImpl);
912
945
  if (start.status >= 400) {
913
946
  throw new ChatgptOAuthError(
914
- `ChatGPT device-code HTTP ${start.status}`,
947
+ formatHttpError("ChatGPT device-code", start.status, start.body),
915
948
  `device_http_${start.status}`
916
949
  );
917
950
  }
918
- const deviceCode = start.body.device_code;
919
- const userCode = start.body.user_code;
920
- const verificationUri = typeof start.body.verification_uri === "string" && start.body.verification_uri || typeof start.body.verification_uri_complete === "string" && start.body.verification_uri_complete;
921
- if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string") {
951
+ const deviceAuthId = typeof start.body.device_auth_id === "string" && start.body.device_auth_id || typeof start.body.device_code === "string" && start.body.device_code || "";
952
+ const userCode = typeof start.body.user_code === "string" && start.body.user_code || typeof start.body.usercode === "string" && start.body.usercode || "";
953
+ const verificationUri = typeof start.body.verification_uri === "string" && start.body.verification_uri || typeof start.body.verification_url === "string" && start.body.verification_url || `${CHATGPT_DEVICE_VERIFY_URL}?user_code=${encodeURIComponent(userCode)}`;
954
+ if (!deviceAuthId || !userCode) {
922
955
  throw new ChatgptOAuthError("Device-code response missing fields", "no_device_code");
923
956
  }
924
- const info = {
957
+ return {
958
+ deviceAuthId,
925
959
  userCode,
926
960
  verificationUri,
961
+ interval: parsePollInterval(start.body.interval, 5),
927
962
  ...typeof start.body.verification_uri_complete === "string" ? { verificationUriComplete: start.body.verification_uri_complete } : {}
928
963
  };
929
- if (options.onUserCode) await options.onUserCode(info);
930
- try {
931
- await (options.openBrowserImpl ?? openBrowser)(
932
- info.verificationUriComplete ?? info.verificationUri
933
- );
934
- } catch {
935
- }
964
+ }
965
+ async function pollChatgptDeviceAuth(options) {
966
+ const clientId2 = options.clientId || process.env.CHATGPT_OAUTH_CLIENT_ID || DEFAULT_CHATGPT_CLIENT_ID;
967
+ const fetchImpl = options.fetchImpl ?? fetch;
968
+ const sleep = options.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
936
969
  const timeoutMs = options.timeoutMs ?? 3e5;
937
970
  const deadline = Date.now() + timeoutMs;
938
- let interval = 5;
971
+ let interval = options.interval ?? 5;
939
972
  let authorizationCode;
973
+ let codeVerifier;
940
974
  while (Date.now() < deadline) {
941
- const poll = await postForm(
975
+ const poll = await postJson(
942
976
  CHATGPT_DEVICE_TOKEN_URL,
943
- { client_id: clientId2, device_code: deviceCode },
977
+ { device_auth_id: options.deviceAuthId, user_code: options.userCode },
944
978
  fetchImpl
945
979
  );
946
980
  const authCode = poll.body.authorization_code;
947
981
  if (typeof authCode === "string" && authCode.length > 0) {
948
982
  authorizationCode = authCode;
983
+ if (typeof poll.body.code_verifier === "string" && poll.body.code_verifier) {
984
+ codeVerifier = poll.body.code_verifier;
985
+ }
949
986
  break;
950
987
  }
988
+ if (poll.status === 403 || poll.status === 404) {
989
+ await sleep(interval * 1e3);
990
+ continue;
991
+ }
951
992
  const err = typeof poll.body.error === "string" ? poll.body.error : "";
952
993
  if (err === "slow_down") interval += 5;
953
994
  else if (err && err !== "authorization_pending") {
954
995
  throw new ChatgptOAuthError(`Device authorization failed: ${err}`, err);
996
+ } else if (poll.status >= 400) {
997
+ throw new ChatgptOAuthError(
998
+ formatHttpError("ChatGPT device poll", poll.status, poll.body),
999
+ `poll_http_${poll.status}`
1000
+ );
955
1001
  }
956
1002
  await sleep(interval * 1e3);
957
1003
  }
958
1004
  if (!authorizationCode) {
959
1005
  throw new ChatgptOAuthError("Timed out waiting for ChatGPT device authorization", "timeout");
960
1006
  }
1007
+ if (!codeVerifier) {
1008
+ throw new ChatgptOAuthError(
1009
+ "Device token response missing code_verifier (server-issued PKCE)",
1010
+ "no_code_verifier"
1011
+ );
1012
+ }
961
1013
  const token = await postForm(
962
1014
  CHATGPT_TOKEN_URL,
963
1015
  {
@@ -965,13 +1017,13 @@ async function runChatgptDeviceFlow(options) {
965
1017
  code: authorizationCode,
966
1018
  redirect_uri: CHATGPT_DEVICE_REDIRECT_URI,
967
1019
  client_id: clientId2,
968
- code_verifier: verifier
1020
+ code_verifier: codeVerifier
969
1021
  },
970
1022
  fetchImpl
971
1023
  );
972
1024
  if (token.status >= 400) {
973
1025
  throw new ChatgptOAuthError(
974
- `ChatGPT token exchange HTTP ${token.status}`,
1026
+ formatHttpError("ChatGPT token exchange", token.status, token.body),
975
1027
  `token_http_${token.status}`
976
1028
  );
977
1029
  }
@@ -1024,12 +1076,47 @@ async function runChatgptBrowserFlow(options) {
1024
1076
  );
1025
1077
  if (token.status >= 400) {
1026
1078
  throw new ChatgptOAuthError(
1027
- `ChatGPT token exchange HTTP ${token.status}`,
1079
+ formatHttpError("ChatGPT token exchange", token.status, token.body),
1028
1080
  `token_http_${token.status}`
1029
1081
  );
1030
1082
  }
1031
1083
  return parseTokenPayload(token.body);
1032
1084
  }
1085
+ async function runChatgptDeviceFlow(options = {}) {
1086
+ const session = await startChatgptDeviceAuth({
1087
+ clientId: options.clientId,
1088
+ fetchImpl: options.fetchImpl
1089
+ });
1090
+ const info = {
1091
+ userCode: session.userCode,
1092
+ verificationUri: session.verificationUri,
1093
+ ...session.verificationUriComplete ? { verificationUriComplete: session.verificationUriComplete } : {}
1094
+ };
1095
+ if (options.onUserCode) {
1096
+ await options.onUserCode(info);
1097
+ } else {
1098
+ process.stderr.write(
1099
+ `[chatgpt oauth] Open ${info.verificationUri} and enter:
1100
+ ${info.userCode}
1101
+ `
1102
+ );
1103
+ }
1104
+ try {
1105
+ await (options.openBrowserImpl ?? openBrowser)(
1106
+ info.verificationUriComplete ?? info.verificationUri
1107
+ );
1108
+ } catch {
1109
+ }
1110
+ return pollChatgptDeviceAuth({
1111
+ deviceAuthId: session.deviceAuthId,
1112
+ userCode: session.userCode,
1113
+ clientId: options.clientId,
1114
+ interval: session.interval,
1115
+ fetchImpl: options.fetchImpl,
1116
+ sleepImpl: options.sleepImpl,
1117
+ timeoutMs: options.timeoutMs
1118
+ });
1119
+ }
1033
1120
  async function runChatgptOAuthFlow(options = {}) {
1034
1121
  const flow = (process.env.CHATGPT_OAUTH_FLOW ?? "device").toLowerCase();
1035
1122
  if (flow === "browser") {
@@ -1037,7 +1124,7 @@ async function runChatgptOAuthFlow(options = {}) {
1037
1124
  }
1038
1125
  return runChatgptDeviceFlow(options);
1039
1126
  }
1040
- var DEFAULT_CHATGPT_CLIENT_ID, CHATGPT_AUTHORIZE_URL, CHATGPT_TOKEN_URL, CHATGPT_DEVICE_CODE_URL, CHATGPT_DEVICE_TOKEN_URL, CHATGPT_DEVICE_REDIRECT_URI, CHATGPT_SCOPE, CHATGPT_AUTH_CLAIMS, ChatgptOAuthError;
1127
+ var DEFAULT_CHATGPT_CLIENT_ID, CHATGPT_AUTHORIZE_URL, CHATGPT_TOKEN_URL, CHATGPT_DEVICE_CODE_URL, CHATGPT_DEVICE_TOKEN_URL, CHATGPT_DEVICE_REDIRECT_URI, CHATGPT_DEVICE_VERIFY_URL, CHATGPT_SCOPE, CHATGPT_AUTH_CLAIMS, ChatgptOAuthError;
1041
1128
  var init_chatgptOAuth = __esm({
1042
1129
  "src/cli/chatgptOAuth.ts"() {
1043
1130
  "use strict";
@@ -1049,6 +1136,7 @@ var init_chatgptOAuth = __esm({
1049
1136
  CHATGPT_DEVICE_CODE_URL = "https://auth.openai.com/api/accounts/deviceauth/usercode";
1050
1137
  CHATGPT_DEVICE_TOKEN_URL = "https://auth.openai.com/api/accounts/deviceauth/token";
1051
1138
  CHATGPT_DEVICE_REDIRECT_URI = "https://auth.openai.com/deviceauth/callback";
1139
+ CHATGPT_DEVICE_VERIFY_URL = "https://auth.openai.com/codex/device";
1052
1140
  CHATGPT_SCOPE = "openid profile email offline_access";
1053
1141
  CHATGPT_AUTH_CLAIMS = "https://api.openai.com/auth";
1054
1142
  ChatgptOAuthError = class extends Error {
@@ -1152,7 +1240,7 @@ async function startAnthropicOAuth(options = {}) {
1152
1240
  }
1153
1241
  return { authorizeUrl, state: state3 };
1154
1242
  }
1155
- async function postJson(url2, payload, fetchImpl) {
1243
+ async function postJson2(url2, payload, fetchImpl) {
1156
1244
  let response;
1157
1245
  try {
1158
1246
  response = await fetchImpl(url2, {
@@ -1212,7 +1300,7 @@ async function completeAnthropicOAuth(options) {
1212
1300
  redirect_uri: ANTHROPIC_REDIRECT_URI,
1213
1301
  state: parsed.state ?? pending.state
1214
1302
  };
1215
- const { status, body } = await postJson(ANTHROPIC_TOKEN_URL, payload, fetchImpl);
1303
+ const { status, body } = await postJson2(ANTHROPIC_TOKEN_URL, payload, fetchImpl);
1216
1304
  if (status >= 400) {
1217
1305
  const code = status === 400 || status === 401 ? "invalid_grant" : `http_${status}`;
1218
1306
  throw new AnthropicOAuthError(
@@ -1227,7 +1315,7 @@ async function refreshAnthropicToken(options) {
1227
1315
  throw new AnthropicOAuthError("Missing refreshToken", "no_refresh_token");
1228
1316
  }
1229
1317
  const fetchImpl = options.fetchImpl ?? fetch;
1230
- const { status, body } = await postJson(
1318
+ const { status, body } = await postJson2(
1231
1319
  ANTHROPIC_TOKEN_URL,
1232
1320
  {
1233
1321
  grant_type: "refresh_token",
@@ -1532,9 +1620,151 @@ var init_keyStore = __esm({
1532
1620
  }
1533
1621
  });
1534
1622
 
1623
+ // src/cli/thinkingCapability.ts
1624
+ function thinkingCapabilityFor(id, model) {
1625
+ const base = PROVIDER_THINKING_CAPABILITY[id] ?? {};
1626
+ const efforts = effortLevelsFor(id, model);
1627
+ const budget = supportsBudget(id, model);
1628
+ return {
1629
+ ...base,
1630
+ effort: efforts.length > 0 || Boolean(base.effort),
1631
+ budget,
1632
+ efforts: efforts.length > 0 ? efforts : void 0
1633
+ };
1634
+ }
1635
+ function effortLevelsFor(id, model) {
1636
+ const m = (model ?? "").trim();
1637
+ switch (id) {
1638
+ case "grok":
1639
+ case "openai-compatible":
1640
+ case "custom":
1641
+ if (grokHasXhigh(m)) return [...BASE_EFFORTS, "xhigh"];
1642
+ return [...BASE_EFFORTS];
1643
+ case "chatgpt":
1644
+ if (gptHasMax(m)) return [...BASE_EFFORTS, "xhigh", "max"];
1645
+ if (gptHasXhigh(m)) return [...BASE_EFFORTS, "xhigh"];
1646
+ return [...BASE_EFFORTS];
1647
+ case "deepseek":
1648
+ return ["high", "max"];
1649
+ case "minimax":
1650
+ return [...BASE_EFFORTS];
1651
+ case "glm":
1652
+ if (glmHasEffortScale(m)) return ["low", "high", "max"];
1653
+ return [];
1654
+ case "anthropic":
1655
+ if (claudeHasXhigh(m)) return ["high", "xhigh", "max"];
1656
+ if (claudeHasMax(m)) return ["high", "max"];
1657
+ return [];
1658
+ default:
1659
+ return [];
1660
+ }
1661
+ }
1662
+ function supportsBudget(id, model) {
1663
+ if (id === "anthropic") return true;
1664
+ if (id === "glm") return !glmHasEffortScale(model);
1665
+ return Boolean(PROVIDER_THINKING_CAPABILITY[id]?.budget);
1666
+ }
1667
+ function grokHasXhigh(model) {
1668
+ const v = parseDottedVersion(model, /grok[-_]?(\d+)(?:[.-](\d+))?/i);
1669
+ if (!v) return false;
1670
+ return v.major > 4 || v.major === 4 && v.minor >= 6;
1671
+ }
1672
+ function gptHasXhigh(model) {
1673
+ const v = parseDottedVersion(model, /gpt[-_]?(\d+)(?:[.-](\d+))?/i);
1674
+ if (!v) return false;
1675
+ return v.major > 5 || v.major === 5 && v.minor >= 4;
1676
+ }
1677
+ function gptHasMax(model) {
1678
+ const v = parseDottedVersion(model, /gpt[-_]?(\d+)(?:[.-](\d+))?/i);
1679
+ if (!v) return false;
1680
+ return v.major > 5 || v.major === 5 && v.minor >= 6;
1681
+ }
1682
+ function claudeHasMax(model) {
1683
+ const v = parseClaudeVersion(model);
1684
+ if (!v) return false;
1685
+ return v.major > 4 || v.major === 4 && v.minor >= 6;
1686
+ }
1687
+ function claudeHasXhigh(model) {
1688
+ const v = parseClaudeVersion(model);
1689
+ if (!v) return false;
1690
+ if (v.major >= 5) return true;
1691
+ return v.major === 4 && v.minor >= 7;
1692
+ }
1693
+ function glmHasEffortScale(model) {
1694
+ const v = parseDottedVersion(model ?? "", /glm[-_]?(\d+)(?:[.-](\d+))?/i);
1695
+ if (!v) return false;
1696
+ return v.major >= 5;
1697
+ }
1698
+ function parseDottedVersion(model, re) {
1699
+ const m = re.exec(model);
1700
+ if (!m) return null;
1701
+ return {
1702
+ major: Number.parseInt(m[1], 10),
1703
+ minor: m[2] ? Number.parseInt(m[2], 10) : 0
1704
+ };
1705
+ }
1706
+ function parseClaudeVersion(model) {
1707
+ const m = /claude-(?:sonnet|opus|haiku)[-_]?(\d+)(?:[.-](\d+))?/i.exec(model);
1708
+ if (!m) return null;
1709
+ return {
1710
+ major: Number.parseInt(m[1], 10),
1711
+ minor: m[2] ? Number.parseInt(m[2], 10) : 0
1712
+ };
1713
+ }
1714
+ var THINKING_EFFORTS, BASE_EFFORTS, PROVIDER_THINKING_CAPABILITY;
1715
+ var init_thinkingCapability = __esm({
1716
+ "src/cli/thinkingCapability.ts"() {
1717
+ "use strict";
1718
+ THINKING_EFFORTS = [
1719
+ "low",
1720
+ "medium",
1721
+ "high",
1722
+ "xhigh",
1723
+ "max"
1724
+ ];
1725
+ BASE_EFFORTS = ["low", "medium", "high"];
1726
+ PROVIDER_THINKING_CAPABILITY = {
1727
+ "openai-compatible": { effort: true },
1728
+ grok: { effort: true },
1729
+ chatgpt: { effort: true },
1730
+ anthropic: { budget: true },
1731
+ glm: { budget: true },
1732
+ deepseek: { effort: true },
1733
+ minimax: { effort: true },
1734
+ custom: { effort: true }
1735
+ };
1736
+ }
1737
+ });
1738
+
1535
1739
  // src/cli/thinking.ts
1536
- function thinkingCapabilityFor(id) {
1537
- return PROVIDER_THINKING_CAPABILITY[id] ?? {};
1740
+ function clampEffort(id, model, requested) {
1741
+ const native = effortLevelsFor(id, model);
1742
+ if (native.includes(requested)) {
1743
+ return { effort: requested, clamped: false };
1744
+ }
1745
+ if (native.length === 0) {
1746
+ return {
1747
+ effort: requested,
1748
+ clamped: true,
1749
+ note: `thinking '${requested}' is not supported for provider "${id}"`
1750
+ };
1751
+ }
1752
+ const want = EFFORT_RANK[requested];
1753
+ let best = native[0];
1754
+ let bestDist = Math.abs(EFFORT_RANK[best] - want);
1755
+ for (const level of native) {
1756
+ const dist = Math.abs(EFFORT_RANK[level] - want);
1757
+ if (dist < bestDist || dist === bestDist && EFFORT_RANK[level] > EFFORT_RANK[best]) {
1758
+ best = level;
1759
+ bestDist = dist;
1760
+ }
1761
+ }
1762
+ const label = model ? `${id}/${model}` : id;
1763
+ return {
1764
+ effort: best,
1765
+ clamped: true,
1766
+ note: `'${requested}' is not native on ${label} \u2014 using '${best}'`
1767
+ };
1538
1768
  }
1539
1769
  function stringifyThinkingSpec(spec) {
1540
1770
  if (spec === "auto") return "auto";
@@ -1546,7 +1776,9 @@ function parseThinkingSpec(raw) {
1546
1776
  const s = (raw ?? "").trim().toLowerCase();
1547
1777
  if (!s || s === "auto") return "auto";
1548
1778
  if (s === "off") return { kind: "off" };
1549
- if (s === "low" || s === "medium" || s === "high") return { kind: "effort", effort: s };
1779
+ if (THINKING_EFFORTS.includes(s)) {
1780
+ return { kind: "effort", effort: s };
1781
+ }
1550
1782
  const m = /^budget:(\d+)$/.exec(s);
1551
1783
  if (m) {
1552
1784
  const n = Number.parseInt(m[1], 10);
@@ -1556,15 +1788,19 @@ function parseThinkingSpec(raw) {
1556
1788
  }
1557
1789
  function isValidThinkingInput(raw) {
1558
1790
  const s = raw.trim().toLowerCase();
1559
- if (s === "auto" || s === "off" || s === "low" || s === "medium" || s === "high") return true;
1791
+ if (s === "auto" || s === "off") return true;
1792
+ if (THINKING_EFFORTS.includes(s)) return true;
1560
1793
  return /^budget:\d+$/.test(s) && Number.parseInt(s.slice(7), 10) > 0;
1561
1794
  }
1562
1795
  function degrade(note) {
1563
1796
  return { patch: {}, degraded: true, note };
1564
1797
  }
1565
- function translateOpenAiCompatibleThinking(providerId, spec) {
1798
+ function withClampNote(patch, clamped, note) {
1799
+ return { patch, degraded: false, note: clamped ? note : void 0 };
1800
+ }
1801
+ function translateOpenAiCompatibleThinking(providerId, spec, model) {
1566
1802
  if (spec === "auto") return { patch: {}, degraded: false };
1567
- const cap3 = thinkingCapabilityFor(providerId);
1803
+ const cap3 = thinkingCapabilityFor(providerId, model);
1568
1804
  switch (spec.kind) {
1569
1805
  case "off":
1570
1806
  if (providerId === "deepseek" || providerId === "glm") {
@@ -1572,20 +1808,40 @@ function translateOpenAiCompatibleThinking(providerId, spec) {
1572
1808
  }
1573
1809
  if (cap3.effort) return { patch: { reasoning_effort: "low" }, degraded: false };
1574
1810
  return degrade(`thinking 'off' is not supported for provider "${providerId}"`);
1575
- case "effort":
1576
- if (!cap3.effort) {
1811
+ case "effort": {
1812
+ if (!cap3.effort && !cap3.efforts?.length) {
1577
1813
  return degrade(`thinking 'effort' is not supported for provider "${providerId}"`);
1578
1814
  }
1815
+ const resolved = clampEffort(providerId, model, spec.effort);
1579
1816
  if (providerId === "deepseek") {
1580
- return {
1581
- patch: {
1817
+ return withClampNote(
1818
+ {
1582
1819
  thinking: { type: "enabled" },
1583
- reasoning_effort: spec.effort === "high" ? "max" : "high"
1820
+ reasoning_effort: resolved.effort === "max" ? "max" : "high"
1584
1821
  },
1585
- degraded: false
1586
- };
1822
+ resolved.clamped,
1823
+ resolved.note
1824
+ );
1587
1825
  }
1588
- return { patch: { reasoning_effort: spec.effort }, degraded: false };
1826
+ if (providerId === "glm") {
1827
+ if (!glmHasEffortScale(model)) {
1828
+ return degrade(`thinking 'effort' is not supported for GLM ${model || "4.x"} \u2014 use budget:N`);
1829
+ }
1830
+ return withClampNote(
1831
+ {
1832
+ thinking: { type: "enabled" },
1833
+ reasoning_effort: resolved.effort
1834
+ },
1835
+ resolved.clamped,
1836
+ resolved.note
1837
+ );
1838
+ }
1839
+ return withClampNote(
1840
+ { reasoning_effort: resolved.effort },
1841
+ resolved.clamped,
1842
+ resolved.note
1843
+ );
1844
+ }
1589
1845
  case "budget":
1590
1846
  if (!cap3.budget) {
1591
1847
  return degrade(`thinking 'budget' is not supported for provider "${providerId}"`);
@@ -1596,18 +1852,24 @@ function translateOpenAiCompatibleThinking(providerId, spec) {
1596
1852
  };
1597
1853
  }
1598
1854
  }
1599
- function translateResponsesThinking(spec) {
1855
+ function translateResponsesThinking(spec, model) {
1600
1856
  if (spec === "auto") return { patch: {}, degraded: false };
1601
1857
  switch (spec.kind) {
1602
1858
  case "off":
1603
1859
  return { patch: { reasoning: { effort: "minimal" } }, degraded: false };
1604
- case "effort":
1605
- return { patch: { reasoning: { effort: spec.effort } }, degraded: false };
1860
+ case "effort": {
1861
+ const resolved = clampEffort("chatgpt", model, spec.effort);
1862
+ return withClampNote(
1863
+ { reasoning: { effort: resolved.effort } },
1864
+ resolved.clamped,
1865
+ resolved.note
1866
+ );
1867
+ }
1606
1868
  case "budget":
1607
- return degrade('thinking "budget" is not supported for chatgpt \u2014 use low/medium/high');
1869
+ return degrade('thinking "budget" is not supported for chatgpt \u2014 use low/medium/high/xhigh/max');
1608
1870
  }
1609
1871
  }
1610
- function translateAnthropicThinking(spec) {
1872
+ function translateAnthropicThinking(spec, model) {
1611
1873
  if (spec === "auto") return { patch: {}, degraded: false };
1612
1874
  switch (spec.kind) {
1613
1875
  case "off":
@@ -1617,23 +1879,31 @@ function translateAnthropicThinking(spec) {
1617
1879
  patch: { thinking: { type: "enabled", budget_tokens: spec.budgetTokens } },
1618
1880
  degraded: false
1619
1881
  };
1620
- case "effort":
1621
- return degrade('thinking "effort" is not supported for anthropic \u2014 use budget:N');
1882
+ case "effort": {
1883
+ const levels = effortLevelsFor("anthropic", model);
1884
+ if (levels.length === 0) {
1885
+ return degrade('thinking "effort" is not supported for this Claude model \u2014 use budget:N');
1886
+ }
1887
+ const resolved = clampEffort("anthropic", model, spec.effort);
1888
+ return withClampNote(
1889
+ { output_config: { effort: resolved.effort } },
1890
+ resolved.clamped,
1891
+ resolved.note
1892
+ );
1893
+ }
1622
1894
  }
1623
1895
  }
1624
- var PROVIDER_THINKING_CAPABILITY;
1896
+ var EFFORT_RANK;
1625
1897
  var init_thinking = __esm({
1626
1898
  "src/cli/thinking.ts"() {
1627
1899
  "use strict";
1628
- PROVIDER_THINKING_CAPABILITY = {
1629
- "openai-compatible": { effort: true },
1630
- "grok": { effort: true },
1631
- "chatgpt": { effort: true },
1632
- "anthropic": { budget: true },
1633
- "glm": { budget: true },
1634
- "deepseek": { effort: true },
1635
- "minimax": { effort: true },
1636
- "custom": { effort: true }
1900
+ init_thinkingCapability();
1901
+ EFFORT_RANK = {
1902
+ low: 1,
1903
+ medium: 2,
1904
+ high: 3,
1905
+ xhigh: 4,
1906
+ max: 5
1637
1907
  };
1638
1908
  }
1639
1909
  });
@@ -1826,14 +2096,14 @@ var init_providerConfig = __esm({
1826
2096
  DEFAULTS = {
1827
2097
  activeProviderId: "openai-compatible",
1828
2098
  modelByProvider: {
1829
- // grok-4.5: flagship; reasoning_effort defaults to "high" on the xAI API
1830
- "openai-compatible": "grok-4.5",
2099
+ // grok-4.6: flagship; native reasoning_effort includes xhigh
2100
+ "openai-compatible": "grok-4.6",
1831
2101
  "minimax": "MiniMax-M2.5",
1832
2102
  "glm": "glm-4.6",
1833
- "grok": "grok-4.5",
2103
+ "grok": "grok-4.6",
1834
2104
  "deepseek": "deepseek-v4-pro",
1835
- "chatgpt": "gpt-5.2-codex",
1836
- "anthropic": "claude-sonnet-4-5",
2105
+ "chatgpt": "gpt-5.6-codex",
2106
+ "anthropic": "claude-sonnet-4-6",
1837
2107
  "custom": ""
1838
2108
  },
1839
2109
  thinkingByProvider: {
@@ -1860,6 +2130,7 @@ __export(modelDiscovery_exports, {
1860
2130
  getCachedModels: () => getCachedModels,
1861
2131
  getDiscoveredModelIds: () => getDiscoveredModelIds,
1862
2132
  getModelsFilePath: () => getModelsFilePath,
2133
+ getStaticFallbackModels: () => getStaticFallbackModels,
1863
2134
  isModelsCacheStale: () => isModelsCacheStale,
1864
2135
  loadModelsRegistry: () => loadModelsRegistry,
1865
2136
  pickDefaultModel: () => pickDefaultModel
@@ -1867,6 +2138,9 @@ __export(modelDiscovery_exports, {
1867
2138
  import { promises as fs3, existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
1868
2139
  import { homedir } from "node:os";
1869
2140
  import path5 from "node:path";
2141
+ function getStaticFallbackModels(provider) {
2142
+ return STATIC_FALLBACKS[provider] ? [...STATIC_FALLBACKS[provider]] : [];
2143
+ }
1870
2144
  async function resolveDiscoveryBaseUrl(provider, options) {
1871
2145
  if (options.baseUrl) return options.baseUrl;
1872
2146
  const { getCustomEndpoint: getCustomEndpoint2 } = await Promise.resolve().then(() => (init_providerConfig(), providerConfig_exports));
@@ -2002,10 +2276,6 @@ async function discoverModelsForProvider(provider, options = {}) {
2002
2276
  const headers2 = await resolveDiscoveryHeaders(provider, authToken);
2003
2277
  response = await fetchImpl(url2, { method: "GET", headers: headers2 });
2004
2278
  } catch (err) {
2005
- const fallback = STATIC_FALLBACKS[provider];
2006
- if (fallback) {
2007
- return cacheFallback(provider, baseUrl, fallback, options, `network: ${err instanceof Error ? err.message : String(err)}`);
2008
- }
2009
2279
  throw new ModelDiscoveryError(
2010
2280
  `Network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}`,
2011
2281
  "network_error"
@@ -2013,10 +2283,6 @@ async function discoverModelsForProvider(provider, options = {}) {
2013
2283
  }
2014
2284
  if (!response.ok) {
2015
2285
  const body = await response.text().catch(() => "");
2016
- const fallback = STATIC_FALLBACKS[provider];
2017
- if (fallback) {
2018
- return cacheFallback(provider, baseUrl, fallback, options, `HTTP ${response.status}: ${body.slice(0, 80)}`);
2019
- }
2020
2286
  throw new ModelDiscoveryError(
2021
2287
  `HTTP ${response.status} from ${url2}: ${body.slice(0, 200)}`,
2022
2288
  `http_${response.status}`
@@ -2026,10 +2292,6 @@ async function discoverModelsForProvider(provider, options = {}) {
2026
2292
  try {
2027
2293
  json2 = await response.json();
2028
2294
  } catch (err) {
2029
- const fallback = STATIC_FALLBACKS[provider];
2030
- if (fallback) {
2031
- return cacheFallback(provider, baseUrl, fallback, options, "invalid_json");
2032
- }
2033
2295
  throw new ModelDiscoveryError(
2034
2296
  `Invalid JSON from ${url2}: ${err instanceof Error ? err.message : String(err)}`,
2035
2297
  "invalid_json"
@@ -2037,10 +2299,6 @@ async function discoverModelsForProvider(provider, options = {}) {
2037
2299
  }
2038
2300
  const models = provider === "anthropic" ? parseAnthropicModelsResponse(json2) : parseOpenAIModelsResponse(json2, baseUrl);
2039
2301
  if (models.length === 0) {
2040
- const fallback = STATIC_FALLBACKS[provider];
2041
- if (fallback) {
2042
- return cacheFallback(provider, baseUrl, fallback, options, "empty_response");
2043
- }
2044
2302
  throw new ModelDiscoveryError(
2045
2303
  `Provider ${provider} returned 0 models \u2014 refusing to overwrite cache`,
2046
2304
  "empty_response"
@@ -2060,22 +2318,6 @@ async function discoverModelsForProvider(provider, options = {}) {
2060
2318
  }
2061
2319
  return entry;
2062
2320
  }
2063
- async function cacheFallback(provider, baseUrl, models, options, lastError) {
2064
- const entry = {
2065
- models,
2066
- fetchedAt: Date.now(),
2067
- baseUrl,
2068
- lastError
2069
- };
2070
- if (!options.skipCacheWrite) {
2071
- const file2 = getModelsFilePath();
2072
- await readModifyWriteRegistry((current) => {
2073
- current[provider] = entry;
2074
- return current;
2075
- }, file2);
2076
- }
2077
- return entry;
2078
- }
2079
2321
  function discoverModelsInBackground(provider, options = {}) {
2080
2322
  discoverModelsForProvider(provider, options).catch((err) => {
2081
2323
  if (err instanceof ModelDiscoveryError && options.onError) {
@@ -2113,6 +2355,9 @@ var init_modelDiscovery = __esm({
2113
2355
  };
2114
2356
  STATIC_FALLBACKS = {
2115
2357
  chatgpt: [
2358
+ { id: "gpt-5.6-codex", displayName: "GPT-5.6 Codex" },
2359
+ { id: "gpt-5.6", displayName: "GPT-5.6" },
2360
+ { id: "gpt-5.4", displayName: "GPT-5.4" },
2116
2361
  { id: "gpt-5.2-codex", displayName: "GPT-5.2 Codex" },
2117
2362
  { id: "gpt-5.2", displayName: "GPT-5.2" },
2118
2363
  { id: "gpt-5.1-codex", displayName: "GPT-5.1 Codex" },
@@ -2121,9 +2366,19 @@ var init_modelDiscovery = __esm({
2121
2366
  { id: "o4-mini", displayName: "o4-mini" }
2122
2367
  ],
2123
2368
  anthropic: [
2369
+ { id: "claude-opus-4-7", displayName: "Claude Opus 4.7" },
2124
2370
  { id: "claude-opus-4-6", displayName: "Claude Opus 4.6" },
2371
+ { id: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6" },
2125
2372
  { id: "claude-sonnet-4-5", displayName: "Claude Sonnet 4.5" },
2126
2373
  { id: "claude-haiku-4-5", displayName: "Claude Haiku 4.5" }
2374
+ ],
2375
+ grok: [
2376
+ { id: "grok-4.6", displayName: "Grok 4.6" },
2377
+ { id: "grok-4.5", displayName: "Grok 4.5" }
2378
+ ],
2379
+ glm: [
2380
+ { id: "glm-5.3", displayName: "GLM-5.3" },
2381
+ { id: "glm-4.6", displayName: "GLM-4.6" }
2127
2382
  ]
2128
2383
  };
2129
2384
  writeChain = Promise.resolve();
@@ -28169,7 +28424,7 @@ function openaiCompatibleProvider(config2) {
28169
28424
  if (thinking.thinking) body.thinking = { type: thinking.thinking };
28170
28425
  if (thinking.reasoningEffort) body.reasoning_effort = thinking.reasoningEffort;
28171
28426
  } else if (thinkingSpec !== "auto") {
28172
- const t = translateOpenAiCompatibleThinking(config2.providerId, thinkingSpec);
28427
+ const t = translateOpenAiCompatibleThinking(config2.providerId, thinkingSpec, config2.model);
28173
28428
  if (t.degraded) {
28174
28429
  console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
28175
28430
  } else {
@@ -28703,7 +28958,7 @@ function anthropicMessagesProvider(config2) {
28703
28958
  }
28704
28959
  const thinkingSpec = config2.thinking ?? "auto";
28705
28960
  if (thinkingSpec !== "auto") {
28706
- const t = translateAnthropicThinking(thinkingSpec);
28961
+ const t = translateAnthropicThinking(thinkingSpec, config2.model);
28707
28962
  if (t.degraded) console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
28708
28963
  else Object.assign(body, t.patch);
28709
28964
  }
@@ -28915,7 +29170,7 @@ function chatgptResponsesProvider(config2) {
28915
29170
  }
28916
29171
  const thinkingSpec = config2.thinking ?? "auto";
28917
29172
  if (thinkingSpec !== "auto") {
28918
- const t = translateResponsesThinking(thinkingSpec);
29173
+ const t = translateResponsesThinking(thinkingSpec, config2.model);
28919
29174
  if (t.degraded) console.warn(`[thinking] ${t.note ?? "unsupported"} \u2014 falling back to provider default.`);
28920
29175
  else Object.assign(body, t.patch);
28921
29176
  }
@@ -42695,9 +42950,9 @@ var init_oauthDesktop = __esm({
42695
42950
  init_chatgptOAuth();
42696
42951
  init_anthropicOAuth();
42697
42952
  DEFAULT_MODELS = {
42698
- grok: "grok-4.5",
42699
- chatgpt: "gpt-5.2-codex",
42700
- anthropic: "claude-sonnet-4-5"
42953
+ grok: "grok-4.6",
42954
+ chatgpt: "gpt-5.6-codex",
42955
+ anthropic: "claude-sonnet-4-6"
42701
42956
  };
42702
42957
  }
42703
42958
  });
@@ -42957,12 +43212,12 @@ function handleModelSet(ctx, model) {
42957
43212
  }
42958
43213
  function handleEffortShow(ctx) {
42959
43214
  const id = ctx.activeProviderSpec.id;
42960
- const cap3 = thinkingCapabilityFor(id);
43215
+ const cap3 = thinkingCapabilityFor(id, ctx.activeModel);
42961
43216
  const current = stringifyThinkingSpec(getThinkingForProvider(id));
42962
43217
  const options = [
42963
43218
  "auto",
42964
43219
  "off",
42965
- ...cap3.effort ? ["low", "medium", "high"] : [],
43220
+ ...cap3.efforts ?? (cap3.effort ? ["low", "medium", "high"] : []),
42966
43221
  ...cap3.budget ? ["budget:<tokens>"] : []
42967
43222
  ];
42968
43223
  appendSystem(
@@ -42975,7 +43230,7 @@ function handleEffortSet(ctx, raw) {
42975
43230
  if (!isValidThinkingInput(raw)) {
42976
43231
  appendSystem(
42977
43232
  ctx.setMessages,
42978
- `[effort] invalid spec "${raw}" \u2014 use auto | off | low | medium | high | budget:<tokens>`
43233
+ `[effort] invalid spec "${raw}" \u2014 use auto | off | low | medium | high | xhigh | max | budget:<tokens>`
42979
43234
  );
42980
43235
  return;
42981
43236
  }
@@ -43536,6 +43791,11 @@ function buildDesktopConfigSnapshot() {
43536
43791
  const providers = PROVIDERS.map((p3) => {
43537
43792
  const cached2 = getCachedModels(p3.id);
43538
43793
  const models = cached2?.models.map((m) => m.id) ?? [];
43794
+ if (models.length === 0) {
43795
+ for (const m of getStaticFallbackModels(p3.id)) {
43796
+ if (!models.includes(m.id)) models.push(m.id);
43797
+ }
43798
+ }
43539
43799
  const defaultModel = config2.modelByProvider[p3.id] ?? "";
43540
43800
  if (defaultModel && !models.includes(defaultModel)) {
43541
43801
  models.unshift(defaultModel);
@@ -43559,7 +43819,7 @@ function buildDesktopConfigSnapshot() {
43559
43819
  hasRefreshToken: Boolean(stored?.refreshToken),
43560
43820
  oauthSupported: isOAuthProvider(p3.id),
43561
43821
  thinking: config2.thinkingByProvider[p3.id] ?? "auto",
43562
- thinkingCapability: thinkingCapabilityFor(p3.id)
43822
+ thinkingCapability: thinkingCapabilityFor(p3.id, defaultModel)
43563
43823
  };
43564
43824
  });
43565
43825
  return {