@bman654/clodex 2.8.4 → 2.9.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/dist/cli.js CHANGED
@@ -382,7 +382,7 @@ import { join } from "path";
382
382
  // package.json
383
383
  var package_default = {
384
384
  name: "@bman654/clodex",
385
- version: "2.8.4",
385
+ version: "2.9.0",
386
386
  publishConfig: {
387
387
  access: "public"
388
388
  },
@@ -3423,7 +3423,7 @@ function savedStopsAfter(current, assignments) {
3423
3423
  }
3424
3424
 
3425
3425
  // src/patch-transforms.ts
3426
- var PATCH_TRANSFORMS_VERSION = 11;
3426
+ var PATCH_TRANSFORMS_VERSION = 12;
3427
3427
  var NATIVE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
3428
3428
  var BASE_EFFORT_LEVELS = ["low", "medium", "high"];
3429
3429
  function projectNativeEffort(effort) {
@@ -3812,7 +3812,7 @@ function applyClodexPatches(source, config) {
3812
3812
  }
3813
3813
  applyOnce(
3814
3814
  patchName,
3815
- /(function [\w$]+\(\)\{)(let (?:[^;{}]|\{[^;{}]*\})*?[\w$]+\(process\.env\.CLAUDE_CODE_REMOTE\)\?(?:(?!\}\s*function )[\s\S])*?\)return process\.env;let ([\w$]+)=\{(?:(?!\}\s*function )[\s\S])*?return \3)(\})/,
3815
+ /(function [\w$]+\(\)\{)(let[ {[](?:[^;{}]|\{[^;{}]*\})*?(?:\{[^;{}]*)?(?:getAgentProxyEnv|[\w$]+\(process\.env\.CLAUDE_CODE_REMOTE\)\?)(?:(?!\}\s*function )[\s\S])*?\)return process\.env;let ([\w$]+)=\{(?:(?!\}\s*function )[\s\S])*?return \3)(\})/,
3816
3816
  (match, head, body, _copyVar, tail) => {
3817
3817
  const at = js.indexOf(match);
3818
3818
  const closingBrace = at < 0 ? -1 : blockEndIndex(js, at + head.length - 1);
@@ -3820,7 +3820,7 @@ function applyClodexPatches(source, config) {
3820
3820
  const bound = braceScanFailed ? 0 : closingBrace - at - (match.length - 1);
3821
3821
  const missingLiteral = requiredBodyLiterals.find((literal) => !body.includes(literal));
3822
3822
  const scrubbedSeen = scrubbedEnvNames.filter((name) => body.includes(name));
3823
- const why = missingLiteral !== void 0 ? "body does not contain " + missingLiteral : scrubbedSeen.length < MIN_SCRUBBED_ENV_NAMES ? "body scrubs only " + scrubbedSeen.length + " of the " + scrubbedEnvNames.length + " known child-env names (expected at least " + MIN_SCRUBBED_ENV_NAMES + ")" : /\bfunction\s*[\w$]*\(/.test(body) ? "body declares a nested function" : braceScanFailed ? "the brace walk never reached the function's closing brace" : bound !== 0 ? "match ends " + bound + " characters from the function it started in" : void 0;
3823
+ const why = missingLiteral !== void 0 ? "body does not contain " + missingLiteral : scrubbedSeen.length < MIN_SCRUBBED_ENV_NAMES ? "body spells only " + scrubbedSeen.length + " of the " + scrubbedEnvNames.length + " known child-env names (expected at least " + MIN_SCRUBBED_ENV_NAMES + ")" : /\bfunction\s*[\w$]*\(/.test(body) ? "body declares a nested function" : braceScanFailed ? "the brace walk never reached the function's closing brace" : bound !== 0 ? "match ends " + Math.abs(bound) + " characters " + (bound > 0 ? "before" : "after") + " the end of the function it started in" : void 0;
3824
3824
  if (why !== void 0) {
3825
3825
  log12("FAIL", patchName, "target validation failed: " + why);
3826
3826
  fail("clodex patch: child network environment target validation failed: " + why);
@@ -7066,7 +7066,7 @@ function resignMachOBinary(path) {
7066
7066
  }
7067
7067
 
7068
7068
  // src/bun-compiled-pointer.ts
7069
- import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, writeSync as writeSync3 } from "fs";
7069
+ import { closeSync as closeSync3, fstatSync, openSync as openSync3, readSync as readSync2, writeSync as writeSync3 } from "fs";
7070
7070
  var TWEAKCC_SCAN_STRIDE = 16384n;
7071
7071
  var SCAN_CHUNK = 1 << 20;
7072
7072
  var ELF_MAGIC = 1179403647;
@@ -7098,6 +7098,9 @@ function readElfLayout(fd) {
7098
7098
  const shstrndx = ident.readUInt16LE(62);
7099
7099
  if (shnum === 0 || phnum === 65535) return null;
7100
7100
  if (shstrndx >= shnum || shentsize < 64 || phentsize < 56) return null;
7101
+ const fileSize = fstatSync(fd).size;
7102
+ const fits = (offset, length) => Number.isSafeInteger(offset) && offset >= 0 && length >= 0 && offset + length <= fileSize;
7103
+ if (!fits(Number(shoff), shnum * shentsize)) return null;
7101
7104
  const shTable = readAt2(fd, shnum * shentsize, Number(shoff));
7102
7105
  if (shTable.length !== shnum * shentsize) return null;
7103
7106
  const raw = [];
@@ -7111,6 +7114,7 @@ function readElfLayout(fd) {
7111
7114
  });
7112
7115
  }
7113
7116
  const strtab = raw[shstrndx];
7117
+ if (!fits(Number(strtab.offset), Number(strtab.size))) return null;
7114
7118
  const names = readAt2(fd, Number(strtab.size), Number(strtab.offset));
7115
7119
  const sections = raw.map((section) => {
7116
7120
  const start = section.nameOffset;
@@ -7123,6 +7127,7 @@ function readElfLayout(fd) {
7123
7127
  size: section.size
7124
7128
  };
7125
7129
  });
7130
+ if (!fits(Number(phoff), phnum * phentsize)) return null;
7126
7131
  const phTable = readAt2(fd, phnum * phentsize, Number(phoff));
7127
7132
  if (phTable.length !== phnum * phentsize) return null;
7128
7133
  const segments = [];
@@ -7192,7 +7197,6 @@ function shimBunCompiledPointer(path) {
7192
7197
  if (!segment) return null;
7193
7198
  const needle = Buffer.alloc(8);
7194
7199
  needle.writeBigUInt64LE(bun.addr);
7195
- if (tweakccWouldFind(fd, segment, needle) !== null) return null;
7196
7200
  const segmentStart = Number(segment.offset);
7197
7201
  const segmentEnd = Number(segment.offset + segment.filesz);
7198
7202
  const bunStart = Number(bun.offset);
@@ -7205,6 +7209,15 @@ function shimBunCompiledPointer(path) {
7205
7209
  }
7206
7210
  const pointerOffset = candidates[0];
7207
7211
  const pointerVaddr = segment.vaddr + BigInt(pointerOffset) - segment.offset;
7212
+ const wouldFind = tweakccWouldFind(fd, segment, needle);
7213
+ if (wouldFind !== null) {
7214
+ if (wouldFind !== pointerVaddr) {
7215
+ throw new Error(
7216
+ `tweakcc's ELF repack would rewrite 0x${wouldFind.toString(16)}, which is not Bun's blob pointer at 0x${pointerVaddr.toString(16)}. Refusing to repack a binary whose Bun global would be left stale.`
7217
+ );
7218
+ }
7219
+ return null;
7220
+ }
7208
7221
  const first = alignUp(segment.vaddr, TWEAKCC_SCAN_STRIDE);
7209
7222
  const last = segment.vaddr + segment.filesz - 8n;
7210
7223
  let standInVaddr = null;
@@ -7241,7 +7254,11 @@ function restoreBunCompiledPointer(path, shim) {
7241
7254
  "the repack left Bun's blob pointer outside the loaded image, or in more than one segment of it"
7242
7255
  );
7243
7256
  }
7244
- const written = readAt2(fd, 8, standInOffset).readBigUInt64LE(0);
7257
+ const standIn = readAt2(fd, 8, standInOffset);
7258
+ if (standIn.length !== 8) {
7259
+ throw new Error("the repacked binary ends before the stand-in for Bun's blob pointer");
7260
+ }
7261
+ const written = standIn.readBigUInt64LE(0);
7245
7262
  if (written === shim.bunVaddr) {
7246
7263
  throw new Error("the repack did not rewrite Bun's blob pointer \u2014 the stand-in was not used");
7247
7264
  }
@@ -10911,43 +10928,152 @@ function resolveUpstreamTools(tools, messages) {
10911
10928
 
10912
10929
  // src/upstream-retry.ts
10913
10930
  var UPSTREAM_MAX_RETRIES_ENV = "CLODEX_UPSTREAM_MAX_RETRIES";
10914
- var MAX_UPSTREAM_MAX_RETRIES = 5;
10931
+ var UPSTREAM_IDLE_TIMEOUT_ENV = "CLODEX_UPSTREAM_IDLE_TIMEOUT_MS";
10932
+ var UPSTREAM_TOTAL_TIMEOUT_ENV = "CLODEX_UPSTREAM_TOTAL_TIMEOUT_MS";
10933
+ var DEFAULT_UPSTREAM_IDLE_TIMEOUT_MS = 12e4;
10934
+ var DEFAULT_UPSTREAM_TOTAL_TIMEOUT_MS = 10 * 6e4;
10935
+ var MIN_UPSTREAM_IDLE_TIMEOUT_MS = 1e4;
10936
+ var MAX_UPSTREAM_IDLE_TIMEOUT_MS = 60 * 6e4;
10937
+ var MIN_UPSTREAM_TOTAL_TIMEOUT_MS = 6e4;
10938
+ var MAX_UPSTREAM_TOTAL_TIMEOUT_MS = 6 * 60 * 6e4;
10939
+ var SDK_INITIAL_RETRY_DELAY_MS = 2e3;
10915
10940
  var reportedValues = /* @__PURE__ */ new Set();
10916
- function reportOnce(raw, message, warn) {
10917
- if (reportedValues.has(raw)) return;
10918
- reportedValues.add(raw);
10941
+ var defaultWarn = (message) => emitParentNotice(`clodex: ${message}`);
10942
+ function reportOnce(key, message, warn) {
10943
+ if (reportedValues.has(key)) return;
10944
+ reportedValues.add(key);
10919
10945
  try {
10920
10946
  warn(message);
10921
10947
  } catch {
10922
10948
  }
10923
10949
  }
10924
- function upstreamMaxRetries(env = process.env, warn = (message) => emitParentNotice(`clodex: ${message}`)) {
10950
+ function timeoutSetting(env, envName, fallback, min, max, warn) {
10951
+ const raw = env[envName]?.trim();
10952
+ if (raw === void 0 || raw === "") return { value: fallback, explicit: false };
10953
+ const value = Number(raw);
10954
+ if (!Number.isInteger(value) || value <= 0) {
10955
+ reportOnce(
10956
+ `${envName}=${raw}`,
10957
+ `ignoring ${envName}=${raw} (expected a positive integer number of milliseconds)`,
10958
+ warn
10959
+ );
10960
+ return { value: fallback, explicit: false };
10961
+ }
10962
+ if (value < min || value > max) {
10963
+ const clamped = Math.min(max, Math.max(min, value));
10964
+ reportOnce(
10965
+ `${envName}=${raw}`,
10966
+ `clamping ${envName}=${raw} to ${clamped}ms (supported range is ${min}-${max}ms)`,
10967
+ warn
10968
+ );
10969
+ return { value: clamped, explicit: true };
10970
+ }
10971
+ return { value, explicit: true };
10972
+ }
10973
+ function maxRetriesForIdleTimeout(idleTimeoutMs) {
10974
+ let retries = 0;
10975
+ let elapsedMs = 0;
10976
+ let delayMs = SDK_INITIAL_RETRY_DELAY_MS;
10977
+ while (elapsedMs + delayMs < idleTimeoutMs) {
10978
+ elapsedMs += delayMs;
10979
+ delayMs *= 2;
10980
+ retries += 1;
10981
+ }
10982
+ return retries;
10983
+ }
10984
+ var MAX_UPSTREAM_MAX_RETRIES = maxRetriesForIdleTimeout(
10985
+ DEFAULT_UPSTREAM_IDLE_TIMEOUT_MS
10986
+ );
10987
+ var DEFAULT_UPSTREAM_MAX_RETRIES = MAX_UPSTREAM_MAX_RETRIES;
10988
+ function configuredUpstreamMaxRetries(env, warn) {
10925
10989
  const raw = env[UPSTREAM_MAX_RETRIES_ENV]?.trim();
10926
10990
  if (raw === void 0 || raw === "") return void 0;
10927
10991
  const value = Number(raw);
10928
10992
  if (!Number.isInteger(value) || value < 0) {
10929
10993
  reportOnce(
10930
- raw,
10994
+ `${UPSTREAM_MAX_RETRIES_ENV}=${raw}`,
10931
10995
  `ignoring ${UPSTREAM_MAX_RETRIES_ENV}=${raw} (expected a non-negative integer)`,
10932
10996
  warn
10933
10997
  );
10934
10998
  return void 0;
10935
10999
  }
10936
- if (value > MAX_UPSTREAM_MAX_RETRIES) {
11000
+ return value;
11001
+ }
11002
+ function resolveUpstreamMaxRetries(env, warn, idleTimeoutMs) {
11003
+ const ceiling = maxRetriesForIdleTimeout(idleTimeoutMs);
11004
+ const defaultRetries = Math.min(DEFAULT_UPSTREAM_MAX_RETRIES, ceiling);
11005
+ const value = configuredUpstreamMaxRetries(env, warn);
11006
+ if (value === void 0) return defaultRetries;
11007
+ if (value > ceiling) {
10937
11008
  reportOnce(
10938
- raw,
10939
- `clamping ${UPSTREAM_MAX_RETRIES_ENV}=${raw} to ${MAX_UPSTREAM_MAX_RETRIES} (higher values exceed the 120s streaming idle budget)`,
11009
+ `${UPSTREAM_MAX_RETRIES_ENV}=${value}:idle=${idleTimeoutMs}`,
11010
+ `clamping ${UPSTREAM_MAX_RETRIES_ENV}=${value} to ${ceiling} (estimated from the SDK fallback backoff and resolved ${idleTimeoutMs}ms idle timeout; provider delays may allow fewer retries)`,
10940
11011
  warn
10941
11012
  );
10942
- return MAX_UPSTREAM_MAX_RETRIES;
11013
+ return ceiling;
10943
11014
  }
10944
11015
  return value;
10945
11016
  }
11017
+ function upstreamRequestBudget(options = {}) {
11018
+ const env = options.env ?? process.env;
11019
+ const warn = options.warn ?? defaultWarn;
11020
+ const hasIdleOverride = options.idleTimeoutMs !== void 0;
11021
+ const configuredIdle = options.idleTimeoutMs !== void 0 ? { value: options.idleTimeoutMs, explicit: false } : timeoutSetting(
11022
+ env,
11023
+ UPSTREAM_IDLE_TIMEOUT_ENV,
11024
+ DEFAULT_UPSTREAM_IDLE_TIMEOUT_MS,
11025
+ MIN_UPSTREAM_IDLE_TIMEOUT_MS,
11026
+ MAX_UPSTREAM_IDLE_TIMEOUT_MS,
11027
+ warn
11028
+ );
11029
+ const configuredTotal = timeoutSetting(
11030
+ env,
11031
+ UPSTREAM_TOTAL_TIMEOUT_ENV,
11032
+ DEFAULT_UPSTREAM_TOTAL_TIMEOUT_MS,
11033
+ MIN_UPSTREAM_TOTAL_TIMEOUT_MS,
11034
+ MAX_UPSTREAM_TOTAL_TIMEOUT_MS,
11035
+ warn
11036
+ );
11037
+ let idleTimeoutMs = configuredIdle.value;
11038
+ let totalTimeoutMs = configuredTotal.value;
11039
+ if (totalTimeoutMs < idleTimeoutMs) {
11040
+ if (hasIdleOverride) {
11041
+ idleTimeoutMs = totalTimeoutMs;
11042
+ } else if (configuredIdle.explicit && !configuredTotal.explicit) {
11043
+ reportOnce(
11044
+ `timeout-pair:raise-total:${idleTimeoutMs}:${totalTimeoutMs}`,
11045
+ `raising the resolved total timeout from ${totalTimeoutMs}ms to ${idleTimeoutMs}ms so it is not shorter than ${UPSTREAM_IDLE_TIMEOUT_ENV}`,
11046
+ warn
11047
+ );
11048
+ totalTimeoutMs = idleTimeoutMs;
11049
+ } else {
11050
+ reportOnce(
11051
+ `timeout-pair:lower-idle:${idleTimeoutMs}:${totalTimeoutMs}`,
11052
+ `lowering the resolved idle timeout from ${idleTimeoutMs}ms to ${totalTimeoutMs}ms because it cannot exceed ${UPSTREAM_TOTAL_TIMEOUT_ENV}`,
11053
+ warn
11054
+ );
11055
+ idleTimeoutMs = totalTimeoutMs;
11056
+ }
11057
+ }
11058
+ return {
11059
+ idleTimeoutMs,
11060
+ totalTimeoutMs,
11061
+ maxRetries: resolveUpstreamMaxRetries(env, warn, idleTimeoutMs)
11062
+ };
11063
+ }
10946
11064
  var CLIENT_MAX_RETRIES_ENV = "CLAUDE_CODE_MAX_RETRIES";
10947
11065
  var DEFAULT_PASSTHROUGH_RETRIES = 1;
10948
- function passthroughUpstreamRetries(env = process.env) {
10949
- const explicit = upstreamMaxRetries(env);
10950
- if (explicit !== void 0) return explicit;
11066
+ function passthroughUpstreamRetries(env = process.env, warn = defaultWarn) {
11067
+ const explicit = configuredUpstreamMaxRetries(env, warn);
11068
+ if (explicit !== void 0) {
11069
+ if (explicit <= MAX_UPSTREAM_MAX_RETRIES) return explicit;
11070
+ reportOnce(
11071
+ `${UPSTREAM_MAX_RETRIES_ENV}=${explicit}:passthrough`,
11072
+ `clamping ${UPSTREAM_MAX_RETRIES_ENV}=${explicit} to ${MAX_UPSTREAM_MAX_RETRIES} (the raw HTTP MITM path supports at most this many replays)`,
11073
+ warn
11074
+ );
11075
+ return MAX_UPSTREAM_MAX_RETRIES;
11076
+ }
10951
11077
  const raw = env[CLIENT_MAX_RETRIES_ENV]?.trim();
10952
11078
  if (raw !== void 0 && raw !== "") {
10953
11079
  const clientRetries = Number(raw);
@@ -10956,6 +11082,49 @@ function passthroughUpstreamRetries(env = process.env) {
10956
11082
  return DEFAULT_PASSTHROUGH_RETRIES;
10957
11083
  }
10958
11084
 
11085
+ // src/upstream-attempts.ts
11086
+ import { RetryError as RetryError2, wrapLanguageModel as wrapLanguageModel2 } from "ai";
11087
+ function trackUpstreamAttempts(model) {
11088
+ if (typeof model === "string") {
11089
+ return { model, deadlineError: (timeoutError) => timeoutError };
11090
+ }
11091
+ const failedAttempts = [];
11092
+ let waitingToRetry = false;
11093
+ const track = async (call) => {
11094
+ waitingToRetry = false;
11095
+ try {
11096
+ const result = await call();
11097
+ failedAttempts.length = 0;
11098
+ return result;
11099
+ } catch (error) {
11100
+ failedAttempts.push(error);
11101
+ waitingToRetry = true;
11102
+ throw error;
11103
+ }
11104
+ };
11105
+ const middleware = {
11106
+ specificationVersion: "v4",
11107
+ wrapGenerate: ({ doGenerate }) => track(doGenerate),
11108
+ wrapStream: ({ doStream }) => track(doStream)
11109
+ };
11110
+ return {
11111
+ model: wrapLanguageModel2({ model, middleware }),
11112
+ deadlineError: (timeoutError) => {
11113
+ if (!waitingToRetry || failedAttempts.length === 0) return timeoutError;
11114
+ const count = failedAttempts.length;
11115
+ return new RetryError2({
11116
+ message: [
11117
+ "Provider retry interrupted by a request deadline after",
11118
+ count,
11119
+ `failed ${count === 1 ? "attempt" : "attempts"}`
11120
+ ].join(" "),
11121
+ reason: "abort",
11122
+ errors: [...failedAttempts]
11123
+ });
11124
+ }
11125
+ };
11126
+ }
11127
+
10959
11128
  // src/sdk-adapter.ts
10960
11129
  function sdkTranslationErrorSignature(error) {
10961
11130
  const message = error instanceof Error ? error.message : typeof error === "string" ? error : void 0;
@@ -11330,8 +11499,6 @@ function toAnthropicUsage(u) {
11330
11499
  cache_read_input_tokens: cacheRead
11331
11500
  };
11332
11501
  }
11333
- var SDK_STREAM_IDLE_TIMEOUT_MS = 12e4;
11334
- var SDK_TOTAL_TIMEOUT_MS = 10 * 6e4;
11335
11502
  function streamAbortError(signal) {
11336
11503
  if (signal?.reason instanceof Error) return signal.reason;
11337
11504
  const error = new Error(
@@ -11552,42 +11719,44 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
11552
11719
  emit("message_stop", { type: "message_stop" });
11553
11720
  }
11554
11721
  async function streamAnthropicResponse(model, params, modelId, write, log12, observer) {
11555
- const idleTimeoutMs = observer?.idleTimeoutMs ?? SDK_STREAM_IDLE_TIMEOUT_MS;
11722
+ const { idleTimeoutMs, totalTimeoutMs, maxRetries } = upstreamRequestBudget({
11723
+ idleTimeoutMs: observer?.idleTimeoutMs
11724
+ });
11725
+ const attempts = trackUpstreamAttempts(model);
11556
11726
  const idleAbort = new AbortController();
11557
11727
  const stopForwardingAbort = forwardAbortSignal(observer?.abortSignal, idleAbort);
11558
11728
  const abortSignal = idleAbort.signal;
11559
- let idleTimer = setTimeout(
11560
- () => idleAbort.abort(new Error(`no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`)),
11561
- idleTimeoutMs
11729
+ const idleError = () => attempts.deadlineError(
11730
+ new Error(`no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`)
11562
11731
  );
11732
+ let idleTimer = setTimeout(() => idleAbort.abort(idleError()), idleTimeoutMs);
11563
11733
  const totalTimer = setTimeout(
11564
- () => idleAbort.abort(new Error(`provider stream exceeded ${Math.round(SDK_TOTAL_TIMEOUT_MS / 1e3)}s`)),
11565
- SDK_TOTAL_TIMEOUT_MS
11734
+ () => idleAbort.abort(attempts.deadlineError(
11735
+ new Error(`provider stream exceeded ${Math.round(totalTimeoutMs / 1e3)}s`)
11736
+ )),
11737
+ totalTimeoutMs
11566
11738
  );
11567
- const result = streamText({
11568
- model,
11569
- ...params,
11570
- maxRetries: upstreamMaxRetries(),
11571
- abortSignal,
11572
- onError: () => {
11573
- },
11574
- onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
11575
- });
11576
- const watchedStream = (async function* () {
11577
- try {
11578
- for await (const part of result.stream) {
11739
+ try {
11740
+ const result = streamText({
11741
+ model: attempts.model,
11742
+ ...params,
11743
+ maxRetries,
11744
+ abortSignal,
11745
+ onError: () => {
11746
+ },
11747
+ onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
11748
+ });
11749
+ const watchedStream = (async function* () {
11750
+ try {
11751
+ for await (const part of result.stream) {
11752
+ clearTimeout(idleTimer);
11753
+ idleTimer = setTimeout(() => idleAbort.abort(idleError()), idleTimeoutMs);
11754
+ yield part;
11755
+ }
11756
+ } finally {
11579
11757
  clearTimeout(idleTimer);
11580
- idleTimer = setTimeout(
11581
- () => idleAbort.abort(new Error(`no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`)),
11582
- idleTimeoutMs
11583
- );
11584
- yield part;
11585
11758
  }
11586
- } finally {
11587
- clearTimeout(idleTimer);
11588
- }
11589
- })();
11590
- try {
11759
+ })();
11591
11760
  await writeAnthropicStream(watchedStream, modelId, write, log12, { ...observer, abortSignal }, params.tools);
11592
11761
  } finally {
11593
11762
  stopForwardingAbort();
@@ -11602,39 +11771,41 @@ async function generateAnthropicResponse(model, params, modelId, options) {
11602
11771
  let finishReason;
11603
11772
  let usage;
11604
11773
  let warnings;
11774
+ const { idleTimeoutMs, totalTimeoutMs, maxRetries } = upstreamRequestBudget({
11775
+ idleTimeoutMs: options?.forceStream ? options.idleTimeoutMs : void 0
11776
+ });
11777
+ const attempts = trackUpstreamAttempts(model);
11605
11778
  if (options?.forceStream) {
11606
11779
  const forceAbort = new AbortController();
11607
11780
  const stopForwardingAbort = forwardAbortSignal(options.abortSignal, forceAbort);
11608
11781
  const abortSignal = forceAbort.signal;
11609
- const idleTimeoutMs = options.idleTimeoutMs ?? SDK_STREAM_IDLE_TIMEOUT_MS;
11610
- let idleTimer = setTimeout(
11611
- () => forceAbort.abort(new Error(`no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`)),
11612
- idleTimeoutMs
11782
+ const idleError = () => attempts.deadlineError(
11783
+ new Error(`no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`)
11613
11784
  );
11785
+ let idleTimer = setTimeout(() => forceAbort.abort(idleError()), idleTimeoutMs);
11614
11786
  const totalTimer = setTimeout(
11615
- () => forceAbort.abort(new Error(`provider stream exceeded ${Math.round(SDK_TOTAL_TIMEOUT_MS / 1e3)}s`)),
11616
- SDK_TOTAL_TIMEOUT_MS
11787
+ () => forceAbort.abort(attempts.deadlineError(
11788
+ new Error(`provider stream exceeded ${Math.round(totalTimeoutMs / 1e3)}s`)
11789
+ )),
11790
+ totalTimeoutMs
11617
11791
  );
11618
- const r = streamText({
11619
- model,
11620
- ...params,
11621
- maxRetries: upstreamMaxRetries(),
11622
- abortSignal,
11623
- onError: () => {
11624
- },
11625
- onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
11626
- });
11627
11792
  const streamedText = [];
11628
11793
  const streamedToolCalls = [];
11629
11794
  let streamedFinishReason = "stop";
11630
11795
  let streamedUsage;
11631
11796
  try {
11797
+ const r = streamText({
11798
+ model: attempts.model,
11799
+ ...params,
11800
+ maxRetries,
11801
+ abortSignal,
11802
+ onError: () => {
11803
+ },
11804
+ onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
11805
+ });
11632
11806
  for await (const part of r.stream) {
11633
11807
  clearTimeout(idleTimer);
11634
- idleTimer = setTimeout(
11635
- () => forceAbort.abort(new Error(`no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`)),
11636
- idleTimeoutMs
11637
- );
11808
+ idleTimer = setTimeout(() => forceAbort.abort(idleError()), idleTimeoutMs);
11638
11809
  options.onPart?.(part.type);
11639
11810
  if (abortSignal.aborted || part.type === "abort") {
11640
11811
  throw streamAbortError(abortSignal);
@@ -11669,17 +11840,22 @@ async function generateAnthropicResponse(model, params, modelId, options) {
11669
11840
  const generateAbort = new AbortController();
11670
11841
  const stopForwardingAbort = forwardAbortSignal(options?.abortSignal, generateAbort);
11671
11842
  const totalTimer = setTimeout(
11672
- () => generateAbort.abort(new Error(`provider request exceeded ${Math.round(SDK_TOTAL_TIMEOUT_MS / 1e3)}s`)),
11673
- SDK_TOTAL_TIMEOUT_MS
11843
+ () => generateAbort.abort(attempts.deadlineError(
11844
+ new Error(`provider request exceeded ${Math.round(totalTimeoutMs / 1e3)}s`)
11845
+ )),
11846
+ totalTimeoutMs
11674
11847
  );
11675
11848
  try {
11676
11849
  const r = await generateText({
11677
- model,
11850
+ model: attempts.model,
11678
11851
  ...params,
11679
- maxRetries: upstreamMaxRetries(),
11852
+ maxRetries,
11680
11853
  abortSignal: generateAbort.signal
11681
11854
  });
11682
11855
  ({ text: text5, toolCalls, finishReason, usage, warnings } = r);
11856
+ } catch (error) {
11857
+ if (generateAbort.signal.aborted) throw streamAbortError(generateAbort.signal);
11858
+ throw error;
11683
11859
  } finally {
11684
11860
  stopForwardingAbort();
11685
11861
  clearTimeout(totalTimer);
@@ -13516,7 +13692,7 @@ import { randomUUID as randomUUID4 } from "crypto";
13516
13692
  import {
13517
13693
  closeSync as closeSync7,
13518
13694
  existsSync as existsSync6,
13519
- fstatSync,
13695
+ fstatSync as fstatSync2,
13520
13696
  fsyncSync as fsyncSync2,
13521
13697
  lstatSync,
13522
13698
  mkdirSync as mkdirSync6,
@@ -13613,7 +13789,7 @@ function readJournalUnlocked(path) {
13613
13789
  throw new Error("Credential cleanup journal must be a regular file.");
13614
13790
  }
13615
13791
  fd = openSync7(path, "r");
13616
- const opened = fstatSync(fd);
13792
+ const opened = fstatSync2(fd);
13617
13793
  if (before.dev !== opened.dev || before.ino !== opened.ino) {
13618
13794
  throw new Error("Credential cleanup journal changed while opening.");
13619
13795
  }
@@ -16653,24 +16829,81 @@ async function collectOpenAiStream(stream) {
16653
16829
  }
16654
16830
  return collected;
16655
16831
  }
16832
+ function startUpstreamBudget(model, streaming) {
16833
+ const { idleTimeoutMs, totalTimeoutMs, maxRetries } = upstreamRequestBudget();
16834
+ const attempts = trackUpstreamAttempts(model);
16835
+ const abort = new AbortController();
16836
+ const idleError = () => attempts.deadlineError(new Error(
16837
+ `no data received from provider for ${Math.round(idleTimeoutMs / 1e3)}s`
16838
+ ));
16839
+ let idleTimer = streaming ? setTimeout(() => abort.abort(idleError()), idleTimeoutMs) : void 0;
16840
+ const totalTimer = setTimeout(
16841
+ () => abort.abort(attempts.deadlineError(new Error(
16842
+ `provider ${streaming ? "stream" : "request"} exceeded ${Math.round(totalTimeoutMs / 1e3)}s`
16843
+ ))),
16844
+ totalTimeoutMs
16845
+ );
16846
+ return {
16847
+ model: attempts.model,
16848
+ maxRetries,
16849
+ abortSignal: abort.signal,
16850
+ onStreamPart: () => {
16851
+ if (idleTimer === void 0) return;
16852
+ clearTimeout(idleTimer);
16853
+ idleTimer = setTimeout(() => abort.abort(idleError()), idleTimeoutMs);
16854
+ },
16855
+ close: () => {
16856
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
16857
+ clearTimeout(totalTimer);
16858
+ if (!abort.signal.aborted) abort.abort();
16859
+ }
16860
+ };
16861
+ }
16862
+ async function* watchOpenAiStream(stream, budget) {
16863
+ for await (const part of stream) {
16864
+ if (budget.abortSignal.aborted || part.type === "abort") {
16865
+ throw budget.abortSignal.reason instanceof Error ? budget.abortSignal.reason : new Error("SDK stream aborted");
16866
+ }
16867
+ budget.onStreamPart();
16868
+ yield part;
16869
+ }
16870
+ if (budget.abortSignal.aborted) {
16871
+ throw budget.abortSignal.reason instanceof Error ? budget.abortSignal.reason : new Error("SDK stream aborted");
16872
+ }
16873
+ }
16656
16874
  async function generateOpenAiResponse(model, params, responseModelId, options) {
16657
16875
  let result;
16658
- if (options?.forceStream) {
16659
- const { stream } = streamText2({
16660
- model,
16661
- ...params,
16662
- maxRetries: upstreamMaxRetries(),
16663
- onError: () => {
16664
- },
16665
- onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
16666
- });
16667
- result = await collectOpenAiStream(stream);
16668
- } else {
16669
- result = await generateText2({
16670
- model,
16671
- ...params,
16672
- maxRetries: upstreamMaxRetries()
16673
- });
16876
+ const streaming = options?.forceStream === true;
16877
+ const budget = startUpstreamBudget(model, streaming);
16878
+ try {
16879
+ if (streaming) {
16880
+ const { stream } = streamText2({
16881
+ model: budget.model,
16882
+ ...params,
16883
+ maxRetries: budget.maxRetries,
16884
+ abortSignal: budget.abortSignal,
16885
+ onError: () => {
16886
+ },
16887
+ onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
16888
+ });
16889
+ result = await collectOpenAiStream(watchOpenAiStream(stream, budget));
16890
+ } else {
16891
+ try {
16892
+ result = await generateText2({
16893
+ model: budget.model,
16894
+ ...params,
16895
+ maxRetries: budget.maxRetries,
16896
+ abortSignal: budget.abortSignal
16897
+ });
16898
+ } catch (error) {
16899
+ if (budget.abortSignal.aborted && budget.abortSignal.reason instanceof Error) {
16900
+ throw budget.abortSignal.reason;
16901
+ }
16902
+ throw error;
16903
+ }
16904
+ }
16905
+ } finally {
16906
+ budget.close();
16674
16907
  }
16675
16908
  reportUnsupportedServiceTier(params, result.warnings);
16676
16909
  const message = { role: "assistant", content: result.text || null };
@@ -16695,41 +16928,47 @@ async function generateOpenAiResponse(model, params, responseModelId, options) {
16695
16928
  };
16696
16929
  }
16697
16930
  async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
16698
- const { stream } = streamText2({
16699
- model,
16700
- ...params,
16701
- maxRetries: upstreamMaxRetries(),
16702
- onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
16703
- });
16704
- const baseData = {
16705
- id: `chatcmpl-${Date.now()}`,
16706
- object: "chat.completion.chunk",
16707
- created: Math.floor(Date.now() / 1e3),
16708
- model: responseModelId
16709
- };
16710
- const send = (delta, finish_reason = null) => onChunk(`data: ${JSON.stringify({ ...baseData, choices: [{ index: 0, delta, finish_reason }] })}
16931
+ const budget = startUpstreamBudget(model, true);
16932
+ try {
16933
+ const { stream } = streamText2({
16934
+ model: budget.model,
16935
+ ...params,
16936
+ maxRetries: budget.maxRetries,
16937
+ abortSignal: budget.abortSignal,
16938
+ onStepFinish: (step) => reportUnsupportedServiceTier(params, step.warnings)
16939
+ });
16940
+ const baseData = {
16941
+ id: `chatcmpl-${Date.now()}`,
16942
+ object: "chat.completion.chunk",
16943
+ created: Math.floor(Date.now() / 1e3),
16944
+ model: responseModelId
16945
+ };
16946
+ const send = (delta, finish_reason = null) => onChunk(`data: ${JSON.stringify({ ...baseData, choices: [{ index: 0, delta, finish_reason }] })}
16711
16947
 
16712
16948
  `);
16713
- for await (const part of stream) {
16714
- const p13 = part;
16715
- switch (p13.type) {
16716
- case "text-delta":
16717
- send({ role: "assistant", content: p13.textDelta ?? p13.text ?? "" });
16718
- break;
16719
- case "tool-input-start":
16720
- send({ role: "assistant", tool_calls: [{ index: 0, id: p13.id ?? p13.toolCallId, type: "function", function: { name: p13.toolName, arguments: "" } }] });
16721
- break;
16722
- case "tool-input-delta":
16723
- send({ tool_calls: [{ index: 0, function: { arguments: p13.delta ?? p13.text ?? p13.argsTextDelta ?? "" } }] });
16724
- break;
16725
- case "finish":
16726
- send({}, p13.finishReason || "stop");
16727
- break;
16728
- case "error":
16729
- throw p13.error instanceof Error || p13.error && typeof p13.error === "object" ? p13.error : new Error(typeof p13.error === "string" ? p13.error : "Upstream stream failed");
16949
+ for await (const part of watchOpenAiStream(stream, budget)) {
16950
+ const p13 = part;
16951
+ switch (p13.type) {
16952
+ case "text-delta":
16953
+ send({ role: "assistant", content: p13.textDelta ?? p13.text ?? "" });
16954
+ break;
16955
+ case "tool-input-start":
16956
+ send({ role: "assistant", tool_calls: [{ index: 0, id: p13.id ?? p13.toolCallId, type: "function", function: { name: p13.toolName, arguments: "" } }] });
16957
+ break;
16958
+ case "tool-input-delta":
16959
+ send({ tool_calls: [{ index: 0, function: { arguments: p13.delta ?? p13.text ?? p13.argsTextDelta ?? "" } }] });
16960
+ break;
16961
+ case "finish":
16962
+ send({}, p13.finishReason || "stop");
16963
+ break;
16964
+ case "error":
16965
+ throw p13.error instanceof Error || p13.error && typeof p13.error === "object" ? p13.error : new Error(typeof p13.error === "string" ? p13.error : "Upstream stream failed");
16966
+ }
16730
16967
  }
16968
+ onChunk("data: [DONE]\n\n");
16969
+ } finally {
16970
+ budget.close();
16731
16971
  }
16732
- onChunk("data: [DONE]\n\n");
16733
16972
  }
16734
16973
 
16735
16974
  // src/server/router.ts