@acosmi/sdk-ts 2.3.0 → 2.5.1

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.
@@ -322,13 +322,20 @@ function resolveThinkingLevel(body, req, caps) {
322
322
  }
323
323
  delete body["temperature"];
324
324
  }
325
- exports.AnthropicAdapter = void 0;
325
+ var ANTHROPIC_SDK_MANAGED_BODY_KEYS; exports.AnthropicAdapter = void 0;
326
326
  var init_anthropic = __esm({
327
327
  "src/models/adapters/anthropic.ts"() {
328
328
  init_types();
329
329
  init_errors();
330
330
  init_betas();
331
331
  init_adapters();
332
+ ANTHROPIC_SDK_MANAGED_BODY_KEYS = /* @__PURE__ */ new Set([
333
+ "thinking",
334
+ "effort",
335
+ "max_tokens",
336
+ "temperature",
337
+ "betas"
338
+ ]);
332
339
  exports.AnthropicAdapter = class {
333
340
  format() {
334
341
  return 0 /* Anthropic */;
@@ -416,6 +423,12 @@ var init_anthropic = __esm({
416
423
  }
417
424
  if (req.extraBody) {
418
425
  for (const [k, v] of Object.entries(req.extraBody)) {
426
+ if (ANTHROPIC_SDK_MANAGED_BODY_KEYS.has(k)) {
427
+ console.warn(
428
+ `acosmi-sdk: extraBody key "${k}" is SDK-managed and was ignored (use the dedicated request field instead).`
429
+ );
430
+ continue;
431
+ }
419
432
  body[k] = v;
420
433
  }
421
434
  }
@@ -775,6 +788,9 @@ var init_openai = __esm({
775
788
  messageStarted = false;
776
789
  thinkingStarted = false;
777
790
  thinkingStopped = false;
791
+ /** thinking block 打开时占用的 Anthropic block index — 关闭时必须用它, 不能用
792
+ * 可能已被 text/tool 推进的 this.blockIndex (否则 content_block_stop 索引错配)。 */
793
+ thinkingBlockIndex = 0;
778
794
  textStarted = false;
779
795
  /** OpenAI tool_call index → Anthropic block index */
780
796
  toolBlockIndex = /* @__PURE__ */ new Map();
@@ -815,6 +831,7 @@ var init_openai = __esm({
815
831
  if (choice.delta.reasoning_content && choice.delta.reasoning_content !== "") {
816
832
  if (!this.thinkingStarted) {
817
833
  this.thinkingStarted = true;
834
+ this.thinkingBlockIndex = this.blockIndex;
818
835
  const blockJSON = JSON.stringify({
819
836
  type: "content_block_start",
820
837
  index: this.blockIndex,
@@ -824,7 +841,7 @@ var init_openai = __esm({
824
841
  }
825
842
  const deltaJSON = JSON.stringify({
826
843
  type: "content_block_delta",
827
- index: this.blockIndex,
844
+ index: this.thinkingBlockIndex,
828
845
  delta: { type: "thinking_delta", thinking: choice.delta.reasoning_content }
829
846
  });
830
847
  events.push({ event: "content_block_delta", data: deltaJSON });
@@ -834,7 +851,7 @@ var init_openai = __esm({
834
851
  this.thinkingStopped = true;
835
852
  const stopJSON = JSON.stringify({
836
853
  type: "content_block_stop",
837
- index: this.blockIndex
854
+ index: this.thinkingBlockIndex
838
855
  });
839
856
  events.push({ event: "content_block_stop", data: stopJSON });
840
857
  this.blockIndex++;
@@ -857,6 +874,15 @@ var init_openai = __esm({
857
874
  }
858
875
  for (const tc of choice.delta.tool_calls ?? []) {
859
876
  if (!this.toolBlockIndex.has(tc.index)) {
877
+ if (this.thinkingStarted && !this.thinkingStopped) {
878
+ this.thinkingStopped = true;
879
+ const stopJSON = JSON.stringify({
880
+ type: "content_block_stop",
881
+ index: this.thinkingBlockIndex
882
+ });
883
+ events.push({ event: "content_block_stop", data: stopJSON });
884
+ this.blockIndex++;
885
+ }
860
886
  if (this.textStarted) {
861
887
  const stopJSON = JSON.stringify({
862
888
  type: "content_block_stop",
@@ -901,9 +927,10 @@ var init_openai = __esm({
901
927
  });
902
928
  events.push({ event: "content_block_stop", data: stopJSON2 });
903
929
  } else if (this.thinkingStarted && !this.thinkingStopped) {
930
+ this.thinkingStopped = true;
904
931
  const stopJSON2 = JSON.stringify({
905
932
  type: "content_block_stop",
906
- index: this.blockIndex
933
+ index: this.thinkingBlockIndex
907
934
  });
908
935
  events.push({ event: "content_block_stop", data: stopJSON2 });
909
936
  }
@@ -944,13 +971,6 @@ function getAdapter(provider) {
944
971
  return defaultOpenAIAdapter;
945
972
  }
946
973
  function getAdapterForModel(m) {
947
- const pref = (m.preferred_format ?? "").trim().toLowerCase();
948
- switch (pref) {
949
- case "anthropic":
950
- return new exports.AnthropicAdapter();
951
- case "openai":
952
- return new exports.OpenAIAdapter();
953
- }
954
974
  let hasAnthropic = false;
955
975
  let hasOpenAI = false;
956
976
  for (const f of m.supported_formats ?? []) {
@@ -963,6 +983,16 @@ function getAdapterForModel(m) {
963
983
  break;
964
984
  }
965
985
  }
986
+ const declared = hasAnthropic || hasOpenAI;
987
+ const pref = (m.preferred_format ?? "").trim().toLowerCase();
988
+ switch (pref) {
989
+ case "anthropic":
990
+ if (!declared || hasAnthropic) return new exports.AnthropicAdapter();
991
+ break;
992
+ case "openai":
993
+ if (!declared || hasOpenAI) return new exports.OpenAIAdapter();
994
+ break;
995
+ }
966
996
  if (hasAnthropic) return new exports.AnthropicAdapter();
967
997
  if (hasOpenAI) return new exports.OpenAIAdapter();
968
998
  return getAdapter((m.provider ?? "").toLowerCase());
@@ -972,6 +1002,7 @@ var init_adapters = __esm({
972
1002
  "src/models/adapters/index.ts"() {
973
1003
  init_anthropic();
974
1004
  init_openai();
1005
+ init_openai();
975
1006
  exports.ProviderFormat = /* @__PURE__ */ ((ProviderFormat2) => {
976
1007
  ProviderFormat2[ProviderFormat2["Anthropic"] = 0] = "Anthropic";
977
1008
  ProviderFormat2[ProviderFormat2["OpenAI"] = 1] = "OpenAI";
@@ -992,8 +1023,14 @@ init_types();
992
1023
  // src/auth/types.ts
993
1024
  function tokenSetIsExpired(t) {
994
1025
  const expiresAt = new Date(t.expires_at).getTime();
1026
+ if (!Number.isFinite(expiresAt)) return true;
995
1027
  return Date.now() > expiresAt - 3e4;
996
1028
  }
1029
+ function isValidTokenSet(x) {
1030
+ if (typeof x !== "object" || x === null) return false;
1031
+ const t = x;
1032
+ return typeof t.access_token === "string" && typeof t.refresh_token === "string" && typeof t.expires_at === "string" && typeof t.scope === "string" && typeof t.client_id === "string" && typeof t.server_url === "string";
1033
+ }
997
1034
 
998
1035
  // src/core/client.ts
999
1036
  init_errors();
@@ -1027,7 +1064,7 @@ var OAuthTokenEndpointError = class extends Error {
1027
1064
  function isInvalidGrantError(err) {
1028
1065
  return err instanceof OAuthTokenEndpointError && err.oauthError === "invalid_grant";
1029
1066
  }
1030
- async function discoverWithProfile(serverURL, profile, signal) {
1067
+ async function discoverWithProfile(serverURL, profile, signal, fetchImpl = globalThis.fetch) {
1031
1068
  let parsed;
1032
1069
  try {
1033
1070
  parsed = new URL(serverURL.replace(/\/+$/, ""));
@@ -1039,7 +1076,7 @@ async function discoverWithProfile(serverURL, profile, signal) {
1039
1076
  const ctl = withTimeout(authTimeoutMs, signal);
1040
1077
  let resp;
1041
1078
  try {
1042
- resp = await fetch(endpoint, { method: "GET", signal: ctl.signal });
1079
+ resp = await fetchImpl(endpoint, { method: "GET", signal: ctl.signal });
1043
1080
  } catch (e) {
1044
1081
  throw new Error(`discover: ${e instanceof Error ? e.message : String(e)}`);
1045
1082
  } finally {
@@ -1061,13 +1098,13 @@ async function discoverWithProfile(serverURL, profile, signal) {
1061
1098
  }
1062
1099
  return meta;
1063
1100
  }
1064
- async function discover(serverURL, signal) {
1065
- return discoverWithProfile(serverURL, "desktop", signal);
1101
+ async function discover(serverURL, signal, fetchImpl = globalThis.fetch) {
1102
+ return discoverWithProfile(serverURL, "desktop", signal, fetchImpl);
1066
1103
  }
1067
- async function discoverWebOAuthMetadata(serverURL, signal) {
1068
- return discoverWithProfile(serverURL, "web", signal);
1104
+ async function discoverWebOAuthMetadata(serverURL, signal, fetchImpl = globalThis.fetch) {
1105
+ return discoverWithProfile(serverURL, "web", signal, fetchImpl);
1069
1106
  }
1070
- async function register(meta, appName, signal) {
1107
+ async function register(meta, appName, signal, fetchImpl = globalThis.fetch) {
1071
1108
  const regReq = {
1072
1109
  client_name: appName,
1073
1110
  token_endpoint_auth_method: "none",
@@ -1078,7 +1115,7 @@ async function register(meta, appName, signal) {
1078
1115
  const ctl = withTimeout(authTimeoutMs, signal);
1079
1116
  let resp;
1080
1117
  try {
1081
- resp = await fetch(meta.registration_endpoint, {
1118
+ resp = await fetchImpl(meta.registration_endpoint, {
1082
1119
  method: "POST",
1083
1120
  headers: { "Content-Type": "application/json" },
1084
1121
  body: JSON.stringify(regReq),
@@ -1098,7 +1135,7 @@ async function register(meta, appName, signal) {
1098
1135
  throw new Error(`register: decode: ${e instanceof Error ? e.message : String(e)}`);
1099
1136
  }
1100
1137
  }
1101
- async function registerWebOAuthClient(meta, opts, signal) {
1138
+ async function registerWebOAuthClient(meta, opts, signal, fetchImpl = globalThis.fetch) {
1102
1139
  const regReq = {
1103
1140
  client_name: opts.clientName,
1104
1141
  token_endpoint_auth_method: "none",
@@ -1110,7 +1147,7 @@ async function registerWebOAuthClient(meta, opts, signal) {
1110
1147
  const ctl = withTimeout(authTimeoutMs, signal);
1111
1148
  let resp;
1112
1149
  try {
1113
- resp = await fetch(meta.registration_endpoint, {
1150
+ resp = await fetchImpl(meta.registration_endpoint, {
1114
1151
  method: "POST",
1115
1152
  headers: { "Content-Type": "application/json" },
1116
1153
  body: JSON.stringify(regReq),
@@ -1314,7 +1351,7 @@ async function createWebAuthorizationRequest(meta, opts) {
1314
1351
  createdAt: Date.now()
1315
1352
  };
1316
1353
  }
1317
- async function completeWebAuthorizationRequest(pending, params, signal) {
1354
+ async function completeWebAuthorizationRequest(pending, params, signal, fetchImpl = globalThis.fetch) {
1318
1355
  if (!params.code) {
1319
1356
  throw new Error("completeWebAuthorizationRequest: missing authorization code");
1320
1357
  }
@@ -1323,18 +1360,19 @@ async function completeWebAuthorizationRequest(pending, params, signal) {
1323
1360
  `completeWebAuthorizationRequest: ${ErrStateMismatch}: callback state does not match pending state (possible CSRF)`
1324
1361
  );
1325
1362
  }
1326
- const meta = await discoverWebOAuthMetadata(pending.serverURL, signal);
1363
+ const meta = await discoverWebOAuthMetadata(pending.serverURL, signal, fetchImpl);
1327
1364
  const resp = await exchangeCode(
1328
1365
  meta,
1329
1366
  pending.clientID,
1330
1367
  params.code,
1331
1368
  pending.redirectURI,
1332
1369
  pending.verifier,
1333
- signal
1370
+ signal,
1371
+ fetchImpl
1334
1372
  );
1335
1373
  return newTokenSet(resp, pending.clientID, pending.serverURL);
1336
1374
  }
1337
- async function exchangeCode(meta, clientID, code, redirectURI, codeVerifier, signal) {
1375
+ async function exchangeCode(meta, clientID, code, redirectURI, codeVerifier, signal, fetchImpl = globalThis.fetch) {
1338
1376
  const data = new URLSearchParams({
1339
1377
  grant_type: "authorization_code",
1340
1378
  client_id: clientID,
@@ -1342,9 +1380,9 @@ async function exchangeCode(meta, clientID, code, redirectURI, codeVerifier, sig
1342
1380
  redirect_uri: redirectURI,
1343
1381
  code_verifier: codeVerifier
1344
1382
  });
1345
- return postToken(meta.token_endpoint, data, signal);
1383
+ return postToken(meta.token_endpoint, data, signal, fetchImpl);
1346
1384
  }
1347
- async function exchangeCodeWithExpiry(meta, clientID, code, redirectURI, codeVerifier, expiresIn, signal) {
1385
+ async function exchangeCodeWithExpiry(meta, clientID, code, redirectURI, codeVerifier, expiresIn, signal, fetchImpl = globalThis.fetch) {
1348
1386
  const data = new URLSearchParams({
1349
1387
  grant_type: "authorization_code",
1350
1388
  client_id: clientID,
@@ -1353,24 +1391,24 @@ async function exchangeCodeWithExpiry(meta, clientID, code, redirectURI, codeVer
1353
1391
  code_verifier: codeVerifier,
1354
1392
  expires_in: String(expiresIn)
1355
1393
  });
1356
- return postToken(meta.token_endpoint, data, signal);
1394
+ return postToken(meta.token_endpoint, data, signal, fetchImpl);
1357
1395
  }
1358
- async function refreshToken(meta, clientID, refreshTokenValue, signal) {
1396
+ async function refreshToken(meta, clientID, refreshTokenValue, signal, fetchImpl = globalThis.fetch) {
1359
1397
  const data = new URLSearchParams({
1360
1398
  grant_type: "refresh_token",
1361
1399
  client_id: clientID,
1362
1400
  refresh_token: refreshTokenValue
1363
1401
  });
1364
- return postToken(meta.token_endpoint, data, signal);
1402
+ return postToken(meta.token_endpoint, data, signal, fetchImpl);
1365
1403
  }
1366
- async function revokeToken(meta, token, signal) {
1404
+ async function revokeToken(meta, token, signal, fetchImpl = globalThis.fetch) {
1367
1405
  if (!meta.revocation_endpoint || meta.revocation_endpoint === "") {
1368
1406
  return;
1369
1407
  }
1370
1408
  const data = new URLSearchParams({ token });
1371
1409
  const ctl = withTimeout(authTimeoutMs, signal);
1372
1410
  try {
1373
- await fetch(meta.revocation_endpoint, {
1411
+ await fetchImpl(meta.revocation_endpoint, {
1374
1412
  method: "POST",
1375
1413
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
1376
1414
  body: data,
@@ -1382,11 +1420,11 @@ async function revokeToken(meta, token, signal) {
1382
1420
  ctl.dispose();
1383
1421
  }
1384
1422
  }
1385
- async function postToken(endpoint, data, signal) {
1423
+ async function postToken(endpoint, data, signal, fetchImpl = globalThis.fetch) {
1386
1424
  const ctl = withTimeout(authTimeoutMs, signal);
1387
1425
  let resp;
1388
1426
  try {
1389
- resp = await fetch(endpoint, {
1427
+ resp = await fetchImpl(endpoint, {
1390
1428
  method: "POST",
1391
1429
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
1392
1430
  body: data,
@@ -1604,7 +1642,15 @@ ${Date.now()}
1604
1642
  await fs.mkdir(dir, { recursive: true, mode: 448 });
1605
1643
  const tmp = `${p}.tmp.${process.pid}.${Date.now()}.${Math.floor(Math.random() * 1e6)}`;
1606
1644
  const data = JSON.stringify(tokens, null, 2);
1607
- await fs.writeFile(tmp, data, { encoding: "utf8", mode: 384 });
1645
+ {
1646
+ const fh = await fs.open(tmp, "w", 384);
1647
+ try {
1648
+ await fh.writeFile(data, { encoding: "utf8" });
1649
+ await fh.sync();
1650
+ } finally {
1651
+ await fh.close();
1652
+ }
1653
+ }
1608
1654
  try {
1609
1655
  await fs.rename(tmp, p);
1610
1656
  } catch (e) {
@@ -1614,6 +1660,15 @@ ${Date.now()}
1614
1660
  }
1615
1661
  throw e;
1616
1662
  }
1663
+ try {
1664
+ const dirHandle = await fs.open(dir, "r");
1665
+ try {
1666
+ await dirHandle.sync();
1667
+ } finally {
1668
+ await dirHandle.close();
1669
+ }
1670
+ } catch {
1671
+ }
1617
1672
  });
1618
1673
  }
1619
1674
  load() {
@@ -1622,7 +1677,14 @@ ${Date.now()}
1622
1677
  const p = await this.resolvePath();
1623
1678
  try {
1624
1679
  const data = await fs.readFile(p, "utf8");
1625
- return JSON.parse(data);
1680
+ let parsed;
1681
+ try {
1682
+ parsed = JSON.parse(data);
1683
+ } catch {
1684
+ return null;
1685
+ }
1686
+ if (!isValidTokenSet(parsed)) return null;
1687
+ return parsed;
1626
1688
  } catch (e) {
1627
1689
  if (isNotExistError(e)) return null;
1628
1690
  throw new Error(
@@ -1673,11 +1735,14 @@ var LocalStorageTokenStore = class {
1673
1735
  async load() {
1674
1736
  const data = globalThis.localStorage.getItem(this.key);
1675
1737
  if (data == null || data === "") return null;
1738
+ let parsed;
1676
1739
  try {
1677
- return JSON.parse(data);
1740
+ parsed = JSON.parse(data);
1678
1741
  } catch {
1679
1742
  return null;
1680
1743
  }
1744
+ if (!isValidTokenSet(parsed)) return null;
1745
+ return parsed;
1681
1746
  }
1682
1747
  async clear() {
1683
1748
  globalThis.localStorage.removeItem(this.key);
@@ -2046,6 +2111,32 @@ function normalizeGatewayBaseURL(input) {
2046
2111
  const path = parsed.pathname.replace(/\/+$/, "");
2047
2112
  return path ? `${parsed.origin}${path}` : parsed.origin;
2048
2113
  }
2114
+ function normalizeOverrideBaseURL(raw, label) {
2115
+ if (typeof raw !== "string") {
2116
+ throw new TypeError(`${label} must be a string`);
2117
+ }
2118
+ const trimmed = raw.trim();
2119
+ if (trimmed.length === 0) {
2120
+ throw new Error(`${label} is empty`);
2121
+ }
2122
+ let parsed;
2123
+ try {
2124
+ parsed = new URL(trimmed);
2125
+ } catch {
2126
+ throw new Error(`${label} is not a valid URL: ${trimmed}`);
2127
+ }
2128
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2129
+ throw new Error(`${label} only allows http/https, got ${parsed.protocol} (${trimmed})`);
2130
+ }
2131
+ if (!parsed.host) {
2132
+ throw new Error(`${label} has empty host: ${trimmed}`);
2133
+ }
2134
+ if (parsed.search.length > 0 || parsed.hash.length > 0) {
2135
+ throw new Error(`${label} must not contain query or hash: ${trimmed}`);
2136
+ }
2137
+ const path = parsed.pathname.replace(/\/+$/, "");
2138
+ return path ? `${parsed.origin}${path}` : parsed.origin;
2139
+ }
2049
2140
  function pickAndNormalizeGatewayURL(cfg) {
2050
2141
  const inputs = [];
2051
2142
  if (cfg.serverURL !== void 0) inputs.push(["serverURL", cfg.serverURL]);
@@ -2066,6 +2157,7 @@ var ErrOAuthCORSBlocked = "oauth_cors_blocked";
2066
2157
  var ErrRefreshProxyFailed = "refresh_proxy_failed";
2067
2158
  var ErrTokenExpired = "token_expired";
2068
2159
  var CHAT_REQUEST_TIMEOUT_MS = 11 * 60 * 1e3;
2160
+ var DEFAULT_API_TIMEOUT_MS = 6e4;
2069
2161
  function newDeferred() {
2070
2162
  let resolve;
2071
2163
  let reject;
@@ -2123,8 +2215,8 @@ var Client = class _Client {
2123
2215
  constructor(cfg = {}) {
2124
2216
  const picked = pickAndNormalizeGatewayURL(cfg);
2125
2217
  this.serverURL = picked ?? DEFAULT_GATEWAY_BASE_URL;
2126
- this.complianceBaseURL = cfg.complianceBaseURL ? cfg.complianceBaseURL.replace(/\/+$/, "") : null;
2127
- this.apiBaseURL = cfg.apiBaseURL ? cfg.apiBaseURL.replace(/\/+$/, "") : null;
2218
+ this.complianceBaseURL = cfg.complianceBaseURL ? normalizeOverrideBaseURL(cfg.complianceBaseURL, "complianceBaseURL") : null;
2219
+ this.apiBaseURL = cfg.apiBaseURL ? normalizeOverrideBaseURL(cfg.apiBaseURL, "apiBaseURL") : null;
2128
2220
  this.oauthMetadataProfile = cfg.oauthMetadataProfile ?? "desktop";
2129
2221
  this.browserRefreshMode = cfg.browserRefreshMode ?? "direct";
2130
2222
  this.refreshProxyURL = cfg.refreshProxyURL ?? null;
@@ -2216,7 +2308,7 @@ var Client = class _Client {
2216
2308
  try {
2217
2309
  let meta;
2218
2310
  try {
2219
- meta = await discover(this.serverURL, signal);
2311
+ meta = await discover(this.serverURL, signal, this.fetchImpl);
2220
2312
  } catch (err) {
2221
2313
  emitError(ErrDiscovery, err);
2222
2314
  throw new Error(`discovery failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -2225,7 +2317,7 @@ var Client = class _Client {
2225
2317
  let clientID = this.getCachedClientID();
2226
2318
  if (clientID === "") {
2227
2319
  try {
2228
- const reg = await register(meta, appName, signal);
2320
+ const reg = await register(meta, appName, signal, this.fetchImpl);
2229
2321
  clientID = reg.client_id;
2230
2322
  } catch (err) {
2231
2323
  emitError(ErrRegistration, err);
@@ -2242,7 +2334,7 @@ var Client = class _Client {
2242
2334
  verifier = r.verifier;
2243
2335
  } catch (err) {
2244
2336
  try {
2245
- const reg = await register(meta, appName, signal);
2337
+ const reg = await register(meta, appName, signal, this.fetchImpl);
2246
2338
  clientID = reg.client_id;
2247
2339
  } catch (regErr) {
2248
2340
  emitError(ErrRegistration, regErr);
@@ -2270,7 +2362,8 @@ var Client = class _Client {
2270
2362
  result.redirectURI,
2271
2363
  verifier,
2272
2364
  opts.expiresIn,
2273
- signal
2365
+ signal,
2366
+ this.fetchImpl
2274
2367
  );
2275
2368
  } else {
2276
2369
  tokenResp = await exchangeCode(
@@ -2279,7 +2372,8 @@ var Client = class _Client {
2279
2372
  result.code,
2280
2373
  result.redirectURI,
2281
2374
  verifier,
2282
- signal
2375
+ signal,
2376
+ this.fetchImpl
2283
2377
  );
2284
2378
  }
2285
2379
  } catch (err) {
@@ -2316,18 +2410,18 @@ var Client = class _Client {
2316
2410
  if (tokens) {
2317
2411
  if (!meta) {
2318
2412
  try {
2319
- meta = await discoverWithProfile(this.serverURL, this.oauthMetadataProfile, signal);
2413
+ meta = await discoverWithProfile(this.serverURL, this.oauthMetadataProfile, signal, this.fetchImpl);
2320
2414
  } catch (e) {
2321
2415
  console.warn(`[acosmi-sdk] warning: discover for revocation failed: ${e instanceof Error ? e.message : String(e)}`);
2322
2416
  }
2323
2417
  }
2324
2418
  if (meta) {
2325
2419
  try {
2326
- await revokeToken(meta, tokens.access_token, signal);
2420
+ await revokeToken(meta, tokens.access_token, signal, this.fetchImpl);
2327
2421
  } catch {
2328
2422
  }
2329
2423
  try {
2330
- await revokeToken(meta, tokens.refresh_token, signal);
2424
+ await revokeToken(meta, tokens.refresh_token, signal, this.fetchImpl);
2331
2425
  } catch {
2332
2426
  }
2333
2427
  }
@@ -2421,7 +2515,7 @@ var Client = class _Client {
2421
2515
  }
2422
2516
  if (this.meta == null) {
2423
2517
  try {
2424
- this.meta = await discoverWithProfile(this.serverURL, this.oauthMetadataProfile, signal);
2518
+ this.meta = await discoverWithProfile(this.serverURL, this.oauthMetadataProfile, signal, this.fetchImpl);
2425
2519
  } catch (e) {
2426
2520
  throw new Error(
2427
2521
  `discover for refresh: ${e instanceof Error ? e.message : String(e)}`
@@ -2434,7 +2528,8 @@ var Client = class _Client {
2434
2528
  this.meta,
2435
2529
  this.tokens.client_id,
2436
2530
  this.tokens.refresh_token,
2437
- signal
2531
+ signal,
2532
+ this.fetchImpl
2438
2533
  );
2439
2534
  } catch (e) {
2440
2535
  const message = e instanceof Error ? e.message : String(e);
@@ -2701,10 +2796,10 @@ var Client = class _Client {
2701
2796
  * v0.5.0: 根据 provider 自动路由到 /anthropic 或 /chat 端点
2702
2797
  */
2703
2798
  async chat(modelID, req, signal) {
2704
- req.stream = false;
2799
+ const r = { ...req, stream: false };
2705
2800
  const ctl = withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS, signal);
2706
2801
  try {
2707
- const { body, adapter } = await this.buildChatRequest(modelID, req, ctl.signal);
2802
+ const { body, adapter } = await this.buildChatRequest(modelID, r, ctl.signal);
2708
2803
  const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2709
2804
  const { result, headers } = await this.doJSONFullRaw("POST", endpoint, body, ctl.signal);
2710
2805
  const resp = adapter.parseResponse(result);
@@ -2802,11 +2897,11 @@ var Client = class _Client {
2802
2897
  return this.chatMessagesOpenAI(modelID, req, adapter, signal);
2803
2898
  }
2804
2899
  async chatMessagesAnthropic(modelID, req, adapter, signal) {
2805
- req.stream = false;
2900
+ const r = { ...req, stream: false };
2806
2901
  const ctl = withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS, signal);
2807
2902
  try {
2808
2903
  const caps = this.getCachedCapabilities(modelID) ?? zeroModelCapabilities();
2809
- const body = adapter.buildRequestBody(caps, req);
2904
+ const body = adapter.buildRequestBody(caps, r);
2810
2905
  const data = JSON.stringify(body);
2811
2906
  const { result } = await this.doJSONFullRaw(
2812
2907
  "POST",
@@ -2839,11 +2934,11 @@ var Client = class _Client {
2839
2934
  }
2840
2935
  }
2841
2936
  async chatMessagesOpenAI(modelID, req, adapter, signal) {
2842
- req.stream = false;
2937
+ const r = { ...req, stream: false };
2843
2938
  const ctl = withRequestTimeout(CHAT_REQUEST_TIMEOUT_MS, signal);
2844
2939
  try {
2845
2940
  const caps = this.getCachedCapabilities(modelID) ?? zeroModelCapabilities();
2846
- const body = adapter.buildRequestBody(caps, req);
2941
+ const body = adapter.buildRequestBody(caps, r);
2847
2942
  const data = JSON.stringify(body);
2848
2943
  const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2849
2944
  const { result } = await this.doJSONFullRaw("POST", endpoint, data, ctl.signal);
@@ -2873,8 +2968,8 @@ var Client = class _Client {
2873
2968
  };
2874
2969
  }
2875
2970
  async *chatStreamGen(modelID, req, signal, retried) {
2876
- req.stream = true;
2877
- const { body, adapter } = await this.buildChatRequest(modelID, req, signal);
2971
+ const r = { ...req, stream: true };
2972
+ const { body, adapter } = await this.buildChatRequest(modelID, r, signal);
2878
2973
  const token = await this.ensureToken(signal);
2879
2974
  const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2880
2975
  const url = this.apiURL(endpoint);
@@ -2942,8 +3037,8 @@ var Client = class _Client {
2942
3037
  }
2943
3038
  }
2944
3039
  async *chatMessagesStreamGen(modelID, req, signal, retried) {
2945
- req.stream = true;
2946
- const { body, adapter } = await this.buildChatRequest(modelID, req, signal);
3040
+ const r = { ...req, stream: true };
3041
+ const { body, adapter } = await this.buildChatRequest(modelID, r, signal);
2947
3042
  const token = await this.ensureToken(signal);
2948
3043
  const endpoint = `/managed-models/${encodeURIComponent(modelID)}${adapter.endpointSuffix()}`;
2949
3044
  const url = this.apiURL(endpoint);
@@ -3115,6 +3210,9 @@ var Client = class _Client {
3115
3210
  throw parseHTTPErrorWithHeader(resp.status, bodyBytes, resp.headers);
3116
3211
  }
3117
3212
  const text = await resp.text();
3213
+ if (!text) {
3214
+ return { result: void 0, headers: resp.headers };
3215
+ }
3118
3216
  const result = JSON.parse(text);
3119
3217
  if (result && typeof result === "object" && "code" in result) {
3120
3218
  const bizErr = apiResponseBusinessError(result);
@@ -3215,6 +3313,18 @@ var Client = class _Client {
3215
3313
  ctl.dispose();
3216
3314
  }
3217
3315
  }
3316
+ /**
3317
+ * 给子 client (agent-runs / compliance) 用的请求超时组合器。
3318
+ *
3319
+ * 返回一个 controller, 其 `signal` 同时受默认/指定超时与外部 `parent` signal 约束 —
3320
+ * 二者任一触发都会 abort (用户传入的 signal 仍然生效)。调用方**必须**在 finally 里
3321
+ * `dispose()` 清掉定时器与监听, 否则 timer 泄漏。
3322
+ *
3323
+ * 仅用于非流式 JSON 请求; 流式 (SSE) / 下载路径不应套短超时 (会切断长连接)。
3324
+ */
3325
+ withRequestTimeout(ms, parent) {
3326
+ return withRequestTimeout(ms, parent);
3327
+ }
3218
3328
  /**
3219
3329
  * fetch 包装 — 错误经 classifyTransport 转 NetworkError
3220
3330
  * 6 处原始 fetch() 全部走此 helper
@@ -4383,6 +4493,9 @@ Client.prototype.updateNotificationPreference = async function(typeCode, pref, s
4383
4493
 
4384
4494
  // src/notifications/ws.ts
4385
4495
  Client.prototype.connect = async function(cfg, signal) {
4496
+ if (this.ws) {
4497
+ await this.disconnect();
4498
+ }
4386
4499
  const noop = () => {
4387
4500
  };
4388
4501
  const filledCfg = {
@@ -4911,7 +5024,10 @@ var AgentRunsClient = class {
4911
5024
  );
4912
5025
  const contentType = resp.headers.get("Content-Type") ?? void 0;
4913
5026
  const filename = filenameFromContentDisposition(resp.headers.get("Content-Disposition")) ?? artifactId;
4914
- const data = await readLimited(resp.body, maxDownloadSize);
5027
+ const data = await readLimited(resp.body, maxDownloadSize + 1);
5028
+ if (data.byteLength > maxDownloadSize) {
5029
+ throw new Error(`download artifact: response exceeds ${maxDownloadSize >> 20}MB limit`);
5030
+ }
4915
5031
  return { data, filename, contentType };
4916
5032
  }
4917
5033
  submitLocalToolResult(runId, result, signal) {
@@ -4967,7 +5083,6 @@ var AgentRunsClient = class {
4967
5083
  };
4968
5084
  }
4969
5085
  const ctl = new AbortController();
4970
- const timer = setTimeout(() => ctl.abort(), timeoutMs);
4971
5086
  let parentAbort;
4972
5087
  if (signal) {
4973
5088
  if (signal.aborted) ctl.abort();
@@ -4976,7 +5091,18 @@ var AgentRunsClient = class {
4976
5091
  signal.addEventListener("abort", parentAbort);
4977
5092
  }
4978
5093
  }
4979
- try {
5094
+ let timer;
5095
+ const timeoutResult = new Promise((resolve) => {
5096
+ timer = setTimeout(() => {
5097
+ ctl.abort();
5098
+ resolve({
5099
+ requestId: event.requestId,
5100
+ ok: false,
5101
+ error: `local tool timed out after ${timeoutMs}ms`
5102
+ });
5103
+ }, timeoutMs);
5104
+ });
5105
+ const handlerTask = (async () => {
4980
5106
  const content = await handler(event.input, {
4981
5107
  runId,
4982
5108
  requestId: event.requestId,
@@ -4984,6 +5110,11 @@ var AgentRunsClient = class {
4984
5110
  signal: ctl.signal
4985
5111
  });
4986
5112
  return { requestId: event.requestId, ok: true, content };
5113
+ })();
5114
+ handlerTask.catch(() => {
5115
+ });
5116
+ try {
5117
+ return await Promise.race([handlerTask, timeoutResult]);
4987
5118
  } catch (e) {
4988
5119
  if (signal?.aborted) throw e;
4989
5120
  const timedOut = ctl.signal.aborted;
@@ -5016,14 +5147,29 @@ var AgentRunsClient = class {
5016
5147
  }
5017
5148
  }
5018
5149
  async requestAPI(method, path, body, signal, opts) {
5019
- const resp = await this.requestRaw(method, path, body, signal, opts);
5150
+ const resp = await this.requestRaw(method, path, body, signal, {
5151
+ ...opts,
5152
+ timeoutMs: opts.timeoutMs ?? DEFAULT_API_TIMEOUT_MS
5153
+ });
5020
5154
  const text = await resp.text();
5155
+ if (!text) return void 0;
5021
5156
  const result = JSON.parse(text);
5022
5157
  const bizErr = apiResponseBusinessError(result);
5023
5158
  if (bizErr) throw bizErr;
5024
5159
  return result.data;
5025
5160
  }
5026
5161
  async requestRaw(method, path, body, signal, opts, retried = false) {
5162
+ if (opts.timeoutMs != null && opts.timeoutMs > 0) {
5163
+ const ctl = this.client.withRequestTimeout(opts.timeoutMs, signal);
5164
+ try {
5165
+ return await this.requestRawInner(method, path, body, ctl.signal, opts, retried);
5166
+ } finally {
5167
+ ctl.dispose();
5168
+ }
5169
+ }
5170
+ return this.requestRawInner(method, path, body, signal, opts, retried);
5171
+ }
5172
+ async requestRawInner(method, path, body, signal, opts, retried) {
5027
5173
  const token = await this.client.ensureToken(signal);
5028
5174
  const url = this.client.apiURL(path);
5029
5175
  const headers = {
@@ -5042,7 +5188,7 @@ var AgentRunsClient = class {
5042
5188
  } catch {
5043
5189
  }
5044
5190
  await this.client.forceRefresh(signal);
5045
- return this.requestRaw(method, path, body, signal, opts, true);
5191
+ return this.requestRawInner(method, path, body, signal, opts, true);
5046
5192
  }
5047
5193
  if (resp.status < 200 || resp.status >= 300) {
5048
5194
  const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
@@ -6374,6 +6520,14 @@ var ComplianceClient = class {
6374
6520
  });
6375
6521
  }
6376
6522
  async executeJson(method, path, body, signal, opts, retried = false) {
6523
+ const ctl = this.client.withRequestTimeout(DEFAULT_API_TIMEOUT_MS, signal);
6524
+ try {
6525
+ return await this.executeJsonInner(method, path, body, ctl.signal, opts, retried);
6526
+ } finally {
6527
+ ctl.dispose();
6528
+ }
6529
+ }
6530
+ async executeJsonInner(method, path, body, signal, opts, retried) {
6377
6531
  const token = await this.client.ensureToken(signal);
6378
6532
  const url = this.client.complianceURL(path);
6379
6533
  const headers = {
@@ -6393,7 +6547,7 @@ var ComplianceClient = class {
6393
6547
  } catch {
6394
6548
  }
6395
6549
  await this.client.forceRefresh(signal);
6396
- return this.executeJson(method, path, body, signal, opts, true);
6550
+ return this.executeJsonInner(method, path, body, signal, opts, true);
6397
6551
  }
6398
6552
  if (resp.status < 200 || resp.status >= 300) {
6399
6553
  const bodyBytes = resp.body ? await readLimited(resp.body, maxErrorBodySize) : new Uint8Array();
@@ -6418,7 +6572,11 @@ var ComplianceClient = class {
6418
6572
  let lastValue;
6419
6573
  while (Date.now() < deadline) {
6420
6574
  if (opts.signal?.aborted) {
6421
- throw new CompliancePollError("compliance poll aborted", "unknown");
6575
+ throw new CompliancePollError(
6576
+ "compliance poll aborted",
6577
+ "unknown",
6578
+ deriveLastInfo(lastValue, false)
6579
+ );
6422
6580
  }
6423
6581
  lastValue = await fetcher();
6424
6582
  const decision = classify(lastValue);
@@ -6426,7 +6584,8 @@ var ComplianceClient = class {
6426
6584
  if (decision === "failed") {
6427
6585
  throw new CompliancePollError(
6428
6586
  "compliance poll observed terminal failure",
6429
- "terminal_failure"
6587
+ "terminal_failure",
6588
+ deriveLastInfo(lastValue, true)
6430
6589
  );
6431
6590
  }
6432
6591
  const sleepMs = Math.min(interval, deadline - Date.now());
@@ -6434,9 +6593,30 @@ var ComplianceClient = class {
6434
6593
  await sleep2(sleepMs, opts.signal);
6435
6594
  interval = Math.min(Math.floor(interval * cfg.multiplier), cfg.maxIntervalMs);
6436
6595
  }
6437
- throw new CompliancePollError("compliance poll timed out", "timeout");
6596
+ throw new CompliancePollError(
6597
+ "compliance poll timed out",
6598
+ "timeout",
6599
+ deriveLastInfo(lastValue, false)
6600
+ );
6438
6601
  }
6439
6602
  };
6603
+ function deriveLastInfo(value, terminal) {
6604
+ if (value == null || typeof value !== "object") return void 0;
6605
+ const v = value;
6606
+ const statusStr = typeof v["status"] === "string" && v["status"] || typeof v["verificationStatus"] === "string" && v["verificationStatus"] || "";
6607
+ const rawCode = v["errorCode"] ?? v["code"];
6608
+ const code = typeof rawCode === "number" ? rawCode : 0;
6609
+ const rawMsg = v["errorMessage"] ?? v["message"];
6610
+ const message = typeof rawMsg === "string" && rawMsg || (statusStr ? `last polled status: ${statusStr}` : "") || "compliance poll: last observed status (no detail)";
6611
+ return {
6612
+ code,
6613
+ message,
6614
+ key: "UNKNOWN_COMPLIANCE_ERROR",
6615
+ retryable: false,
6616
+ terminal,
6617
+ stepUpRequired: false
6618
+ };
6619
+ }
6440
6620
  function classifyTimestamp(status) {
6441
6621
  switch (status) {
6442
6622
  case "VERIFIED":
@@ -6988,6 +7168,7 @@ exports.BillingModeEnum = BillingModeEnum;
6988
7168
  exports.Client = Client;
6989
7169
  exports.ComplianceClient = ComplianceClient;
6990
7170
  exports.CompliancePollError = CompliancePollError;
7171
+ exports.DEFAULT_API_TIMEOUT_MS = DEFAULT_API_TIMEOUT_MS;
6991
7172
  exports.DEFAULT_GATEWAY_BASE_URL = DEFAULT_GATEWAY_BASE_URL;
6992
7173
  exports.DefaultRetryPolicy = DefaultRetryPolicy;
6993
7174
  exports.ErrAuthDenied = ErrAuthDenied;
@@ -7111,6 +7292,7 @@ exports.isRegion = isRegion;
7111
7292
  exports.isSSECommentLine = isSSECommentLine;
7112
7293
  exports.isSSLError = isSSLError;
7113
7294
  exports.isTerminalRemoteEvent = isTerminalRemoteEvent;
7295
+ exports.isValidTokenSet = isValidTokenSet;
7114
7296
  exports.maxEndUserIdLength = maxEndUserIdLength;
7115
7297
  exports.modelScopes = modelScopes;
7116
7298
  exports.modelSupportsImageInput = modelSupportsImageInput;
@@ -7120,6 +7302,7 @@ exports.newThinkingConfig = newThinkingConfig;
7120
7302
  exports.newTokenSet = newTokenSet;
7121
7303
  exports.newWebSearchTool = newWebSearchTool;
7122
7304
  exports.normalizeGatewayBaseURL = normalizeGatewayBaseURL;
7305
+ exports.normalizeOverrideBaseURL = normalizeOverrideBaseURL;
7123
7306
  exports.parseNotificationEvent = parseNotificationEvent;
7124
7307
  exports.parseRemoteControlEvent = parseRemoteControlEvent;
7125
7308
  exports.parseSettlement = parseSettlement;