@verboo/code 0.15.16 → 0.15.17

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +987 -452
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -17973,7 +17973,7 @@ function getOauthConfig() {
17973
17973
  }
17974
17974
  return config;
17975
17975
  }
17976
- var CLAUDE_AI_INFERENCE_SCOPE = "user:inference", CLAUDE_AI_PROFILE_SCOPE = "user:profile", CONSOLE_SCOPE = "org:create_api_key", OAUTH_BETA_HEADER = "oauth-2025-04-20", CONSOLE_OAUTH_SCOPES, CLAUDE_AI_OAUTH_SCOPES, ALL_OAUTH_SCOPES, VERBOO_API_BASE_URL = "https://code.verboo.ai", VERBOO_FRONT_BASE_URL = "https://code.verboo.ai", VERBOO_ROUTER_URL = "https://code.verboo.ai/router/v1", VERBOO_OAUTH_SCOPES, PROD_OAUTH_CONFIG, MCP_CLIENT_METADATA_URL = "https://claude.ai/oauth/claude-code-client-metadata", STAGING_OAUTH_CONFIG, ALLOWED_OAUTH_BASE_URLS;
17976
+ var CLAUDE_AI_INFERENCE_SCOPE = "user:inference", CLAUDE_AI_PROFILE_SCOPE = "user:profile", CONSOLE_SCOPE = "org:create_api_key", OAUTH_BETA_HEADER = "oauth-2025-04-20", CONSOLE_OAUTH_SCOPES, CLAUDE_AI_OAUTH_SCOPES, ALL_OAUTH_SCOPES, VERBOO_API_BASE_URL = "https://code.verboo.ai", VERBOO_FRONT_BASE_URL = "https://code.verboo.ai", VERBOO_ROUTER_URL = "https://code.verboo.ai/router/v1", VERBOO_OAUTH_SCOPES, PROD_OAUTH_CONFIG, MCP_CLIENT_METADATA_URL = "https://code.verboo.ai/.well-known/oauth-client/verboo-code", STAGING_OAUTH_CONFIG, ALLOWED_OAUTH_BASE_URLS;
17977
17977
  var init_oauth = __esm(() => {
17978
17978
  init_envUtils();
17979
17979
  CONSOLE_OAUTH_SCOPES = [
@@ -118686,7 +118686,7 @@ function getClaudeCodeUserAgent() {
118686
118686
  return `claude-code/${"99.0.0"}`;
118687
118687
  }
118688
118688
  function getVerbooCodeUserAgent() {
118689
- const version2 = "0.15.16";
118689
+ const version2 = "0.15.17";
118690
118690
  return `verboo-code/${version2}`;
118691
118691
  }
118692
118692
 
@@ -152401,9 +152401,17 @@ function resolveToolNameByUniquePrefix(toolNames, name) {
152401
152401
  const uniqueToolNames = [...new Set(toolNames)];
152402
152402
  if (uniqueToolNames.includes(name))
152403
152403
  return name;
152404
- if (name.length < 3 || name.startsWith("mcp__"))
152404
+ const caseInsensitiveExactMatches = uniqueToolNames.filter((toolName) => toolName.toLowerCase() === name.toLowerCase());
152405
+ if (caseInsensitiveExactMatches.length === 1) {
152406
+ return caseInsensitiveExactMatches[0];
152407
+ }
152408
+ const normalizedName = name.toLowerCase();
152409
+ if (name.length < 3)
152405
152410
  return;
152406
- const prefixMatches = uniqueToolNames.filter((toolName) => toolName.startsWith(name));
152411
+ const prefixMatches = uniqueToolNames.filter((toolName) => {
152412
+ const normalizedToolName = toolName.toLowerCase();
152413
+ return !normalizedToolName.startsWith("mcp__") && normalizedToolName.startsWith(normalizedName);
152414
+ });
152407
152415
  const oneCharacterCompletions = prefixMatches.filter((toolName) => toolName.length === name.length + 1);
152408
152416
  if (oneCharacterCompletions.length === 1) {
152409
152417
  return oneCharacterCompletions[0];
@@ -191380,7 +191388,7 @@ async function fetchPortalUrl(accessToken) {
191380
191388
  throw apiError;
191381
191389
  }
191382
191390
  }
191383
- var subscriptionSchema, subscriptionsSchema, portalSchema;
191391
+ var subscriptionSourceSchema, subscriptionSchema, subscriptionsSchema, portalSchema;
191384
191392
  var init_verbooSubscriptions = __esm(() => {
191385
191393
  init_axios2();
191386
191394
  init_zod();
@@ -191388,6 +191396,17 @@ var init_verbooSubscriptions = __esm(() => {
191388
191396
  init_debug();
191389
191397
  init_log3();
191390
191398
  init_verbooApiError();
191399
+ subscriptionSourceSchema = exports_external2.union([
191400
+ exports_external2.enum([
191401
+ "stripe",
191402
+ "manual",
191403
+ "trial",
191404
+ "stripe_trial",
191405
+ "woovi",
191406
+ "managed_seat"
191407
+ ]),
191408
+ exports_external2.string().min(1)
191409
+ ]);
191391
191410
  subscriptionSchema = exports_external2.object({
191392
191411
  id: exports_external2.string().uuid(),
191393
191412
  groupId: exports_external2.string().uuid(),
@@ -191401,7 +191420,7 @@ var init_verbooSubscriptions = __esm(() => {
191401
191420
  status: exports_external2.string(),
191402
191421
  models: exports_external2.array(exports_external2.string()).optional()
191403
191422
  }).passthrough().optional(),
191404
- source: exports_external2.string().optional(),
191423
+ source: subscriptionSourceSchema.optional(),
191405
191424
  status: exports_external2.string().min(1),
191406
191425
  wooviSubscriptionId: exports_external2.string().optional(),
191407
191426
  currentPeriodStart: exports_external2.string().datetime({ offset: true }).optional(),
@@ -192809,6 +192828,102 @@ var init_openaiSchemaSanitizer = __esm(() => {
192809
192828
  });
192810
192829
 
192811
192830
  // src/services/api/codexShim.ts
192831
+ function maxBufferedToolArgumentChars() {
192832
+ const raw = process.env.VERBOO_MAX_BUFFERED_TOOL_ARGUMENT_CHARS;
192833
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
192834
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS;
192835
+ }
192836
+ function parseCompletedCodexTool(item) {
192837
+ if (typeof item.name !== "string" || !item.name || typeof item.arguments !== "string" || item.arguments.length > maxBufferedToolArgumentChars()) {
192838
+ throw new Error("Codex completed response contained a malformed tool call; no tool was committed");
192839
+ }
192840
+ for (const key of ["id", "call_id"]) {
192841
+ const value = item[key];
192842
+ if (value != null && (typeof value !== "string" || !value.trim())) {
192843
+ throw new Error("Codex completed response contained a malformed tool call ID; no tool was committed");
192844
+ }
192845
+ }
192846
+ const rawToolUseId = item.call_id ?? item.id;
192847
+ if (typeof rawToolUseId !== "string" || !rawToolUseId.trim()) {
192848
+ throw new Error("Codex completed response omitted its tool call ID; no tool was committed");
192849
+ }
192850
+ let input;
192851
+ try {
192852
+ input = JSON.parse(item.arguments);
192853
+ } catch {
192854
+ throw new Error("Codex completed response contained invalid tool arguments; no tool was committed");
192855
+ }
192856
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
192857
+ throw new Error("Codex completed response contained non-object tool arguments; no tool was committed");
192858
+ }
192859
+ return {
192860
+ input,
192861
+ toolUseId: rawToolUseId
192862
+ };
192863
+ }
192864
+ function parseCodexSseEventChunk(chunk) {
192865
+ const lines = chunk.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
192866
+ if (lines.length === 0)
192867
+ return;
192868
+ const eventLine = lines.find((line) => line.startsWith("event:"));
192869
+ const dataLines = lines.filter((line) => line.startsWith("data:"));
192870
+ if (dataLines.length === 0)
192871
+ return;
192872
+ const rawData = dataLines.map((line) => line.slice("data:".length).trimStart()).join(`
192873
+ `);
192874
+ if (rawData === "[DONE]")
192875
+ return;
192876
+ let data;
192877
+ try {
192878
+ const parsed = JSON.parse(rawData);
192879
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
192880
+ throw new Error("Codex SSE data was not a JSON object");
192881
+ }
192882
+ data = parsed;
192883
+ } catch (error41) {
192884
+ throw new Error("Codex SSE emitted invalid JSON; the response was not committed", { cause: error41 });
192885
+ }
192886
+ const framedEvent = eventLine?.slice("event:".length).trim() ?? "";
192887
+ const payloadEvent = typeof data.type === "string" ? data.type : "";
192888
+ if (framedEvent && payloadEvent && framedEvent !== payloadEvent) {
192889
+ throw new Error("Codex SSE event name contradicted its payload type; no tool was committed");
192890
+ }
192891
+ const event = framedEvent || payloadEvent;
192892
+ if (!event) {
192893
+ throw new Error("Codex SSE data omitted its event type; the response was not committed");
192894
+ }
192895
+ return { event, data };
192896
+ }
192897
+ function completedCodexVisibleText(response) {
192898
+ const output = Array.isArray(response.output) ? response.output : [];
192899
+ let visible = "";
192900
+ for (const item of output) {
192901
+ if (item?.type !== "message" || !Array.isArray(item.content))
192902
+ continue;
192903
+ for (const part of item.content) {
192904
+ if (part?.type === "output_text" && typeof part.text === "string") {
192905
+ visible += stripThinkTags(part.text);
192906
+ }
192907
+ }
192908
+ }
192909
+ return visible;
192910
+ }
192911
+ function requireCodexTerminalResponse(payload, expectedStatus) {
192912
+ const response = payload?.response;
192913
+ if (!response || typeof response !== "object" || Array.isArray(response)) {
192914
+ throw new Error(`Codex response.${expectedStatus} omitted its response object; the response was not committed`);
192915
+ }
192916
+ if (response.status !== expectedStatus) {
192917
+ throw new Error(`Codex response.${expectedStatus} carried a missing or contradictory status; the response was not committed`);
192918
+ }
192919
+ if (typeof response.id !== "string" || !response.id.trim()) {
192920
+ throw new Error(`Codex response.${expectedStatus} omitted its response ID; the response was not committed`);
192921
+ }
192922
+ if (!Array.isArray(response.output)) {
192923
+ throw new Error(`Codex response.${expectedStatus} omitted its output array; the response was not committed`);
192924
+ }
192925
+ return response;
192926
+ }
192812
192927
  function makeUsage(usage) {
192813
192928
  return buildAnthropicUsageFromRawUsage(usage);
192814
192929
  }
@@ -193171,80 +193286,110 @@ async function performCodexRequest(options2) {
193171
193286
  return response;
193172
193287
  }
193173
193288
  async function* readSseEvents(response, signal) {
193174
- const reader = response.body?.getReader();
193175
- if (!reader)
193289
+ const responseBody = response.body;
193290
+ if (!responseBody)
193176
193291
  return;
193292
+ const reader = responseBody.getReader();
193177
193293
  const decoder = new TextDecoder;
193178
193294
  let buffer = "";
193179
- const STREAM_IDLE_TIMEOUT_MS = 120000;
193295
+ const STREAM_IDLE_TIMEOUT_MS = (() => {
193296
+ const raw = process.env.VERBOO_STREAM_IDLE_TIMEOUT_MS;
193297
+ const parsed = raw ? parseInt(raw, 10) : NaN;
193298
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 120000;
193299
+ })();
193180
193300
  let lastDataTime = Date.now();
193301
+ let pendingReaderCancellation;
193302
+ let reachedEOF = false;
193303
+ const cancelReader = (reason) => {
193304
+ if (pendingReaderCancellation)
193305
+ return;
193306
+ pendingReaderCancellation = reader.cancel(reason).then(() => {
193307
+ return;
193308
+ }, () => {
193309
+ return;
193310
+ });
193311
+ };
193181
193312
  async function readWithTimeout() {
193182
193313
  return new Promise((resolve19, reject) => {
193314
+ let settled = false;
193315
+ let abortCleanup;
193316
+ const cleanup = () => {
193317
+ clearTimeout(timeoutId);
193318
+ if (signal && abortCleanup)
193319
+ signal.removeEventListener("abort", abortCleanup);
193320
+ };
193321
+ const resolveOnce = (result) => {
193322
+ if (settled)
193323
+ return;
193324
+ settled = true;
193325
+ cleanup();
193326
+ if (result.value)
193327
+ lastDataTime = Date.now();
193328
+ resolve19(result);
193329
+ };
193330
+ const rejectOnce = (error41) => {
193331
+ if (settled)
193332
+ return;
193333
+ settled = true;
193334
+ cleanup();
193335
+ reject(error41);
193336
+ };
193183
193337
  const timeoutId = setTimeout(() => {
193184
193338
  const elapsed = Math.round((Date.now() - lastDataTime) / 1000);
193185
- reject(new Error(`Codex SSE stream idle for ${elapsed}s (limit: ${STREAM_IDLE_TIMEOUT_MS / 1000}s). Connection likely dropped.`));
193339
+ const timeoutError = new Error(`Codex SSE stream idle for ${elapsed}s (limit: ${STREAM_IDLE_TIMEOUT_MS / 1000}s). Connection likely dropped.`);
193340
+ cancelReader(timeoutError);
193341
+ rejectOnce(timeoutError);
193186
193342
  }, STREAM_IDLE_TIMEOUT_MS);
193187
- let abortCleanup;
193188
193343
  if (signal) {
193189
193344
  abortCleanup = () => {
193190
- clearTimeout(timeoutId);
193345
+ const abortError = signal.reason instanceof Error ? signal.reason : Object.assign(new Error("The operation was aborted"), {
193346
+ name: "AbortError"
193347
+ });
193348
+ cancelReader(abortError);
193349
+ rejectOnce(abortError);
193191
193350
  };
193351
+ if (signal.aborted) {
193352
+ abortCleanup();
193353
+ return;
193354
+ }
193192
193355
  signal.addEventListener("abort", abortCleanup, { once: true });
193193
193356
  }
193194
- reader.read().then((result) => {
193195
- clearTimeout(timeoutId);
193196
- if (signal && abortCleanup)
193197
- signal.removeEventListener("abort", abortCleanup);
193198
- if (result.value)
193199
- lastDataTime = Date.now();
193200
- resolve19(result);
193201
- }, (err2) => {
193202
- clearTimeout(timeoutId);
193203
- if (signal && abortCleanup)
193204
- signal.removeEventListener("abort", abortCleanup);
193205
- reject(err2);
193206
- });
193357
+ reader.read().then(resolveOnce, rejectOnce);
193207
193358
  });
193208
193359
  }
193209
- while (true) {
193210
- const { done, value } = await readWithTimeout();
193211
- if (done)
193212
- break;
193213
- buffer += decoder.decode(value, { stream: true });
193214
- const chunks = buffer.split(`
193215
-
193216
- `);
193217
- buffer = chunks.pop() ?? "";
193218
- for (const chunk of chunks) {
193219
- const lines = chunk.split(`
193220
- `).map((line) => line.trim()).filter(Boolean);
193221
- if (lines.length === 0)
193222
- continue;
193223
- const eventLine = lines.find((line) => line.startsWith("event: "));
193224
- const dataLines = lines.filter((line) => line.startsWith("data: "));
193225
- if (!eventLine || dataLines.length === 0)
193226
- continue;
193227
- const event = eventLine.slice(7).trim();
193228
- const rawData = dataLines.map((line) => line.slice(6)).join(`
193229
- `);
193230
- if (rawData === "[DONE]")
193231
- continue;
193232
- let data;
193233
- try {
193234
- const parsed = JSON.parse(rawData);
193235
- if (!parsed || typeof parsed !== "object")
193236
- continue;
193237
- data = parsed;
193238
- } catch {
193239
- continue;
193360
+ try {
193361
+ while (true) {
193362
+ const { done, value } = await readWithTimeout();
193363
+ if (done) {
193364
+ reachedEOF = true;
193365
+ buffer += decoder.decode();
193366
+ const finalEvent = parseCodexSseEventChunk(buffer);
193367
+ if (finalEvent)
193368
+ yield finalEvent;
193369
+ break;
193370
+ }
193371
+ buffer += decoder.decode(value, { stream: true });
193372
+ const chunks = buffer.split(/\r?\n\r?\n/);
193373
+ buffer = chunks.pop() ?? "";
193374
+ for (const chunk of chunks) {
193375
+ const event = parseCodexSseEventChunk(chunk);
193376
+ if (event)
193377
+ yield event;
193240
193378
  }
193241
- yield { event, data };
193242
193379
  }
193380
+ } finally {
193381
+ if (!reachedEOF) {
193382
+ cancelReader("Codex SSE consumer completed before transport EOF");
193383
+ }
193384
+ await pendingReaderCancellation;
193385
+ try {
193386
+ reader.releaseLock();
193387
+ } catch {}
193243
193388
  }
193244
193389
  }
193245
193390
  function determineStopReason(response, sawToolUse) {
193246
193391
  const output = Array.isArray(response?.output) ? response.output : [];
193247
- if (sawToolUse || output.some((item) => item?.type === "function_call")) {
193392
+ if (response?.status !== "incomplete" && (sawToolUse || output.some((item) => item?.type === "function_call"))) {
193248
193393
  return "tool_use";
193249
193394
  }
193250
193395
  const incompleteReason = response?.incomplete_details?.reason;
@@ -193260,8 +193405,12 @@ async function collectCodexCompletedResponse(response, signal) {
193260
193405
  const msg = event.data?.response?.error?.message ?? event.data?.error?.message ?? "Codex response failed";
193261
193406
  throw APIError.generate(500, undefined, msg, new Headers);
193262
193407
  }
193263
- if (event.event === "response.completed" || event.event === "response.incomplete") {
193264
- completedResponse = event.data?.response;
193408
+ if (event.event === "response.completed") {
193409
+ completedResponse = requireCodexTerminalResponse(event.data, "completed");
193410
+ break;
193411
+ }
193412
+ if (event.event === "response.incomplete") {
193413
+ completedResponse = requireCodexTerminalResponse(event.data, "incomplete");
193265
193414
  break;
193266
193415
  }
193267
193416
  }
@@ -193274,18 +193423,26 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193274
193423
  const messageId = makeMessageId();
193275
193424
  const toolBlocksByItemId = new Map;
193276
193425
  let activeTextBlockIndex = null;
193426
+ let emittedVisibleText = "";
193277
193427
  const thinkFilter = createThinkTagFilter();
193278
193428
  let nextContentBlockIndex = 0;
193279
193429
  let sawToolUse = false;
193280
193430
  let finalResponse;
193431
+ let terminalEvent;
193432
+ let terminalErrorMessage;
193433
+ let totalBufferedToolArgumentChars = 0;
193434
+ const toolArgumentCharsLimit = maxBufferedToolArgumentChars();
193281
193435
  const closeActiveTextBlock = async function* () {
193282
193436
  if (activeTextBlockIndex === null)
193283
193437
  return;
193438
+ const textBlockIndex = activeTextBlockIndex;
193439
+ activeTextBlockIndex = null;
193284
193440
  const tail = thinkFilter.flush();
193285
193441
  if (tail) {
193442
+ emittedVisibleText += tail;
193286
193443
  yield {
193287
193444
  type: "content_block_delta",
193288
- index: activeTextBlockIndex,
193445
+ index: textBlockIndex,
193289
193446
  delta: {
193290
193447
  type: "text_delta",
193291
193448
  text: tail
@@ -193294,9 +193451,8 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193294
193451
  }
193295
193452
  yield {
193296
193453
  type: "content_block_stop",
193297
- index: activeTextBlockIndex
193454
+ index: textBlockIndex
193298
193455
  };
193299
- activeTextBlockIndex = null;
193300
193456
  };
193301
193457
  const startTextBlockIfNeeded = async function* () {
193302
193458
  if (activeTextBlockIndex !== null)
@@ -193308,6 +193464,21 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193308
193464
  content_block: { type: "text", text: "" }
193309
193465
  };
193310
193466
  };
193467
+ const replaceToolArguments = (toolBlock, nextArguments) => {
193468
+ const nextTotal = totalBufferedToolArgumentChars - toolBlock.argumentsBuffer.length + nextArguments.length;
193469
+ if (nextArguments.length > toolArgumentCharsLimit || nextTotal > toolArgumentCharsLimit) {
193470
+ throw new Error("Codex tool arguments exceeded the configured safety limit; no tool was committed");
193471
+ }
193472
+ toolBlock.argumentsBuffer = nextArguments;
193473
+ totalBufferedToolArgumentChars = nextTotal;
193474
+ };
193475
+ const appendToolArguments = (toolBlock, delta) => {
193476
+ if (toolBlock.argumentsBuffer.length + delta.length > toolArgumentCharsLimit || totalBufferedToolArgumentChars + delta.length > toolArgumentCharsLimit) {
193477
+ throw new Error("Codex tool arguments exceeded the configured safety limit; no tool was committed");
193478
+ }
193479
+ toolBlock.argumentsBuffer += delta;
193480
+ totalBufferedToolArgumentChars += delta.length;
193481
+ };
193311
193482
  const findToolBlockEntry = (item) => {
193312
193483
  for (const candidate of [item.id, item.call_id]) {
193313
193484
  if (candidate == null)
@@ -193316,12 +193487,18 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193316
193487
  const toolBlock = toolBlocksByItemId.get(itemId);
193317
193488
  if (toolBlock)
193318
193489
  return [itemId, toolBlock];
193490
+ for (const entry of toolBlocksByItemId) {
193491
+ if (entry[0] === itemId || entry[1].toolUseId === itemId)
193492
+ return entry;
193493
+ }
193319
193494
  }
193320
193495
  return;
193321
193496
  };
193322
- const toolNameMayBeIncomplete = (name) => Boolean(name) && !advertisedToolNames.includes(name) && advertisedToolNames.some((toolName) => toolName.startsWith(name));
193323
193497
  const canonicalizeFinalToolName = (toolBlock) => {
193324
193498
  const resolvedName = resolveToolNameByUniquePrefix(advertisedToolNames, toolBlock.name);
193499
+ if (advertisedToolNames.length > 0 && !resolvedName) {
193500
+ throw new Error("Codex completed response selected an unadvertised tool; no tool was committed");
193501
+ }
193325
193502
  if (resolvedName)
193326
193503
  toolBlock.name = resolvedName;
193327
193504
  };
@@ -193329,8 +193506,11 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193329
193506
  if (typeof item.name === "string" && item.name) {
193330
193507
  toolBlock.name = item.name;
193331
193508
  }
193332
- if (typeof item.arguments === "string" && (!toolBlock.hasStarted || item.arguments.startsWith(toolBlock.argumentsBuffer))) {
193333
- toolBlock.argumentsBuffer = item.arguments;
193509
+ if (typeof item.arguments === "string") {
193510
+ replaceToolArguments(toolBlock, item.arguments);
193511
+ }
193512
+ if (item.call_id != null || item.id != null) {
193513
+ toolBlock.toolUseId = String(item.call_id ?? item.id);
193334
193514
  }
193335
193515
  canonicalizeFinalToolName(toolBlock);
193336
193516
  };
@@ -193348,14 +193528,10 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193348
193528
  };
193349
193529
  toolBlock.emittedArgumentsLength = toolBlock.argumentsBuffer.length;
193350
193530
  };
193351
- const startToolBlock = async function* (toolBlock, force = false) {
193531
+ const startToolBlock = async function* (toolBlock) {
193352
193532
  if (toolBlock.hasStarted)
193353
193533
  return;
193354
- if (!force && (!toolBlock.name || toolNameMayBeIncomplete(toolBlock.name))) {
193355
- return;
193356
- }
193357
- if (force)
193358
- canonicalizeFinalToolName(toolBlock);
193534
+ canonicalizeFinalToolName(toolBlock);
193359
193535
  toolBlock.hasStarted = true;
193360
193536
  toolBlock.startedName = toolBlock.name || "tool";
193361
193537
  yield {
@@ -193370,45 +193546,47 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193370
193546
  };
193371
193547
  yield* emitPendingToolArguments(toolBlock);
193372
193548
  };
193373
- const flushToolBlocks = async function* (force = false) {
193549
+ const flushToolBlocks = async function* () {
193374
193550
  const orderedBlocks = [...toolBlocksByItemId.values()].sort((a2, b) => a2.index - b.index);
193375
193551
  for (const toolBlock of orderedBlocks) {
193376
193552
  if (toolBlock.hasStopped)
193377
193553
  continue;
193378
193554
  if (!toolBlock.hasStarted) {
193379
- yield* startToolBlock(toolBlock, force || toolBlock.isDone);
193555
+ if (!toolBlock.isDone)
193556
+ break;
193557
+ yield* startToolBlock(toolBlock);
193380
193558
  if (!toolBlock.hasStarted)
193381
193559
  break;
193382
193560
  }
193383
- if (toolBlock.isDone && toolBlock.startedName !== (toolBlock.name || "tool")) {
193384
- toolBlock.startedName = toolBlock.name || "tool";
193385
- yield {
193386
- type: "content_block_start",
193387
- index: toolBlock.index,
193388
- content_block: {
193389
- type: "tool_use",
193390
- id: toolBlock.toolUseId,
193391
- name: toolBlock.startedName,
193392
- input: {}
193393
- }
193394
- };
193395
- }
193396
193561
  yield* emitPendingToolArguments(toolBlock);
193397
193562
  if (toolBlock.isDone) {
193563
+ toolBlock.hasStopped = true;
193398
193564
  yield {
193399
193565
  type: "content_block_stop",
193400
193566
  index: toolBlock.index
193401
193567
  };
193402
- toolBlock.hasStopped = true;
193403
193568
  }
193404
193569
  }
193405
193570
  };
193406
193571
  const removeStoppedToolBlocks = () => {
193407
193572
  for (const [itemId, toolBlock] of toolBlocksByItemId) {
193408
- if (toolBlock.hasStopped)
193573
+ if (toolBlock.hasStopped) {
193574
+ totalBufferedToolArgumentChars -= toolBlock.argumentsBuffer.length;
193409
193575
  toolBlocksByItemId.delete(itemId);
193576
+ }
193410
193577
  }
193411
193578
  };
193579
+ const closeOpenBlocksForFailure = async function* () {
193580
+ yield* closeActiveTextBlock();
193581
+ const orderedBlocks = [...new Set(toolBlocksByItemId.values())].sort((a2, b) => a2.index - b.index);
193582
+ for (const toolBlock of orderedBlocks) {
193583
+ if (!toolBlock.hasStarted || toolBlock.hasStopped)
193584
+ continue;
193585
+ toolBlock.hasStopped = true;
193586
+ yield { type: "content_block_stop", index: toolBlock.index };
193587
+ }
193588
+ toolBlocksByItemId.clear();
193589
+ };
193412
193590
  yield {
193413
193591
  type: "message_start",
193414
193592
  message: {
@@ -193422,116 +193600,244 @@ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolN
193422
193600
  usage: makeUsage()
193423
193601
  }
193424
193602
  };
193425
- for await (const event of readSseEvents(response, signal)) {
193426
- const payload = event.data;
193427
- if (event.event === "response.output_item.added") {
193428
- const item = payload.item;
193429
- if (item?.type === "function_call") {
193430
- yield* closeActiveTextBlock();
193603
+ try {
193604
+ for await (const event of readSseEvents(response, signal)) {
193605
+ const payload = event.data;
193606
+ if (event.event === "response.output_item.added") {
193607
+ const item = payload.item;
193608
+ if (item?.type === "function_call") {
193609
+ yield* closeActiveTextBlock();
193610
+ const blockIndex = nextContentBlockIndex++;
193611
+ const toolUseId = item.call_id ?? item.id ?? `call_${blockIndex}`;
193612
+ const toolBlock = {
193613
+ index: blockIndex,
193614
+ toolUseId,
193615
+ name: typeof item.name === "string" ? item.name : "",
193616
+ argumentsBuffer: "",
193617
+ emittedArgumentsLength: 0,
193618
+ hasStarted: false,
193619
+ isDone: false,
193620
+ hasStopped: false
193621
+ };
193622
+ if (typeof item.arguments === "string") {
193623
+ replaceToolArguments(toolBlock, item.arguments);
193624
+ }
193625
+ const itemKey = String(item.id ?? toolUseId);
193626
+ if (toolBlocksByItemId.has(itemKey)) {
193627
+ throw new Error("Codex stream repeated a tool item ID; no tool was committed");
193628
+ }
193629
+ toolBlocksByItemId.set(itemKey, toolBlock);
193630
+ }
193631
+ continue;
193632
+ }
193633
+ if (event.event === "response.content_part.added") {
193634
+ if (payload.part?.type === "output_text") {
193635
+ if (toolBlocksByItemId.size > 0) {
193636
+ throw new Error("Codex emitted text after a reserved tool block; the out-of-order response was not committed");
193637
+ }
193638
+ yield* startTextBlockIfNeeded();
193639
+ }
193640
+ continue;
193641
+ }
193642
+ if (event.event === "response.output_text.delta") {
193643
+ if (toolBlocksByItemId.size > 0) {
193644
+ throw new Error("Codex emitted text after a reserved tool block; the out-of-order response was not committed");
193645
+ }
193646
+ yield* startTextBlockIfNeeded();
193647
+ if (activeTextBlockIndex !== null) {
193648
+ const visible = thinkFilter.feed(payload.delta ?? "");
193649
+ if (visible) {
193650
+ emittedVisibleText += visible;
193651
+ yield {
193652
+ type: "content_block_delta",
193653
+ index: activeTextBlockIndex,
193654
+ delta: {
193655
+ type: "text_delta",
193656
+ text: visible
193657
+ }
193658
+ };
193659
+ }
193660
+ }
193661
+ continue;
193662
+ }
193663
+ if (event.event === "response.function_call_arguments.delta") {
193664
+ const toolBlock = toolBlocksByItemId.get(String(payload.item_id ?? ""));
193665
+ if (toolBlock) {
193666
+ if (typeof payload.delta === "string") {
193667
+ appendToolArguments(toolBlock, payload.delta);
193668
+ }
193669
+ }
193670
+ continue;
193671
+ }
193672
+ if (event.event === "response.output_item.done") {
193673
+ const item = payload.item;
193674
+ if (item?.type === "function_call") {
193675
+ const toolBlockEntry = findToolBlockEntry(item);
193676
+ if (toolBlockEntry) {
193677
+ const [, toolBlock] = toolBlockEntry;
193678
+ applyFinalToolItem(toolBlock, item);
193679
+ toolBlock.isDone = true;
193680
+ }
193681
+ } else if (item?.type === "message") {
193682
+ yield* closeActiveTextBlock();
193683
+ }
193684
+ continue;
193685
+ }
193686
+ if (event.event === "response.completed") {
193687
+ terminalEvent = "completed";
193688
+ finalResponse = requireCodexTerminalResponse(payload, "completed");
193689
+ break;
193690
+ }
193691
+ if (event.event === "response.incomplete") {
193692
+ terminalEvent = "incomplete";
193693
+ finalResponse = requireCodexTerminalResponse(payload, "incomplete");
193694
+ break;
193695
+ }
193696
+ if (event.event === "response.failed") {
193697
+ terminalEvent = "failed";
193698
+ terminalErrorMessage = payload?.response?.error?.message ?? payload?.error?.message ?? "Codex response failed";
193699
+ break;
193700
+ }
193701
+ }
193702
+ yield* closeActiveTextBlock();
193703
+ if (terminalEvent === "failed") {
193704
+ toolBlocksByItemId.clear();
193705
+ throw APIError.generate(500, undefined, terminalErrorMessage ?? "Codex response failed", new Headers);
193706
+ }
193707
+ if (!terminalEvent || !finalResponse) {
193708
+ toolBlocksByItemId.clear();
193709
+ throw APIError.generate(500, undefined, "Codex response ended without a terminal payload", new Headers);
193710
+ }
193711
+ const finalVisibleText = completedCodexVisibleText(finalResponse);
193712
+ let missingSuffix = "";
193713
+ if (!emittedVisibleText) {
193714
+ missingSuffix = finalVisibleText;
193715
+ } else if (finalVisibleText.startsWith(emittedVisibleText)) {
193716
+ missingSuffix = finalVisibleText.slice(emittedVisibleText.length);
193717
+ } else if (finalVisibleText !== emittedVisibleText) {
193718
+ throw APIError.generate(500, undefined, "Codex completed text contradicted streamed text; response was not committed", new Headers);
193719
+ }
193720
+ if (missingSuffix && toolBlocksByItemId.size > 0) {
193721
+ toolBlocksByItemId.clear();
193722
+ throw APIError.generate(500, undefined, "Codex completed text arrived after a reserved tool block; the out-of-order response was not committed", new Headers);
193723
+ }
193724
+ if (missingSuffix) {
193725
+ yield* startTextBlockIfNeeded();
193726
+ if (activeTextBlockIndex !== null) {
193727
+ emittedVisibleText += missingSuffix;
193728
+ yield {
193729
+ type: "content_block_delta",
193730
+ index: activeTextBlockIndex,
193731
+ delta: { type: "text_delta", text: missingSuffix }
193732
+ };
193733
+ }
193734
+ yield* closeActiveTextBlock();
193735
+ }
193736
+ if (terminalEvent === "incomplete") {
193737
+ toolBlocksByItemId.clear();
193738
+ yield {
193739
+ type: "message_delta",
193740
+ delta: {
193741
+ stop_reason: determineStopReason(finalResponse, false),
193742
+ stop_sequence: null
193743
+ },
193744
+ usage: makeUsage(finalResponse.usage)
193745
+ };
193746
+ yield { type: "message_stop" };
193747
+ return;
193748
+ }
193749
+ const finalOutput = Array.isArray(finalResponse?.output) ? finalResponse.output : [];
193750
+ const reservedToolIndices = [...toolBlocksByItemId.values()].map((toolBlock) => toolBlock.index);
193751
+ const firstFinalToolIndex = reservedToolIndices.length > 0 ? Math.min(...reservedToolIndices) : nextContentBlockIndex;
193752
+ const completedToolBlocks = new Set;
193753
+ const completedToolCallIds = new Set;
193754
+ for (const item of finalOutput) {
193755
+ if (item?.type !== "function_call")
193756
+ continue;
193757
+ try {
193758
+ parseCompletedCodexTool(item);
193759
+ } catch (error41) {
193760
+ toolBlocksByItemId.clear();
193761
+ throw APIError.generate(500, undefined, error41 instanceof Error ? error41.message : "Invalid Codex tool call", new Headers);
193762
+ }
193763
+ let toolBlockEntry = findToolBlockEntry(item);
193764
+ if (!toolBlockEntry) {
193431
193765
  const blockIndex = nextContentBlockIndex++;
193432
- const toolUseId = item.call_id ?? item.id ?? `call_${blockIndex}`;
193766
+ const { toolUseId } = parseCompletedCodexTool(item);
193433
193767
  const toolBlock = {
193434
193768
  index: blockIndex,
193435
193769
  toolUseId,
193436
- name: typeof item.name === "string" ? item.name : "",
193437
- argumentsBuffer: typeof item.arguments === "string" ? item.arguments : "",
193770
+ name: "",
193771
+ argumentsBuffer: "",
193438
193772
  emittedArgumentsLength: 0,
193439
193773
  hasStarted: false,
193440
193774
  isDone: false,
193441
193775
  hasStopped: false
193442
193776
  };
193443
- toolBlocksByItemId.set(String(item.id ?? toolUseId), toolBlock);
193444
- sawToolUse = true;
193445
- yield* flushToolBlocks();
193777
+ const itemKey = String(item.id ?? item.call_id ?? toolUseId);
193778
+ toolBlocksByItemId.set(itemKey, toolBlock);
193779
+ toolBlockEntry = [itemKey, toolBlock];
193446
193780
  }
193447
- continue;
193448
- }
193449
- if (event.event === "response.content_part.added") {
193450
- if (payload.part?.type === "output_text") {
193451
- yield* startTextBlockIfNeeded();
193781
+ if (completedToolBlocks.has(toolBlockEntry[1])) {
193782
+ toolBlocksByItemId.clear();
193783
+ throw APIError.generate(500, undefined, "Codex completed response repeated a tool call; no tool was committed", new Headers);
193452
193784
  }
193453
- continue;
193454
- }
193455
- if (event.event === "response.output_text.delta") {
193456
- yield* startTextBlockIfNeeded();
193457
- if (activeTextBlockIndex !== null) {
193458
- const visible = thinkFilter.feed(payload.delta ?? "");
193459
- if (visible) {
193460
- yield {
193461
- type: "content_block_delta",
193462
- index: activeTextBlockIndex,
193463
- delta: {
193464
- type: "text_delta",
193465
- text: visible
193466
- }
193467
- };
193468
- }
193785
+ applyFinalToolItem(toolBlockEntry[1], item);
193786
+ if (completedToolCallIds.has(toolBlockEntry[1].toolUseId)) {
193787
+ toolBlocksByItemId.clear();
193788
+ throw APIError.generate(500, undefined, "Codex completed response reused a tool call ID; no tool was committed", new Headers);
193469
193789
  }
193470
- continue;
193790
+ completedToolCallIds.add(toolBlockEntry[1].toolUseId);
193791
+ toolBlockEntry[1].isDone = true;
193792
+ completedToolBlocks.add(toolBlockEntry[1]);
193471
193793
  }
193472
- if (event.event === "response.function_call_arguments.delta") {
193473
- const toolBlock = toolBlocksByItemId.get(String(payload.item_id ?? ""));
193474
- if (toolBlock) {
193475
- if (typeof payload.delta === "string") {
193476
- toolBlock.argumentsBuffer += payload.delta;
193477
- }
193478
- yield* flushToolBlocks();
193479
- }
193480
- continue;
193794
+ let authoritativeToolOffset = 0;
193795
+ for (const toolBlock of completedToolBlocks) {
193796
+ toolBlock.index = firstFinalToolIndex + authoritativeToolOffset++;
193481
193797
  }
193482
- if (event.event === "response.output_item.done") {
193483
- const item = payload.item;
193484
- if (item?.type === "function_call") {
193485
- const toolBlockEntry = findToolBlockEntry(item);
193486
- if (toolBlockEntry) {
193487
- const [, toolBlock] = toolBlockEntry;
193488
- applyFinalToolItem(toolBlock, item);
193489
- toolBlock.isDone = true;
193490
- yield* flushToolBlocks();
193491
- removeStoppedToolBlocks();
193492
- }
193493
- } else if (item?.type === "message") {
193494
- yield* closeActiveTextBlock();
193495
- }
193496
- continue;
193798
+ if (completedToolBlocks.size > 0) {
193799
+ nextContentBlockIndex = firstFinalToolIndex + completedToolBlocks.size;
193497
193800
  }
193498
- if (event.event === "response.completed" || event.event === "response.incomplete") {
193499
- finalResponse = payload.response;
193500
- break;
193501
- }
193502
- if (event.event === "response.failed") {
193503
- const msg = payload?.response?.error?.message ?? payload?.error?.message ?? "Codex response failed";
193504
- throw APIError.generate(500, undefined, msg, new Headers);
193801
+ for (const [itemId, toolBlock] of toolBlocksByItemId) {
193802
+ if (!completedToolBlocks.has(toolBlock)) {
193803
+ totalBufferedToolArgumentChars -= toolBlock.argumentsBuffer.length;
193804
+ toolBlocksByItemId.delete(itemId);
193805
+ }
193505
193806
  }
193807
+ sawToolUse = completedToolBlocks.size > 0;
193808
+ yield* flushToolBlocks();
193809
+ removeStoppedToolBlocks();
193810
+ yield {
193811
+ type: "message_delta",
193812
+ delta: {
193813
+ stop_reason: determineStopReason(finalResponse, sawToolUse),
193814
+ stop_sequence: null
193815
+ },
193816
+ usage: makeUsage(finalResponse?.usage)
193817
+ };
193818
+ yield { type: "message_stop" };
193819
+ } catch (error41) {
193820
+ yield* closeOpenBlocksForFailure();
193821
+ throw error41;
193506
193822
  }
193507
- yield* closeActiveTextBlock();
193508
- const finalOutput = Array.isArray(finalResponse?.output) ? finalResponse.output : [];
193509
- for (const item of finalOutput) {
193510
- if (item?.type !== "function_call")
193511
- continue;
193512
- const toolBlockEntry = findToolBlockEntry(item);
193513
- if (!toolBlockEntry)
193514
- continue;
193515
- applyFinalToolItem(toolBlockEntry[1], item);
193516
- }
193517
- for (const toolBlock of toolBlocksByItemId.values()) {
193518
- toolBlock.isDone = true;
193519
- }
193520
- yield* flushToolBlocks(true);
193521
- removeStoppedToolBlocks();
193522
- yield {
193523
- type: "message_delta",
193524
- delta: {
193525
- stop_reason: determineStopReason(finalResponse, sawToolUse),
193526
- stop_sequence: null
193527
- },
193528
- usage: makeUsage(finalResponse?.usage)
193529
- };
193530
- yield { type: "message_stop" };
193531
193823
  }
193532
193824
  function convertCodexResponseToAnthropicMessage(data, model2, advertisedToolNames = []) {
193533
193825
  const content = [];
193534
193826
  const output = Array.isArray(data.output) ? data.output : [];
193827
+ if (data.status == null) {
193828
+ throw new Error("Codex response omitted its terminal status; the response was not committed");
193829
+ }
193830
+ if (data.status === "failed") {
193831
+ const failureMessage = typeof data.error?.message === "string" ? `: ${data.error.message}` : "";
193832
+ throw new Error(`Codex response failed${failureMessage}`);
193833
+ }
193834
+ if (data.status !== "completed" && data.status !== "incomplete") {
193835
+ throw new Error(`Codex response carried non-terminal status "${String(data.status)}"; the response was not committed`);
193836
+ }
193837
+ const mayCommitTools = data.status === "completed";
193838
+ const completedToolCallIds = new Set;
193839
+ let completedToolArgumentChars = 0;
193840
+ const toolArgumentCharsLimit = maxBufferedToolArgumentChars();
193535
193841
  for (const item of output) {
193536
193842
  if (item?.type === "message" && Array.isArray(item.content)) {
193537
193843
  for (const part of item.content) {
@@ -193544,17 +193850,23 @@ function convertCodexResponseToAnthropicMessage(data, model2, advertisedToolName
193544
193850
  }
193545
193851
  continue;
193546
193852
  }
193547
- if (item?.type === "function_call") {
193853
+ if (item?.type === "function_call" && mayCommitTools) {
193854
+ const { input, toolUseId } = parseCompletedCodexTool(item);
193855
+ completedToolArgumentChars += item.arguments.length;
193856
+ if (completedToolArgumentChars > toolArgumentCharsLimit) {
193857
+ throw new Error("Codex tool arguments exceeded the configured safety limit; no tool was committed");
193858
+ }
193859
+ if (completedToolCallIds.has(toolUseId)) {
193860
+ throw new Error("Codex completed response reused a tool call ID; no tool was committed");
193861
+ }
193862
+ completedToolCallIds.add(toolUseId);
193548
193863
  const toolName = resolveToolNameByUniquePrefix(advertisedToolNames, item.name ?? "") ?? item.name ?? "tool";
193549
- let input;
193550
- try {
193551
- input = JSON.parse(item.arguments ?? "{}");
193552
- } catch {
193553
- input = { raw: item.arguments ?? "" };
193864
+ if (advertisedToolNames.length > 0 && !resolveToolNameByUniquePrefix(advertisedToolNames, item.name ?? "")) {
193865
+ throw new Error("Codex completed response selected an unadvertised tool; no tool was committed");
193554
193866
  }
193555
193867
  content.push({
193556
193868
  type: "tool_use",
193557
- id: item.call_id ?? item.id ?? makeMessageId(),
193869
+ id: toolUseId,
193558
193870
  name: toolName,
193559
193871
  input
193560
193872
  });
@@ -193571,6 +193883,7 @@ function convertCodexResponseToAnthropicMessage(data, model2, advertisedToolName
193571
193883
  usage: makeUsage(data.usage)
193572
193884
  };
193573
193885
  }
193886
+ var DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS;
193574
193887
  var init_codexShim = __esm(() => {
193575
193888
  init_sdk();
193576
193889
  init_Tool();
@@ -193579,6 +193892,7 @@ var init_codexShim = __esm(() => {
193579
193892
  init_fetchWithProxyRetry();
193580
193893
  init_openaiSchemaSanitizer();
193581
193894
  init_thinkTagSanitizer();
193895
+ DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS = 64 * 1024 * 1024;
193582
193896
  });
193583
193897
 
193584
193898
  // src/services/api/visionDelegate.ts
@@ -194820,29 +195134,84 @@ function convertChunkUsage(usage) {
194820
195134
  return;
194821
195135
  return buildAnthropicUsageFromRawUsage(usage);
194822
195136
  }
194823
- function repairPossiblyTruncatedObjectJson(raw) {
195137
+ function hasInvalidToolArguments(raw, toolName) {
195138
+ const trimmed2 = raw.trim();
195139
+ if (!hasToolFieldMapping(toolName)) {
195140
+ try {
195141
+ const parsed = JSON.parse(raw);
195142
+ return parsed === null || typeof parsed !== "object" || Array.isArray(parsed);
195143
+ } catch {
195144
+ return true;
195145
+ }
195146
+ }
195147
+ if (!trimmed2.startsWith("{"))
195148
+ return false;
194824
195149
  try {
194825
- const parsed = JSON.parse(raw);
194826
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? raw : null;
195150
+ JSON.parse(raw);
195151
+ return false;
194827
195152
  } catch {
194828
- for (const combo of JSON_REPAIR_SUFFIXES) {
194829
- try {
194830
- const repaired = raw + combo;
194831
- const parsed = JSON.parse(repaired);
194832
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
194833
- return repaired;
194834
- }
194835
- } catch {}
195153
+ return !(toolName.toLowerCase() === "bash" && /^\{\s*[^{}]*;\s*\}$/.test(trimmed2));
195154
+ }
195155
+ }
195156
+ function maxBufferedToolArgumentChars2() {
195157
+ const raw = process.env.VERBOO_MAX_BUFFERED_TOOL_ARGUMENT_CHARS;
195158
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
195159
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS2;
195160
+ }
195161
+ function mergeOpaqueMetadata(current, next) {
195162
+ const merged = Object.fromEntries(Object.entries(current ?? {}));
195163
+ for (const [key, nextValue] of Object.entries(next)) {
195164
+ const currentValue = merged[key];
195165
+ if (currentValue !== null && nextValue !== null && typeof currentValue === "object" && typeof nextValue === "object" && !Array.isArray(currentValue) && !Array.isArray(nextValue)) {
195166
+ Object.defineProperty(merged, key, {
195167
+ value: mergeOpaqueMetadata(currentValue, nextValue),
195168
+ enumerable: true,
195169
+ configurable: true,
195170
+ writable: true
195171
+ });
195172
+ } else {
195173
+ Object.defineProperty(merged, key, {
195174
+ value: nextValue,
195175
+ enumerable: true,
195176
+ configurable: true,
195177
+ writable: true
195178
+ });
194836
195179
  }
194837
- return null;
194838
195180
  }
195181
+ return merged;
194839
195182
  }
194840
- function mergeStreamedToolName(current, fragment) {
194841
- if (!fragment)
194842
- return current;
194843
- if (!current || fragment.startsWith(current))
194844
- return fragment;
194845
- return current + fragment;
195183
+ function resolveStreamedToolName(advertisedToolNames, fragments) {
195184
+ const nonEmpty = fragments.filter(Boolean);
195185
+ const concatenated = nonEmpty.join("");
195186
+ if (advertisedToolNames.length === 0) {
195187
+ return { name: concatenated, ambiguous: false };
195188
+ }
195189
+ const candidates = new Set([concatenated]);
195190
+ let cumulative = "";
195191
+ for (const fragment of nonEmpty) {
195192
+ if (!cumulative) {
195193
+ cumulative = fragment;
195194
+ continue;
195195
+ }
195196
+ const currentFolded = cumulative.toLowerCase();
195197
+ const fragmentFolded = fragment.toLowerCase();
195198
+ if (fragmentFolded.startsWith(currentFolded)) {
195199
+ cumulative = fragment;
195200
+ } else if (!currentFolded.startsWith(fragmentFolded)) {
195201
+ cumulative += fragment;
195202
+ }
195203
+ }
195204
+ candidates.add(cumulative);
195205
+ const resolved = new Set;
195206
+ for (const candidate of candidates) {
195207
+ const toolName = resolveToolNameByUniquePrefix(advertisedToolNames, candidate);
195208
+ if (toolName)
195209
+ resolved.add(toolName);
195210
+ }
195211
+ if (resolved.size === 1) {
195212
+ return { name: [...resolved][0], ambiguous: false };
195213
+ }
195214
+ return { name: concatenated, ambiguous: true };
194846
195215
  }
194847
195216
  function getAdvertisedToolNames(params) {
194848
195217
  return (params.tools ?? []).flatMap((tool) => typeof tool.name === "string" && tool.name && tool.name !== "ToolSearchTool" ? [tool.name] : []);
@@ -194923,7 +195292,11 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
194923
195292
  let lastStopReason = null;
194924
195293
  let hasEmittedFinalUsage = false;
194925
195294
  let hasProcessedFinishReason = false;
195295
+ let sawDoneMarker = false;
194926
195296
  const streamState = createStreamState();
195297
+ let nextSyntheticProtocolIndex = -2;
195298
+ let totalBufferedToolArgumentChars = 0;
195299
+ const toolArgumentCharsLimit = maxBufferedToolArgumentChars2();
194927
195300
  yield {
194928
195301
  type: "message_start",
194929
195302
  message: {
@@ -194942,9 +195315,11 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
194942
195315
  }
194943
195316
  }
194944
195317
  };
194945
- const reader = response.body?.getReader();
194946
- if (!reader)
194947
- return;
195318
+ const responseBody = response.body;
195319
+ if (!responseBody) {
195320
+ throw new Error("Upstream SSE response had no readable body");
195321
+ }
195322
+ const reader = responseBody.getReader();
194948
195323
  const decoder = new TextDecoder;
194949
195324
  let buffer = "";
194950
195325
  const STREAM_IDLE_TIMEOUT_MS = (() => {
@@ -194954,32 +195329,61 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
194954
195329
  })();
194955
195330
  let lastDataTime = Date.now();
194956
195331
  const streamStartedAt = Date.now();
195332
+ let pendingReaderCancellation;
195333
+ const cancelReader = (reason) => {
195334
+ pendingReaderCancellation = reader.cancel(reason).then(() => {
195335
+ return;
195336
+ }, () => {
195337
+ return;
195338
+ });
195339
+ };
194957
195340
  async function readWithTimeout() {
194958
195341
  return new Promise((resolve19, reject) => {
195342
+ let settled = false;
195343
+ let abortCleanup;
195344
+ const cleanup = () => {
195345
+ clearTimeout(timeoutId);
195346
+ if (signal && abortCleanup) {
195347
+ signal.removeEventListener("abort", abortCleanup);
195348
+ }
195349
+ };
195350
+ const resolveOnce = (result) => {
195351
+ if (settled)
195352
+ return;
195353
+ settled = true;
195354
+ cleanup();
195355
+ if (result.value)
195356
+ lastDataTime = Date.now();
195357
+ resolve19(result);
195358
+ };
195359
+ const rejectOnce = (error41) => {
195360
+ if (settled)
195361
+ return;
195362
+ settled = true;
195363
+ cleanup();
195364
+ reject(error41);
195365
+ };
194959
195366
  const timeoutId = setTimeout(() => {
194960
195367
  const elapsed = Math.round((Date.now() - lastDataTime) / 1000);
194961
- reject(new Error(`OpenAI/Gemini SSE stream idle for ${elapsed}s (limit: ${STREAM_IDLE_TIMEOUT_MS / 1000}s). Connection likely dropped.`));
195368
+ const timeoutError = new Error(`OpenAI/Gemini SSE stream idle for ${elapsed}s (limit: ${STREAM_IDLE_TIMEOUT_MS / 1000}s). Connection likely dropped.`);
195369
+ cancelReader(timeoutError);
195370
+ rejectOnce(timeoutError);
194962
195371
  }, STREAM_IDLE_TIMEOUT_MS);
194963
- let abortCleanup;
194964
195372
  if (signal) {
194965
195373
  abortCleanup = () => {
194966
- clearTimeout(timeoutId);
195374
+ const abortError = signal.reason instanceof Error ? signal.reason : Object.assign(new Error("The operation was aborted"), {
195375
+ name: "AbortError"
195376
+ });
195377
+ cancelReader(abortError);
195378
+ rejectOnce(abortError);
194967
195379
  };
195380
+ if (signal.aborted) {
195381
+ abortCleanup();
195382
+ return;
195383
+ }
194968
195384
  signal.addEventListener("abort", abortCleanup, { once: true });
194969
195385
  }
194970
- reader.read().then((result) => {
194971
- clearTimeout(timeoutId);
194972
- if (signal && abortCleanup)
194973
- signal.removeEventListener("abort", abortCleanup);
194974
- if (result.value)
194975
- lastDataTime = Date.now();
194976
- resolve19(result);
194977
- }, (err2) => {
194978
- clearTimeout(timeoutId);
194979
- if (signal && abortCleanup)
194980
- signal.removeEventListener("abort", abortCleanup);
194981
- reject(err2);
194982
- });
195386
+ reader.read().then(resolveOnce, rejectOnce);
194983
195387
  });
194984
195388
  }
194985
195389
  const closeActiveContentBlock = async function* () {
@@ -195000,25 +195404,38 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195000
195404
  contentBlockIndex++;
195001
195405
  hasEmittedContentStart = false;
195002
195406
  };
195003
- const toolNameMayBeIncomplete = (name) => Boolean(name) && !advertisedToolNames.includes(name) && advertisedToolNames.some((toolName) => toolName.startsWith(name));
195004
- const canonicalizeFinalToolName = (toolCall) => {
195005
- const resolvedName = resolveToolNameByUniquePrefix(advertisedToolNames, toolCall.name);
195006
- if (resolvedName)
195007
- toolCall.name = resolvedName;
195407
+ const finalizeToolCall = (toolCall) => {
195408
+ const distinctIds = [...new Set(toolCall.idFragments.filter(Boolean))];
195409
+ if (distinctIds.length > 1) {
195410
+ return "conflicting_tool_call_ids";
195411
+ }
195412
+ if (distinctIds.length === 1)
195413
+ toolCall.id = distinctIds[0];
195414
+ const resolvedName = resolveStreamedToolName(advertisedToolNames, toolCall.nameFragments);
195415
+ toolCall.name = resolvedName.name;
195416
+ if (resolvedName.ambiguous)
195417
+ return "ambiguous_tool_name_fragments";
195418
+ if (!toolCall.id.trim())
195419
+ return "missing_tool_call_id";
195420
+ if (!toolCall.name.trim())
195421
+ return "missing_tool_name";
195422
+ if (toolCall.ambiguousArgumentFraming) {
195423
+ return "ambiguous_tool_argument_fragments";
195424
+ }
195425
+ if (hasInvalidToolArguments(toolCall.jsonBuffer, toolCall.name)) {
195426
+ return "malformed_structured_tool_arguments";
195427
+ }
195428
+ return null;
195008
195429
  };
195009
- const startToolCall = async function* (toolCall, force = false) {
195010
- if (toolCall.hasStarted || !toolCall.id || !toolCall.name)
195430
+ const startToolCall = async function* (toolCall) {
195431
+ if (toolCall.hasStarted || toolCall.index === null || !toolCall.id || !toolCall.name)
195011
195432
  return;
195012
- if (!force && toolNameMayBeIncomplete(toolCall.name))
195013
- return;
195014
- if (force)
195015
- canonicalizeFinalToolName(toolCall);
195433
+ const blockIndex = toolCall.index;
195016
195434
  toolCall.normalizeAtStop = hasToolFieldMapping(toolCall.name);
195017
195435
  toolCall.hasStarted = true;
195018
- toolCall.startedName = toolCall.name;
195019
195436
  yield {
195020
195437
  type: "content_block_start",
195021
- index: toolCall.index,
195438
+ index: blockIndex,
195022
195439
  content_block: {
195023
195440
  type: "tool_use",
195024
195441
  id: toolCall.id,
@@ -195033,7 +195450,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195033
195450
  if (!toolCall.normalizeAtStop && toolCall.jsonBuffer) {
195034
195451
  yield {
195035
195452
  type: "content_block_delta",
195036
- index: toolCall.index,
195453
+ index: blockIndex,
195037
195454
  delta: {
195038
195455
  type: "input_json_delta",
195039
195456
  partial_json: toolCall.jsonBuffer
@@ -195042,20 +195459,28 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195042
195459
  toolCall.emittedJsonLength = toolCall.jsonBuffer.length;
195043
195460
  }
195044
195461
  };
195045
- const flushReadyToolCalls = async function* (force = false) {
195046
- const orderedCalls = [...activeToolCalls.values()].sort((a2, b) => a2.index - b.index);
195047
- for (const toolCall of orderedCalls) {
195462
+ const orderedActiveToolCalls = () => [...activeToolCalls.entries()].sort(([leftProtocolIndex], [rightProtocolIndex]) => leftProtocolIndex - rightProtocolIndex).map(([, toolCall]) => toolCall);
195463
+ const flushReadyToolCalls = async function* () {
195464
+ for (const toolCall of orderedActiveToolCalls()) {
195465
+ if (toolCall.index === null) {
195466
+ toolCall.index = contentBlockIndex++;
195467
+ }
195048
195468
  if (!toolCall.hasStarted) {
195049
- if (!force && !toolCall.readyToStart)
195050
- break;
195051
- yield* startToolCall(toolCall, force);
195469
+ yield* startToolCall(toolCall);
195052
195470
  if (!toolCall.hasStarted) {
195053
- if (!force)
195054
- break;
195055
195471
  continue;
195056
195472
  }
195057
195473
  }
195058
- if (!toolCall.normalizeAtStop && toolCall.emittedJsonLength < toolCall.jsonBuffer.length) {
195474
+ if (toolCall.normalizeAtStop) {
195475
+ yield {
195476
+ type: "content_block_delta",
195477
+ index: toolCall.index,
195478
+ delta: {
195479
+ type: "input_json_delta",
195480
+ partial_json: JSON.stringify(normalizeToolArguments(toolCall.name, toolCall.jsonBuffer))
195481
+ }
195482
+ };
195483
+ } else if (toolCall.emittedJsonLength < toolCall.jsonBuffer.length) {
195059
195484
  yield {
195060
195485
  type: "content_block_delta",
195061
195486
  index: toolCall.index,
@@ -195066,7 +195491,61 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195066
195491
  };
195067
195492
  toolCall.emittedJsonLength = toolCall.jsonBuffer.length;
195068
195493
  }
195494
+ toolCall.hasStopped = true;
195495
+ yield { type: "content_block_stop", index: toolCall.index };
195496
+ }
195497
+ };
195498
+ const discardActiveToolCalls = (reason) => {
195499
+ if (activeToolCalls.size > 0) {
195500
+ logForDebugging(JSON.stringify({
195501
+ type: "discarded_uncommitted_tool_calls",
195502
+ model: model2,
195503
+ reason,
195504
+ count: activeToolCalls.size
195505
+ }), { level: "warn" });
195506
+ }
195507
+ activeToolCalls.clear();
195508
+ };
195509
+ const closeOpenBlocksForFailure = async function* () {
195510
+ if (hasEmittedContentStart) {
195511
+ yield* closeActiveContentBlock();
195512
+ }
195513
+ if (hasEmittedThinkingStart && !hasClosedThinking) {
195514
+ const thinkingBlockIndex = contentBlockIndex;
195515
+ hasClosedThinking = true;
195516
+ contentBlockIndex++;
195517
+ yield { type: "content_block_stop", index: thinkingBlockIndex };
195518
+ }
195519
+ for (const toolCall of orderedActiveToolCalls()) {
195520
+ if (!toolCall.hasStarted || toolCall.hasStopped || toolCall.index === null)
195521
+ continue;
195522
+ toolCall.hasStopped = true;
195523
+ yield { type: "content_block_stop", index: toolCall.index };
195524
+ }
195525
+ discardActiveToolCalls("stream_exception");
195526
+ };
195527
+ const resolveProtocolIndex = (toolCall, batchSize) => {
195528
+ if (Number.isInteger(toolCall.index))
195529
+ return toolCall.index;
195530
+ if (typeof toolCall.id === "string" && toolCall.id) {
195531
+ const matches = [...activeToolCalls.entries()].filter(([, active]) => active.idFragments.includes(toolCall.id));
195532
+ if (matches.length === 1)
195533
+ return matches[0][0];
195534
+ if (matches.length > 1) {
195535
+ throw new Error("Upstream omitted tool index and reused an ambiguous tool call ID; no tool was committed.");
195536
+ }
195537
+ if (activeToolCalls.size === 1 && batchSize === 1) {
195538
+ return activeToolCalls.keys().next().value;
195539
+ }
195540
+ return nextSyntheticProtocolIndex--;
195541
+ }
195542
+ if (batchSize === 1 && activeToolCalls.size === 1) {
195543
+ return activeToolCalls.keys().next().value;
195069
195544
  }
195545
+ if (batchSize === 1 && activeToolCalls.size === 0) {
195546
+ return nextSyntheticProtocolIndex--;
195547
+ }
195548
+ throw new Error("Upstream omitted tool indices for parallel calls; no tool was committed.");
195070
195549
  };
195071
195550
  try {
195072
195551
  while (true) {
@@ -195081,18 +195560,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195081
195560
  duration_ms: Date.now() - streamStartedAt,
195082
195561
  had_content: hasEmittedContentStart || hasEmittedThinkingStart
195083
195562
  }), { level: "error" });
195084
- if (hasEmittedContentStart) {
195085
- yield* closeActiveContentBlock();
195086
- }
195087
- if (hasEmittedThinkingStart && !hasClosedThinking) {
195088
- yield { type: "content_block_stop", index: contentBlockIndex };
195089
- }
195090
- for (const [, toolCall] of activeToolCalls) {
195091
- if (toolCall.hasStarted) {
195092
- yield { type: "content_block_stop", index: toolCall.index };
195093
- }
195094
- }
195095
- activeToolCalls.clear();
195563
+ discardActiveToolCalls("premature_eof");
195096
195564
  throw new Error(`Upstream stream closed without finish_reason after ${elapsedSec}s — likely a guard_proxy/vLLM disconnect. The session was interrupted, not completed.`);
195097
195565
  }
195098
195566
  break;
@@ -195103,15 +195571,23 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195103
195571
  buffer = lines.pop() ?? "";
195104
195572
  for (const line of lines) {
195105
195573
  const trimmed2 = line.trim();
195106
- if (!trimmed2 || trimmed2 === "data: [DONE]")
195574
+ if (!trimmed2)
195107
195575
  continue;
195576
+ if (trimmed2 === "data: [DONE]") {
195577
+ if (!hasProcessedFinishReason) {
195578
+ throw new Error("Upstream SSE emitted [DONE] without finish_reason; the response was not committed.");
195579
+ }
195580
+ sawDoneMarker = true;
195581
+ cancelReader("OpenAI SSE completed");
195582
+ break;
195583
+ }
195108
195584
  if (!trimmed2.startsWith("data: "))
195109
195585
  continue;
195110
195586
  let chunk;
195111
195587
  try {
195112
195588
  chunk = JSON.parse(trimmed2.slice(6));
195113
195589
  } catch {
195114
- continue;
195590
+ throw new Error("Upstream emitted invalid SSE JSON; the response was not committed.");
195115
195591
  }
195116
195592
  const routerStatus = chunk.router_status;
195117
195593
  if (typeof routerStatus === "string") {
@@ -195123,18 +195599,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195123
195599
  const inStreamError = chunk.error;
195124
195600
  if (inStreamError && typeof inStreamError === "object") {
195125
195601
  const message = typeof inStreamError.message === "string" ? inStreamError.message : "Provider returned an in-stream error";
195126
- if (hasEmittedContentStart) {
195127
- yield* closeActiveContentBlock();
195128
- }
195129
- if (hasEmittedThinkingStart && !hasClosedThinking) {
195130
- yield { type: "content_block_stop", index: contentBlockIndex };
195131
- }
195132
- for (const [, toolCall] of activeToolCalls) {
195133
- if (toolCall.hasStarted) {
195134
- yield { type: "content_block_stop", index: toolCall.index };
195135
- }
195136
- }
195137
- activeToolCalls.clear();
195602
+ discardActiveToolCalls("in_stream_error");
195138
195603
  const errorPayload = {
195139
195604
  error: {
195140
195605
  message,
@@ -195146,8 +195611,33 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195146
195611
  }
195147
195612
  const chunkUsage = convertChunkUsage(chunk.usage);
195148
195613
  for (const choice of chunk.choices ?? []) {
195149
- const delta = choice.delta;
195614
+ const delta = choice.delta ?? {};
195615
+ if (choice.finish_reason != null && (typeof choice.finish_reason !== "string" || !choice.finish_reason.trim())) {
195616
+ throw new Error("Upstream emitted a malformed finish_reason; the response was not committed.");
195617
+ }
195618
+ if (hasProcessedFinishReason) {
195619
+ const hasLateOutput = Object.entries(delta).some(([key, value2]) => {
195620
+ if (key === "role" || value2 == null || value2 === "")
195621
+ return false;
195622
+ if (Array.isArray(value2))
195623
+ return value2.length > 0;
195624
+ if (typeof value2 === "object") {
195625
+ return Object.keys(value2).length > 0;
195626
+ }
195627
+ return true;
195628
+ });
195629
+ if (hasLateOutput) {
195630
+ throw new Error("Upstream emitted assistant output after finish_reason; the late output was not committed.");
195631
+ }
195632
+ continue;
195633
+ }
195634
+ if (activeToolCalls.size > 0 && (delta.reasoning_content != null && delta.reasoning_content !== "" || delta.content != null && delta.content !== "")) {
195635
+ throw new Error("Upstream emitted text after a reserved tool block; the out-of-order response was not committed.");
195636
+ }
195150
195637
  if (delta.reasoning_content != null && delta.reasoning_content !== "") {
195638
+ if (hasClosedThinking || hasEmittedContentStart) {
195639
+ throw new Error("Upstream emitted reasoning after visible text; the out-of-order delta was not committed.");
195640
+ }
195151
195641
  if (!hasEmittedThinkingStart) {
195152
195642
  yield {
195153
195643
  type: "content_block_start",
@@ -195158,7 +195648,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195158
195648
  }
195159
195649
  yield {
195160
195650
  type: "content_block_delta",
195161
- index: hasClosedThinking ? contentBlockIndex - 1 : contentBlockIndex,
195651
+ index: contentBlockIndex,
195162
195652
  delta: {
195163
195653
  type: "thinking_delta",
195164
195654
  thinking: delta.reasoning_content
@@ -195189,9 +195679,31 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195189
195679
  }
195190
195680
  processStreamChunk(streamState, delta.content);
195191
195681
  }
195192
- if (delta.tool_calls) {
195193
- for (const tc of delta.tool_calls) {
195194
- let active = activeToolCalls.get(tc.index);
195682
+ const streamedToolCalls = [
195683
+ ...delta.tool_calls ?? []
195684
+ ];
195685
+ if (delta.function_call) {
195686
+ streamedToolCalls.push({
195687
+ index: -1,
195688
+ id: `${messageId}_function_call`,
195689
+ type: "function",
195690
+ function: delta.function_call
195691
+ });
195692
+ }
195693
+ if (streamedToolCalls.length > 0) {
195694
+ for (const tc of streamedToolCalls) {
195695
+ if (tc.id != null && typeof tc.id !== "string" || tc.function?.name != null && typeof tc.function.name !== "string" || tc.function?.arguments != null && typeof tc.function.arguments !== "string") {
195696
+ discardActiveToolCalls("malformed_tool_call_delta");
195697
+ throw new Error("Upstream returned malformed tool call fields; no tool was committed.");
195698
+ }
195699
+ let protocolIndex;
195700
+ try {
195701
+ protocolIndex = resolveProtocolIndex(tc, streamedToolCalls.length);
195702
+ } catch (error41) {
195703
+ discardActiveToolCalls("ambiguous_missing_tool_index");
195704
+ throw error41;
195705
+ }
195706
+ let active = activeToolCalls.get(protocolIndex);
195195
195707
  if (!active) {
195196
195708
  if (hasEmittedThinkingStart && !hasClosedThinking) {
195197
195709
  yield {
@@ -195207,40 +195719,52 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195207
195719
  active = {
195208
195720
  id: "",
195209
195721
  name: "",
195210
- index: contentBlockIndex++,
195722
+ idFragments: [],
195723
+ nameFragments: [],
195724
+ index: null,
195211
195725
  jsonBuffer: "",
195726
+ ambiguousArgumentFraming: false,
195212
195727
  emittedJsonLength: 0,
195213
195728
  normalizeAtStop: false,
195214
195729
  hasStarted: false,
195215
- readyToStart: false
195730
+ hasStopped: false
195216
195731
  };
195217
- activeToolCalls.set(tc.index, active);
195732
+ activeToolCalls.set(protocolIndex, active);
195218
195733
  }
195219
- if (tc.id && !active.id) {
195220
- active.id = tc.id;
195734
+ if (tc.id) {
195735
+ active.idFragments.push(tc.id);
195221
195736
  }
195222
195737
  const nameFragment = tc.function?.name;
195223
195738
  if (nameFragment) {
195224
- active.name = mergeStreamedToolName(active.name, nameFragment);
195739
+ active.nameFragments.push(nameFragment);
195225
195740
  }
195226
195741
  const argumentFragment = tc.function?.arguments;
195227
195742
  if (typeof argumentFragment === "string") {
195743
+ if (argumentFragment.length > 0 && active.jsonBuffer.length > 0 && argumentFragment.startsWith(active.jsonBuffer)) {
195744
+ active.ambiguousArgumentFraming = true;
195745
+ }
195746
+ if (active.jsonBuffer.length + argumentFragment.length > toolArgumentCharsLimit || totalBufferedToolArgumentChars + argumentFragment.length > toolArgumentCharsLimit) {
195747
+ discardActiveToolCalls("tool_arguments_limit_exceeded");
195748
+ throw new Error("Upstream tool arguments exceeded the configured safety limit; no tool was committed.");
195749
+ }
195228
195750
  active.jsonBuffer += argumentFragment;
195751
+ totalBufferedToolArgumentChars += argumentFragment.length;
195229
195752
  processStreamChunk(streamState, argumentFragment);
195230
195753
  }
195231
195754
  const thoughtSignature = tc.thought_signature;
195232
- const extraContent = tc.extra_content ? { ...tc.extra_content } : thoughtSignature ? { google: { thought_signature: thoughtSignature } } : undefined;
195233
- if (extraContent) {
195234
- active.extra_content = {
195235
- ...active.extra_content ?? {},
195236
- ...extraContent
195755
+ let extraContent;
195756
+ if (typeof thoughtSignature === "string" && thoughtSignature) {
195757
+ extraContent = {
195758
+ google: { thought_signature: thoughtSignature }
195237
195759
  };
195238
195760
  }
195239
- if (argumentFragment?.trim()) {
195240
- active.readyToStart = true;
195761
+ if (tc.extra_content) {
195762
+ extraContent = mergeOpaqueMetadata(extraContent, tc.extra_content);
195763
+ }
195764
+ if (extraContent) {
195765
+ active.extra_content = mergeOpaqueMetadata(active.extra_content, extraContent);
195241
195766
  }
195242
195767
  }
195243
- yield* flushReadyToolCalls();
195244
195768
  }
195245
195769
  if (choice.finish_reason && !hasProcessedFinishReason) {
195246
195770
  hasProcessedFinishReason = true;
@@ -195252,97 +195776,32 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195252
195776
  if (hasEmittedContentStart) {
195253
195777
  yield* closeActiveContentBlock();
195254
195778
  }
195255
- for (const toolCall of activeToolCalls.values()) {
195256
- canonicalizeFinalToolName(toolCall);
195257
- if (toolCall.emittedJsonLength === 0) {
195258
- toolCall.normalizeAtStop = hasToolFieldMapping(toolCall.name);
195259
- }
195260
- }
195261
- const startedBeforeFinish = new Set([...activeToolCalls.values()].filter((toolCall) => toolCall.hasStarted).map((toolCall) => toolCall.index));
195262
- yield* flushReadyToolCalls(true);
195263
- for (const [, tc] of activeToolCalls) {
195264
- const wasStarted = startedBeforeFinish.has(tc.index);
195265
- if (!tc.hasStarted) {
195266
- continue;
195267
- }
195268
- if (wasStarted && (tc.extra_content || tc.startedName !== tc.name)) {
195269
- yield {
195270
- type: "content_block_start",
195271
- index: tc.index,
195272
- content_block: {
195273
- type: "tool_use",
195274
- id: tc.id,
195275
- name: tc.name,
195276
- input: {},
195277
- ...tc.extra_content ? { extra_content: tc.extra_content } : {},
195278
- ...tc.extra_content?.google?.thought_signature ? {
195279
- signature: (tc.extra_content?.google).thought_signature
195280
- } : {}
195281
- }
195282
- };
195283
- }
195284
- if (tc.normalizeAtStop) {
195285
- let partialJson;
195286
- if (choice.finish_reason === "length") {
195287
- partialJson = tc.jsonBuffer;
195288
- } else {
195289
- const repairedStructuredJson = repairPossiblyTruncatedObjectJson(tc.jsonBuffer);
195290
- if (repairedStructuredJson) {
195291
- partialJson = repairedStructuredJson;
195292
- } else {
195293
- partialJson = JSON.stringify(normalizeToolArguments(tc.name, tc.jsonBuffer));
195294
- }
195295
- }
195296
- yield {
195297
- type: "content_block_delta",
195298
- index: tc.index,
195299
- delta: {
195300
- type: "input_json_delta",
195301
- partial_json: partialJson
195302
- }
195303
- };
195304
- yield { type: "content_block_stop", index: tc.index };
195305
- continue;
195306
- }
195307
- if (tc.emittedJsonLength < tc.jsonBuffer.length) {
195308
- yield {
195309
- type: "content_block_delta",
195310
- index: tc.index,
195311
- delta: {
195312
- type: "input_json_delta",
195313
- partial_json: tc.jsonBuffer.slice(tc.emittedJsonLength)
195314
- }
195315
- };
195316
- tc.emittedJsonLength = tc.jsonBuffer.length;
195317
- }
195318
- let suffixToAdd = "";
195319
- if (tc.jsonBuffer) {
195320
- try {
195321
- JSON.parse(tc.jsonBuffer);
195322
- } catch {
195323
- const str = tc.jsonBuffer.trimEnd();
195324
- for (const combo of JSON_REPAIR_SUFFIXES) {
195325
- try {
195326
- JSON.parse(str + combo);
195327
- suffixToAdd = combo;
195328
- break;
195329
- } catch {}
195779
+ const isToolFinish = choice.finish_reason === "tool_calls" || choice.finish_reason === "function_call";
195780
+ if (!isToolFinish) {
195781
+ discardActiveToolCalls(`finish_reason:${choice.finish_reason}`);
195782
+ } else {
195783
+ const invalidReasons = [];
195784
+ const finalizedToolCallIds = new Set;
195785
+ for (const toolCall of activeToolCalls.values()) {
195786
+ const invalidReason = finalizeToolCall(toolCall);
195787
+ if (invalidReason)
195788
+ invalidReasons.push(invalidReason);
195789
+ if (toolCall.id) {
195790
+ if (finalizedToolCallIds.has(toolCall.id)) {
195791
+ invalidReasons.push("duplicate_tool_call_id_across_indices");
195330
195792
  }
195793
+ finalizedToolCallIds.add(toolCall.id);
195331
195794
  }
195795
+ toolCall.normalizeAtStop = hasToolFieldMapping(toolCall.name);
195332
195796
  }
195333
- if (suffixToAdd) {
195334
- yield {
195335
- type: "content_block_delta",
195336
- index: tc.index,
195337
- delta: {
195338
- type: "input_json_delta",
195339
- partial_json: suffixToAdd
195340
- }
195341
- };
195797
+ if (activeToolCalls.size === 0 || invalidReasons.length > 0) {
195798
+ discardActiveToolCalls(invalidReasons.length > 0 ? invalidReasons.join(",") : "tool_finish_without_tool_call");
195799
+ throw new Error(`Upstream returned ${choice.finish_reason} with an incomplete or ambiguous tool call; no tool was committed.`);
195342
195800
  }
195343
- yield { type: "content_block_stop", index: tc.index };
195801
+ yield* flushReadyToolCalls();
195802
+ activeToolCalls.clear();
195344
195803
  }
195345
- const stopReason = choice.finish_reason === "tool_calls" ? "tool_use" : choice.finish_reason === "length" ? "max_tokens" : "end_turn";
195804
+ const stopReason = choice.finish_reason === "tool_calls" || choice.finish_reason === "function_call" ? "tool_use" : choice.finish_reason === "length" ? "max_tokens" : "end_turn";
195346
195805
  if (choice.finish_reason === "content_filter" || choice.finish_reason === "safety") {
195347
195806
  if (!hasEmittedContentStart) {
195348
195807
  yield {
@@ -195382,6 +195841,9 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195382
195841
  }
195383
195842
  };
195384
195843
  }
195844
+ if (hasEmittedContentStart) {
195845
+ yield* closeActiveContentBlock();
195846
+ }
195385
195847
  lastStopReason = stopReason;
195386
195848
  yield {
195387
195849
  type: "message_delta",
@@ -195402,9 +195864,19 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, a
195402
195864
  hasEmittedFinalUsage = true;
195403
195865
  }
195404
195866
  }
195867
+ if (sawDoneMarker)
195868
+ break;
195405
195869
  }
195870
+ } catch (error41) {
195871
+ yield* closeOpenBlocksForFailure();
195872
+ throw error41;
195406
195873
  } finally {
195407
- reader.releaseLock();
195874
+ await pendingReaderCancellation;
195875
+ try {
195876
+ reader.releaseLock();
195877
+ } catch (releaseError) {
195878
+ logForDebugging(`Failed to release OpenAI stream reader after cancellation: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`, { level: "warn" });
195879
+ }
195408
195880
  }
195409
195881
  const stats = getStreamStats(streamState);
195410
195882
  if (stats.totalChunks > 0) {
@@ -196010,6 +196482,10 @@ class OpenAIShimMessages {
196010
196482
  }
196011
196483
  _convertNonStreamingResponse(data, model2, advertisedToolNames = []) {
196012
196484
  const choice = data.choices?.[0];
196485
+ const toolArgumentCharsLimit = maxBufferedToolArgumentChars2();
196486
+ if (!choice || typeof choice.finish_reason !== "string" || !choice.finish_reason.trim()) {
196487
+ throw new Error("Upstream non-streaming response ended without a terminal choice; no output was committed.");
196488
+ }
196013
196489
  const content = [];
196014
196490
  const reasoningText = choice?.message?.reasoning_content;
196015
196491
  if (typeof reasoningText === "string" && reasoningText) {
@@ -196037,8 +196513,34 @@ class OpenAIShimMessages {
196037
196513
  });
196038
196514
  }
196039
196515
  }
196040
- if (choice?.message?.tool_calls) {
196041
- for (const tc of choice.message.tool_calls) {
196516
+ const isToolFinish = choice?.finish_reason === "tool_calls" || choice?.finish_reason === "function_call";
196517
+ const completedToolCalls = [
196518
+ ...choice?.message?.tool_calls ?? [],
196519
+ ...choice?.message?.function_call ? [
196520
+ {
196521
+ id: `${data.id ?? makeMessageId2()}_function_call`,
196522
+ function: choice.message.function_call
196523
+ }
196524
+ ] : []
196525
+ ];
196526
+ if (isToolFinish) {
196527
+ if (completedToolCalls.length === 0) {
196528
+ throw new Error("Upstream returned a tool finish reason without a tool call; no tool was committed.");
196529
+ }
196530
+ const completedToolCallIds = new Set;
196531
+ let completedToolArgumentChars = 0;
196532
+ for (const tc of completedToolCalls) {
196533
+ const resolvedToolName = typeof tc.function?.name === "string" ? resolveToolNameByUniquePrefix(advertisedToolNames, tc.function.name) : undefined;
196534
+ if (typeof tc.id !== "string" || !tc.id.trim() || typeof tc.function?.name !== "string" || !tc.function.name.trim() || typeof tc.function?.arguments !== "string" || tc.function.arguments.length > toolArgumentCharsLimit || advertisedToolNames.length > 0 && !resolvedToolName || hasInvalidToolArguments(tc.function.arguments, resolvedToolName ?? tc.function.name) || completedToolCallIds.has(tc.id)) {
196535
+ throw new Error("Upstream returned malformed, incomplete, or duplicate tool calls; no tool was committed.");
196536
+ }
196537
+ completedToolArgumentChars += tc.function.arguments.length;
196538
+ if (completedToolArgumentChars > toolArgumentCharsLimit) {
196539
+ throw new Error("Upstream tool arguments exceeded the configured safety limit; no tool was committed.");
196540
+ }
196541
+ completedToolCallIds.add(tc.id);
196542
+ }
196543
+ for (const tc of completedToolCalls) {
196042
196544
  const toolName = resolveToolNameByUniquePrefix(advertisedToolNames, tc.function.name) ?? tc.function.name;
196043
196545
  const input = normalizeToolArguments(toolName, tc.function.arguments);
196044
196546
  content.push({
@@ -196047,11 +196549,20 @@ class OpenAIShimMessages {
196047
196549
  name: toolName,
196048
196550
  input,
196049
196551
  ...tc.extra_content ? { extra_content: tc.extra_content } : {},
196050
- ...tc.extra_content?.google?.thought_signature ? { signature: tc.extra_content.google.thought_signature } : {}
196552
+ ...tc.extra_content?.google?.thought_signature ? {
196553
+ signature: (tc.extra_content?.google).thought_signature
196554
+ } : {}
196051
196555
  });
196052
196556
  }
196557
+ } else if (completedToolCalls.length > 0) {
196558
+ logForDebugging(JSON.stringify({
196559
+ type: "discarded_uncommitted_tool_calls",
196560
+ model: model2,
196561
+ reason: `finish_reason:${choice?.finish_reason ?? "missing"}`,
196562
+ count: completedToolCalls.length
196563
+ }), { level: "warn" });
196053
196564
  }
196054
- const stopReason = choice?.finish_reason === "tool_calls" ? "tool_use" : choice?.finish_reason === "length" ? "max_tokens" : "end_turn";
196565
+ const stopReason = choice?.finish_reason === "tool_calls" || choice?.finish_reason === "function_call" ? "tool_use" : choice?.finish_reason === "length" ? "max_tokens" : "end_turn";
196055
196566
  if (choice?.finish_reason === "content_filter" || choice?.finish_reason === "safety") {
196056
196567
  content.push({
196057
196568
  type: "text",
@@ -196093,7 +196604,7 @@ function createOpenAIShimClient(options2) {
196093
196604
  messages: beta.messages
196094
196605
  };
196095
196606
  }
196096
- var GITHUB_429_MAX_RETRIES = 3, GITHUB_429_BASE_DELAY_SEC = 1, GITHUB_429_MAX_DELAY_SEC = 32, GEMINI_API_HOST = "generativelanguage.googleapis.com", VERBOO_SESSION_HEADER = "X-Verboo-Session-Id", COPILOT_HEADERS2, JSON_REPAIR_SUFFIXES, routerStatusHandler = null, routerStatusHandlerGeneration = 0, activeWarmingHints, OpenAIShimStream;
196607
+ var GITHUB_429_MAX_RETRIES = 3, GITHUB_429_BASE_DELAY_SEC = 1, GITHUB_429_MAX_DELAY_SEC = 32, GEMINI_API_HOST = "generativelanguage.googleapis.com", VERBOO_SESSION_HEADER = "X-Verboo-Session-Id", COPILOT_HEADERS2, DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS2, routerStatusHandler = null, routerStatusHandlerGeneration = 0, activeWarmingHints, OpenAIShimStream;
196097
196608
  var init_openaiShim = __esm(() => {
196098
196609
  init_sdk();
196099
196610
  init_Tool();
@@ -196127,18 +196638,7 @@ var init_openaiShim = __esm(() => {
196127
196638
  "Editor-Plugin-Version": "copilot-chat/0.26.7",
196128
196639
  "Copilot-Integration-Id": "vscode-chat"
196129
196640
  };
196130
- JSON_REPAIR_SUFFIXES = [
196131
- "}",
196132
- '"}',
196133
- "]}",
196134
- '"]}',
196135
- "}}",
196136
- '"}}',
196137
- "]}}",
196138
- '"]}}',
196139
- '"]}]}',
196140
- "}]}"
196141
- ];
196641
+ DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS2 = 64 * 1024 * 1024;
196142
196642
  activeWarmingHints = new Set;
196143
196643
  OpenAIShimStream = class OpenAIShimStream {
196144
196644
  generator;
@@ -391284,7 +391784,7 @@ function getAnthropicEnvMetadata() {
391284
391784
  function getBuildAgeMinutes() {
391285
391785
  if (false)
391286
391786
  ;
391287
- const buildTime = new Date("2026-08-18T16:18:48.339Z").getTime();
391787
+ const buildTime = new Date("2026-08-25T17:16:19.335Z").getTime();
391288
391788
  if (isNaN(buildTime))
391289
391789
  return;
391290
391790
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -405590,7 +406090,7 @@ function normalizeContentFromAPI(contentBlocks, tools, agentId) {
405590
406090
  normalizedInput = contentBlock.input;
405591
406091
  }
405592
406092
  const resolvedTool = findToolByNameOrUniquePrefix(tools, contentBlock.name);
405593
- const normalizedToolName = resolvedTool && resolvedTool.name !== contentBlock.name && !resolvedTool.aliases?.includes(contentBlock.name) && resolvedTool.name.startsWith(contentBlock.name) ? resolvedTool.name : contentBlock.name;
406093
+ const normalizedToolName = resolvedTool && resolvedTool.name !== contentBlock.name && !resolvedTool.aliases?.includes(contentBlock.name) && resolvedTool.name.toLowerCase().startsWith(contentBlock.name.toLowerCase()) ? resolvedTool.name : contentBlock.name;
405594
406094
  if (typeof normalizedInput === "object" && normalizedInput !== null) {
405595
406095
  if (resolvedTool) {
405596
406096
  try {
@@ -405766,6 +406266,7 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405766
406266
  }
405767
406267
  if (isSyntheticApiErrorMessage(message)) {
405768
406268
  onSetStreamMode("tool-use");
406269
+ onStreamingToolUses(() => []);
405769
406270
  }
405770
406271
  }
405771
406272
  onStreamingText?.(() => null);
@@ -405777,6 +406278,7 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405777
406278
  return;
405778
406279
  }
405779
406280
  if (message.event.type === "message_start") {
406281
+ onStreamingToolUses(() => []);
405780
406282
  if (message.ttftMs != null) {
405781
406283
  onApiMetrics?.({ ttftMs: message.ttftMs });
405782
406284
  }
@@ -405802,14 +406304,28 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405802
406304
  onSetStreamMode("tool-input");
405803
406305
  const contentBlock = message.event.content_block;
405804
406306
  const index = message.event.index;
405805
- onStreamingToolUses((_) => [
405806
- ..._,
405807
- {
405808
- index,
405809
- contentBlock,
405810
- unparsedToolInput: ""
406307
+ onStreamingToolUses((current) => {
406308
+ const exactMatchIndex = current.findIndex((toolUse) => toolUse.index === index && toolUse.contentBlock.id === contentBlock.id);
406309
+ if (exactMatchIndex !== -1) {
406310
+ return current.map((toolUse, currentIndex) => currentIndex === exactMatchIndex ? { ...toolUse, contentBlock } : toolUse);
406311
+ }
406312
+ const reusedIndex = current.findIndex((toolUse) => toolUse.index === index);
406313
+ if (reusedIndex !== -1) {
406314
+ return current.map((toolUse, currentIndex) => currentIndex === reusedIndex ? {
406315
+ index,
406316
+ contentBlock,
406317
+ unparsedToolInput: ""
406318
+ } : toolUse);
405811
406319
  }
405812
- ]);
406320
+ return [
406321
+ ...current,
406322
+ {
406323
+ index,
406324
+ contentBlock,
406325
+ unparsedToolInput: ""
406326
+ }
406327
+ ];
406328
+ });
405813
406329
  return;
405814
406330
  }
405815
406331
  case "server_tool_use":
@@ -405844,13 +406360,10 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405844
406360
  if (!element) {
405845
406361
  return _;
405846
406362
  }
405847
- return [
405848
- ..._.filter((_2) => _2 !== element),
405849
- {
405850
- ...element,
405851
- unparsedToolInput: element.unparsedToolInput + delta
405852
- }
405853
- ];
406363
+ return _.map((toolUse) => toolUse === element ? {
406364
+ ...toolUse,
406365
+ unparsedToolInput: toolUse.unparsedToolInput + delta
406366
+ } : toolUse);
405854
406367
  });
405855
406368
  return;
405856
406369
  }
@@ -410827,6 +411340,15 @@ import { mkdir as mkdir26 } from "fs/promises";
410827
411340
  import { createServer as createServer4 } from "http";
410828
411341
  import { join as join100 } from "path";
410829
411342
  import { parse as parse13 } from "url";
411343
+ function getRefreshCredentialInvalidationScope(error42) {
411344
+ if (error42 instanceof InvalidClientError || error42 instanceof UnauthorizedClientError) {
411345
+ return "all";
411346
+ }
411347
+ if (error42 instanceof InvalidGrantError) {
411348
+ return "tokens";
411349
+ }
411350
+ return;
411351
+ }
410830
411352
  function redactSensitiveUrlParams(url3) {
410831
411353
  try {
410832
411354
  const parsedUrl = new URL(url3);
@@ -411633,7 +412155,7 @@ class ClaudeAuthProvider {
411633
412155
  const storage3 = getSecureStorage();
411634
412156
  const data = await storage3.readAsync();
411635
412157
  const serverKey = getServerKey(this.serverName, this.serverConfig);
411636
- const tokenData = data?.mcpOAuth?.[serverKey];
412158
+ let tokenData = data?.mcpOAuth?.[serverKey];
411637
412159
  if (isXaaEnabled() && this.serverConfig.oauth?.xaa && !tokenData?.refreshToken && (!tokenData?.accessToken || (tokenData.expiresAt - Date.now()) / 1000 <= 300)) {
411638
412160
  if (!this._refreshInProgress) {
411639
412161
  logMCPDebug(this.serverName, tokenData ? `XAA: access_token expiring, attempting silent exchange` : `XAA: no access_token yet, attempting silent exchange`);
@@ -411678,22 +412200,31 @@ class ClaudeAuthProvider {
411678
412200
  logMCPDebug(this.serverName, `Token refreshed successfully`);
411679
412201
  return refreshed;
411680
412202
  }
411681
- logMCPDebug(this.serverName, `Token refresh failed, returning current tokens`);
412203
+ logMCPDebug(this.serverName, `Token refresh did not return new tokens; checking stored credentials`);
412204
+ tokenData = (await storage3.readAsync())?.mcpOAuth?.[serverKey];
412205
+ if (!tokenData?.accessToken) {
412206
+ logMCPDebug(this.serverName, `Refresh invalidated stored credentials; authorization required`);
412207
+ return;
412208
+ }
411682
412209
  } catch (error42) {
411683
412210
  logMCPDebug(this.serverName, `Token refresh error: ${errorMessage(error42)}`);
411684
412211
  }
411685
412212
  }
412213
+ if (!tokenData) {
412214
+ return;
412215
+ }
412216
+ const currentExpiresIn = (tokenData.expiresAt - Date.now()) / 1000;
411686
412217
  const tokens = {
411687
412218
  access_token: tokenData.accessToken,
411688
412219
  refresh_token: needsStepUp ? undefined : tokenData.refreshToken,
411689
- expires_in: expiresIn,
412220
+ expires_in: currentExpiresIn,
411690
412221
  scope: tokenData.scope,
411691
412222
  token_type: "Bearer"
411692
412223
  };
411693
412224
  logMCPDebug(this.serverName, `Returning tokens`);
411694
412225
  logMCPDebug(this.serverName, `Token length: ${tokens.access_token?.length}`);
411695
412226
  logMCPDebug(this.serverName, `Has refresh token: ${!!tokens.refresh_token}`);
411696
- logMCPDebug(this.serverName, `Expires in: ${Math.floor(expiresIn)}s`);
412227
+ logMCPDebug(this.serverName, `Expires in: ${Math.floor(currentExpiresIn)}s`);
411697
412228
  return tokens;
411698
412229
  }
411699
412230
  async saveTokens(tokens) {
@@ -412070,8 +412601,10 @@ class ClaudeAuthProvider {
412070
412601
  emitRefreshEvent("failure", "no_tokens_returned");
412071
412602
  return;
412072
412603
  } catch (error42) {
412073
- if (error42 instanceof InvalidGrantError) {
412074
- logMCPDebug(this.serverName, `Token refresh failed with invalid_grant: ${error42.message}`);
412604
+ const invalidationScope = getRefreshCredentialInvalidationScope(error42);
412605
+ if (invalidationScope) {
412606
+ const clientRegistrationInvalid = invalidationScope === "all";
412607
+ logMCPDebug(this.serverName, clientRegistrationInvalid ? `Token refresh rejected the OAuth client (${error42 instanceof OAuthError ? error42.errorCode : "invalid_client"}): ${errorMessage(error42)}` : `Token refresh failed with invalid_grant: ${errorMessage(error42)}`);
412075
412608
  clearKeychainCache();
412076
412609
  const storage3 = getSecureStorage();
412077
412610
  const data = storage3.read();
@@ -412090,9 +412623,9 @@ class ClaudeAuthProvider {
412090
412623
  };
412091
412624
  }
412092
412625
  }
412093
- logMCPDebug(this.serverName, `No valid tokens in storage, clearing stored tokens`);
412094
- await this.invalidateCredentials("tokens");
412095
- emitRefreshEvent("failure", "invalid_grant");
412626
+ logMCPDebug(this.serverName, clientRegistrationInvalid ? `No valid tokens in storage, clearing the stale OAuth client registration` : `No valid tokens in storage, clearing stored tokens`);
412627
+ await this.invalidateCredentials(invalidationScope);
412628
+ emitRefreshEvent("failure", clientRegistrationInvalid ? "invalid_client" : "invalid_grant");
412096
412629
  return;
412097
412630
  }
412098
412631
  const isTimeoutError = error42 instanceof Error && /timeout|timed out|etimedout|econnreset/i.test(error42.message);
@@ -433087,7 +433620,7 @@ function buildPrimarySection() {
433087
433620
  });
433088
433621
  return [{
433089
433622
  label: "Version",
433090
- value: "0.15.16"
433623
+ value: "0.15.17"
433091
433624
  }, {
433092
433625
  label: "Session name",
433093
433626
  value: nameValue
@@ -447017,7 +447550,7 @@ function getReleaseTagUrl(version2 = publicBuildVersion) {
447017
447550
  return `${VERBOO_RELEASES_URL}/tag/v${normalizePublicVersion(version2)}`;
447018
447551
  }
447019
447552
  function getPublicBuildVersion() {
447020
- return "0.15.16";
447553
+ return "0.15.17";
447021
447554
  }
447022
447555
  var import_semver10, VERBOO_RELEASES_URL = "https://github.com/verbeux-ai/code/releases", fallbackBuildVersion, publicBuildVersion;
447023
447556
  var init_version = __esm(() => {
@@ -474015,7 +474548,7 @@ var import_react_compiler_runtime198, React97, import_react155, jsx_runtime267,
474015
474548
  const msg_1 = createAssistantMessage({
474016
474549
  content: [streamingToolUse.contentBlock]
474017
474550
  });
474018
- msg_1.uuid = deriveUUID(streamingToolUse.contentBlock.id, 0);
474551
+ msg_1.uuid = deriveUUID(streamingToolUse.contentBlock.id, streamingToolUse.index);
474019
474552
  return normalizeMessages([msg_1]);
474020
474553
  }), [streamingToolUsesWithoutInProgress]);
474021
474554
  const isTranscriptMode = screen === "transcript";
@@ -498411,7 +498944,7 @@ var init_bridge_kick = __esm(() => {
498411
498944
  var call66 = async () => {
498412
498945
  return {
498413
498946
  type: "text",
498414
- value: `${"99.0.0"} (built ${"2026-08-18T16:18:48.339Z"})`
498947
+ value: `${"99.0.0"} (built ${"2026-08-25T17:16:19.335Z"})`
498415
498948
  };
498416
498949
  }, version2, version_default;
498417
498950
  var init_version2 = __esm(() => {
@@ -500233,6 +500766,8 @@ var init_verbooInChrome = __esm(() => {
500233
500766
  VERBOO_IN_CHROME_TOOL_NAMES = [
500234
500767
  "navigate",
500235
500768
  "read_page",
500769
+ "find",
500770
+ "extract_page_content",
500236
500771
  "structured_extract",
500237
500772
  "click",
500238
500773
  "type",
@@ -522147,7 +522682,7 @@ function printStartupScreen(modelOverride) {
522147
522682
  const home = process.env.HOME || process.env.USERPROFILE || "";
522148
522683
  const cwd2 = process.cwd();
522149
522684
  const displayCwd = home && cwd2.startsWith(home) ? `~${cwd2.slice(home.length)}` : cwd2;
522150
- const version3 = "0.15.16";
522685
+ const version3 = "0.15.17";
522151
522686
  const columns = process.stdout.columns ?? STARTUP_DEFAULT_COLUMNS;
522152
522687
  process.stdout.write(renderStartupScreen(p, version3, displayCwd, columns));
522153
522688
  }
@@ -541376,7 +541911,7 @@ var init_routerRateLimitHook = __esm(() => {
541376
541911
  function getSemverPart(version3) {
541377
541912
  return `${import_semver13.major(version3, { loose: true })}.${import_semver13.minor(version3, { loose: true })}.${import_semver13.patch(version3, { loose: true })}`;
541378
541913
  }
541379
- function useUpdateNotification(updatedVersion, initialVersion = "0.15.16") {
541914
+ function useUpdateNotification(updatedVersion, initialVersion = "0.15.17") {
541380
541915
  const [lastNotifiedSemver, setLastNotifiedSemver] = import_react226.useState(() => getSemverPart(initialVersion));
541381
541916
  const [pendingNotification2, setPendingNotification] = import_react226.useState(null);
541382
541917
  if (updatedVersion) {
@@ -541416,7 +541951,7 @@ function AutoUpdater({
541416
541951
  return;
541417
541952
  }
541418
541953
  if (false) {}
541419
- const currentVersion = "0.15.16";
541954
+ const currentVersion = "0.15.17";
541420
541955
  const channel2 = getInitialSettings()?.autoUpdatesChannel ?? "latest";
541421
541956
  let latestVersion = await getLatestVersion(channel2);
541422
541957
  const isDisabled = isAutoUpdaterDisabled();
@@ -541769,17 +542304,17 @@ function PackageManagerAutoUpdater(t0) {
541769
542304
  const maxVersion = await getMaxVersion();
541770
542305
  if (maxVersion && latest && gt(latest, maxVersion)) {
541771
542306
  logForDebugging(`PackageManagerAutoUpdater: maxVersion ${maxVersion} is set, capping update from ${latest} to ${maxVersion}`);
541772
- if (gte("0.15.16", maxVersion)) {
541773
- logForDebugging(`PackageManagerAutoUpdater: current version ${"0.15.16"} is already at or above maxVersion ${maxVersion}, skipping update`);
542307
+ if (gte("0.15.17", maxVersion)) {
542308
+ logForDebugging(`PackageManagerAutoUpdater: current version ${"0.15.17"} is already at or above maxVersion ${maxVersion}, skipping update`);
541774
542309
  setUpdateAvailable(false);
541775
542310
  return;
541776
542311
  }
541777
542312
  latest = maxVersion;
541778
542313
  }
541779
- const hasUpdate = latest && !gte("0.15.16", latest) && !shouldSkipVersion(latest);
542314
+ const hasUpdate = latest && !gte("0.15.17", latest) && !shouldSkipVersion(latest);
541780
542315
  setUpdateAvailable(!!hasUpdate);
541781
542316
  if (hasUpdate) {
541782
- logForDebugging(`PackageManagerAutoUpdater: Update available ${"0.15.16"} -> ${latest}`);
542317
+ logForDebugging(`PackageManagerAutoUpdater: Update available ${"0.15.17"} -> ${latest}`);
541783
542318
  }
541784
542319
  };
541785
542320
  $2[0] = t1;
@@ -541813,7 +542348,7 @@ function PackageManagerAutoUpdater(t0) {
541813
542348
  wrap: "truncate",
541814
542349
  children: [
541815
542350
  "currentVersion: ",
541816
- "0.15.16"
542351
+ "0.15.17"
541817
542352
  ]
541818
542353
  });
541819
542354
  $2[3] = verbose;
@@ -557503,10 +558038,10 @@ async function autoUpdateCliInBackground() {
557503
558038
  return;
557504
558039
  const channel2 = getInitialSettings()?.autoUpdatesChannel ?? "latest";
557505
558040
  const latest = await getLatestVersion(channel2);
557506
- if (!latest || gte("0.15.16", latest))
558041
+ if (!latest || gte("0.15.17", latest))
557507
558042
  return;
557508
558043
  writeToStdout(`
557509
- Nova versão disponível: ${latest} (atual: ${"0.15.16"})
558044
+ Nova versão disponível: ${latest} (atual: ${"0.15.17"})
557510
558045
  `);
557511
558046
  writeToStdout(`Atualizando automaticamente...
557512
558047
  `);
@@ -574944,7 +575479,7 @@ var init_ApproveApiKey = __esm(() => {
574944
575479
 
574945
575480
  // src/components/LogoV2/WelcomeV2.tsx
574946
575481
  function WelcomeV2() {
574947
- const version3 = "0.15.16";
575482
+ const version3 = "0.15.17";
574948
575483
  return /* @__PURE__ */ jsx_runtime476.jsxs(ThemedBox_default, {
574949
575484
  flexDirection: "column",
574950
575485
  marginY: 1,
@@ -593892,7 +594427,7 @@ __export(exports_update, {
593892
594427
  });
593893
594428
  async function update() {
593894
594429
  logEvent("tengu_update_check", {});
593895
- writeToStdout(`Current version: ${"0.15.16"}
594430
+ writeToStdout(`Current version: ${"0.15.17"}
593896
594431
  `);
593897
594432
  const channel2 = getInitialSettings()?.autoUpdatesChannel ?? "latest";
593898
594433
  writeToStdout(`Checking for updates to ${channel2} version...
@@ -593977,8 +594512,8 @@ async function update() {
593977
594512
  writeToStdout(`Verboo Code is managed by Homebrew.
593978
594513
  `);
593979
594514
  const latest = await getLatestVersion(channel2);
593980
- if (latest && !gte("0.15.16", latest)) {
593981
- writeToStdout(`Update available: ${"0.15.16"} → ${latest}
594515
+ if (latest && !gte("0.15.17", latest)) {
594516
+ writeToStdout(`Update available: ${"0.15.17"} → ${latest}
593982
594517
  `);
593983
594518
  writeToStdout(`
593984
594519
  `);
@@ -593994,8 +594529,8 @@ async function update() {
593994
594529
  writeToStdout(`Verboo Code is managed by winget.
593995
594530
  `);
593996
594531
  const latest = await getLatestVersion(channel2);
593997
- if (latest && !gte("0.15.16", latest)) {
593998
- writeToStdout(`Update available: ${"0.15.16"} → ${latest}
594532
+ if (latest && !gte("0.15.17", latest)) {
594533
+ writeToStdout(`Update available: ${"0.15.17"} → ${latest}
593999
594534
  `);
594000
594535
  writeToStdout(`
594001
594536
  `);
@@ -594011,8 +594546,8 @@ async function update() {
594011
594546
  writeToStdout(`Verboo Code is managed by apk.
594012
594547
  `);
594013
594548
  const latest = await getLatestVersion(channel2);
594014
- if (latest && !gte("0.15.16", latest)) {
594015
- writeToStdout(`Update available: ${"0.15.16"} → ${latest}
594549
+ if (latest && !gte("0.15.17", latest)) {
594550
+ writeToStdout(`Update available: ${"0.15.17"} → ${latest}
594016
594551
  `);
594017
594552
  writeToStdout(`
594018
594553
  `);
@@ -594065,11 +594600,11 @@ async function update() {
594065
594600
  `);
594066
594601
  await gracefulShutdown(1);
594067
594602
  }
594068
- if (result.latestVersion === "0.15.16") {
594069
- writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.16"})`) + `
594603
+ if (result.latestVersion === "0.15.17") {
594604
+ writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.17"})`) + `
594070
594605
  `);
594071
594606
  } else {
594072
- writeToStdout(source_default.green(`Successfully updated from ${"0.15.16"} to version ${result.latestVersion}`) + `
594607
+ writeToStdout(source_default.green(`Successfully updated from ${"0.15.17"} to version ${result.latestVersion}`) + `
594073
594608
  `);
594074
594609
  await regenerateCompletionCache();
594075
594610
  }
@@ -594129,12 +594664,12 @@ async function update() {
594129
594664
  `);
594130
594665
  await gracefulShutdown(1);
594131
594666
  }
594132
- if (latestVersion === "0.15.16") {
594133
- writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.16"})`) + `
594667
+ if (latestVersion === "0.15.17") {
594668
+ writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.17"})`) + `
594134
594669
  `);
594135
594670
  await gracefulShutdown(0);
594136
594671
  }
594137
- writeToStdout(`New version available: ${latestVersion} (current: ${"0.15.16"})
594672
+ writeToStdout(`New version available: ${latestVersion} (current: ${"0.15.17"})
594138
594673
  `);
594139
594674
  writeToStdout(`Installing update...
594140
594675
  `);
@@ -594179,7 +594714,7 @@ async function update() {
594179
594714
  logForDebugging(`update: Installation status: ${status2}`);
594180
594715
  switch (status2) {
594181
594716
  case "success":
594182
- writeToStdout(source_default.green(`Successfully updated from ${"0.15.16"} to version ${latestVersion}`) + `
594717
+ writeToStdout(source_default.green(`Successfully updated from ${"0.15.17"} to version ${latestVersion}`) + `
594183
594718
  `);
594184
594719
  await regenerateCompletionCache();
594185
594720
  break;
@@ -595518,7 +596053,7 @@ ${chromeSystemPrompt}` : chromeSystemPrompt;
595518
596053
  is_native_binary: isInBundledMode()
595519
596054
  });
595520
596055
  logMemoryDiagnostics("start", {
595521
- version: "0.15.16",
596056
+ version: "0.15.17",
595522
596057
  debug: debug2,
595523
596058
  debugToStderr,
595524
596059
  print: print ?? false,
@@ -596329,7 +596864,7 @@ Usage: verboo --remote "your task description"`, () => gracefulShutdown(1));
596329
596864
  pendingHookMessages
596330
596865
  }, renderAndRun);
596331
596866
  }
596332
- }).version(`0.15.16 (${cliDesc})`, "-v, --version", "Output the version number");
596867
+ }).version(`0.15.17 (${cliDesc})`, "-v, --version", "Output the version number");
596333
596868
  program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
596334
596869
  program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
596335
596870
  if (canUserConfigureAdvisor()) {
@@ -596944,7 +597479,7 @@ if (false) {}
596944
597479
  async function main2() {
596945
597480
  const args = process.argv.slice(2);
596946
597481
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
596947
- console.log(`${"0.15.16"} (Verboo Code)`);
597482
+ console.log(`${"0.15.17"} (Verboo Code)`);
596948
597483
  return;
596949
597484
  }
596950
597485
  if (!IS_VERBOO_CLI && args.includes("--provider")) {
@@ -597118,4 +597653,4 @@ async function main2() {
597118
597653
  }
597119
597654
  main2();
597120
597655
 
597121
- //# debugId=F84B47F82CF95DAE64756E2164756E21
597656
+ //# debugId=5CD1B0C278B044C764756E2164756E21