@verboo/code 0.15.15 → 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 +1247 -498
  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.15";
118689
+ const version2 = "0.15.17";
118690
118690
  return `verboo-code/${version2}`;
118691
118691
  }
118692
118692
 
@@ -152397,19 +152397,34 @@ function toolMatchesName(tool, name) {
152397
152397
  function findToolByName(tools, name) {
152398
152398
  return tools.find((t) => toolMatchesName(t, name));
152399
152399
  }
152400
- function findToolByNameOrUniquePrefix(tools, name) {
152401
- const exactMatch = findToolByName(tools, name);
152402
- if (exactMatch)
152403
- return exactMatch;
152404
- if (name.length < 3 || name.startsWith("mcp__"))
152400
+ function resolveToolNameByUniquePrefix(toolNames, name) {
152401
+ const uniqueToolNames = [...new Set(toolNames)];
152402
+ if (uniqueToolNames.includes(name))
152403
+ return name;
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 = tools.filter((tool) => tool.name.startsWith(name));
152407
- const oneCharacterCompletions = prefixMatches.filter((tool) => tool.name.length === name.length + 1);
152411
+ const prefixMatches = uniqueToolNames.filter((toolName) => {
152412
+ const normalizedToolName = toolName.toLowerCase();
152413
+ return !normalizedToolName.startsWith("mcp__") && normalizedToolName.startsWith(normalizedName);
152414
+ });
152415
+ const oneCharacterCompletions = prefixMatches.filter((toolName) => toolName.length === name.length + 1);
152408
152416
  if (oneCharacterCompletions.length === 1) {
152409
152417
  return oneCharacterCompletions[0];
152410
152418
  }
152411
152419
  return prefixMatches.length === 1 ? prefixMatches[0] : undefined;
152412
152420
  }
152421
+ function findToolByNameOrUniquePrefix(tools, name) {
152422
+ const exactMatch = findToolByName(tools, name);
152423
+ if (exactMatch)
152424
+ return exactMatch;
152425
+ const resolvedName = resolveToolNameByUniquePrefix(tools.map((tool) => tool.name), name);
152426
+ return resolvedName ? tools.find((tool) => tool.name === resolvedName) : undefined;
152427
+ }
152413
152428
  function buildTool(def) {
152414
152429
  return {
152415
152430
  ...TOOL_DEFAULTS,
@@ -191373,7 +191388,7 @@ async function fetchPortalUrl(accessToken) {
191373
191388
  throw apiError;
191374
191389
  }
191375
191390
  }
191376
- var subscriptionSchema, subscriptionsSchema, portalSchema;
191391
+ var subscriptionSourceSchema, subscriptionSchema, subscriptionsSchema, portalSchema;
191377
191392
  var init_verbooSubscriptions = __esm(() => {
191378
191393
  init_axios2();
191379
191394
  init_zod();
@@ -191381,6 +191396,17 @@ var init_verbooSubscriptions = __esm(() => {
191381
191396
  init_debug();
191382
191397
  init_log3();
191383
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
+ ]);
191384
191410
  subscriptionSchema = exports_external2.object({
191385
191411
  id: exports_external2.string().uuid(),
191386
191412
  groupId: exports_external2.string().uuid(),
@@ -191394,7 +191420,7 @@ var init_verbooSubscriptions = __esm(() => {
191394
191420
  status: exports_external2.string(),
191395
191421
  models: exports_external2.array(exports_external2.string()).optional()
191396
191422
  }).passthrough().optional(),
191397
- source: exports_external2.string().optional(),
191423
+ source: subscriptionSourceSchema.optional(),
191398
191424
  status: exports_external2.string().min(1),
191399
191425
  wooviSubscriptionId: exports_external2.string().optional(),
191400
191426
  currentPeriodStart: exports_external2.string().datetime({ offset: true }).optional(),
@@ -192802,6 +192828,102 @@ var init_openaiSchemaSanitizer = __esm(() => {
192802
192828
  });
192803
192829
 
192804
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
+ }
192805
192927
  function makeUsage(usage) {
192806
192928
  return buildAnthropicUsageFromRawUsage(usage);
192807
192929
  }
@@ -193164,80 +193286,110 @@ async function performCodexRequest(options2) {
193164
193286
  return response;
193165
193287
  }
193166
193288
  async function* readSseEvents(response, signal) {
193167
- const reader = response.body?.getReader();
193168
- if (!reader)
193289
+ const responseBody = response.body;
193290
+ if (!responseBody)
193169
193291
  return;
193292
+ const reader = responseBody.getReader();
193170
193293
  const decoder = new TextDecoder;
193171
193294
  let buffer = "";
193172
- 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
+ })();
193173
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
+ };
193174
193312
  async function readWithTimeout() {
193175
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
+ };
193176
193337
  const timeoutId = setTimeout(() => {
193177
193338
  const elapsed = Math.round((Date.now() - lastDataTime) / 1000);
193178
- 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);
193179
193342
  }, STREAM_IDLE_TIMEOUT_MS);
193180
- let abortCleanup;
193181
193343
  if (signal) {
193182
193344
  abortCleanup = () => {
193183
- 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);
193184
193350
  };
193351
+ if (signal.aborted) {
193352
+ abortCleanup();
193353
+ return;
193354
+ }
193185
193355
  signal.addEventListener("abort", abortCleanup, { once: true });
193186
193356
  }
193187
- reader.read().then((result) => {
193188
- clearTimeout(timeoutId);
193189
- if (signal && abortCleanup)
193190
- signal.removeEventListener("abort", abortCleanup);
193191
- if (result.value)
193192
- lastDataTime = Date.now();
193193
- resolve19(result);
193194
- }, (err2) => {
193195
- clearTimeout(timeoutId);
193196
- if (signal && abortCleanup)
193197
- signal.removeEventListener("abort", abortCleanup);
193198
- reject(err2);
193199
- });
193357
+ reader.read().then(resolveOnce, rejectOnce);
193200
193358
  });
193201
193359
  }
193202
- while (true) {
193203
- const { done, value } = await readWithTimeout();
193204
- if (done)
193205
- break;
193206
- buffer += decoder.decode(value, { stream: true });
193207
- const chunks = buffer.split(`
193208
-
193209
- `);
193210
- buffer = chunks.pop() ?? "";
193211
- for (const chunk of chunks) {
193212
- const lines = chunk.split(`
193213
- `).map((line) => line.trim()).filter(Boolean);
193214
- if (lines.length === 0)
193215
- continue;
193216
- const eventLine = lines.find((line) => line.startsWith("event: "));
193217
- const dataLines = lines.filter((line) => line.startsWith("data: "));
193218
- if (!eventLine || dataLines.length === 0)
193219
- continue;
193220
- const event = eventLine.slice(7).trim();
193221
- const rawData = dataLines.map((line) => line.slice(6)).join(`
193222
- `);
193223
- if (rawData === "[DONE]")
193224
- continue;
193225
- let data;
193226
- try {
193227
- const parsed = JSON.parse(rawData);
193228
- if (!parsed || typeof parsed !== "object")
193229
- continue;
193230
- data = parsed;
193231
- } catch {
193232
- 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;
193233
193370
  }
193234
- yield { event, data };
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;
193378
+ }
193379
+ }
193380
+ } finally {
193381
+ if (!reachedEOF) {
193382
+ cancelReader("Codex SSE consumer completed before transport EOF");
193235
193383
  }
193384
+ await pendingReaderCancellation;
193385
+ try {
193386
+ reader.releaseLock();
193387
+ } catch {}
193236
193388
  }
193237
193389
  }
193238
193390
  function determineStopReason(response, sawToolUse) {
193239
193391
  const output = Array.isArray(response?.output) ? response.output : [];
193240
- if (sawToolUse || output.some((item) => item?.type === "function_call")) {
193392
+ if (response?.status !== "incomplete" && (sawToolUse || output.some((item) => item?.type === "function_call"))) {
193241
193393
  return "tool_use";
193242
193394
  }
193243
193395
  const incompleteReason = response?.incomplete_details?.reason;
@@ -193253,8 +193405,12 @@ async function collectCodexCompletedResponse(response, signal) {
193253
193405
  const msg = event.data?.response?.error?.message ?? event.data?.error?.message ?? "Codex response failed";
193254
193406
  throw APIError.generate(500, undefined, msg, new Headers);
193255
193407
  }
193256
- if (event.event === "response.completed" || event.event === "response.incomplete") {
193257
- 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");
193258
193414
  break;
193259
193415
  }
193260
193416
  }
@@ -193263,22 +193419,30 @@ async function collectCodexCompletedResponse(response, signal) {
193263
193419
  }
193264
193420
  return completedResponse;
193265
193421
  }
193266
- async function* codexStreamToAnthropic(response, model2, signal) {
193422
+ async function* codexStreamToAnthropic(response, model2, signal, advertisedToolNames = []) {
193267
193423
  const messageId = makeMessageId();
193268
193424
  const toolBlocksByItemId = new Map;
193269
193425
  let activeTextBlockIndex = null;
193426
+ let emittedVisibleText = "";
193270
193427
  const thinkFilter = createThinkTagFilter();
193271
193428
  let nextContentBlockIndex = 0;
193272
193429
  let sawToolUse = false;
193273
193430
  let finalResponse;
193431
+ let terminalEvent;
193432
+ let terminalErrorMessage;
193433
+ let totalBufferedToolArgumentChars = 0;
193434
+ const toolArgumentCharsLimit = maxBufferedToolArgumentChars();
193274
193435
  const closeActiveTextBlock = async function* () {
193275
193436
  if (activeTextBlockIndex === null)
193276
193437
  return;
193438
+ const textBlockIndex = activeTextBlockIndex;
193439
+ activeTextBlockIndex = null;
193277
193440
  const tail = thinkFilter.flush();
193278
193441
  if (tail) {
193442
+ emittedVisibleText += tail;
193279
193443
  yield {
193280
193444
  type: "content_block_delta",
193281
- index: activeTextBlockIndex,
193445
+ index: textBlockIndex,
193282
193446
  delta: {
193283
193447
  type: "text_delta",
193284
193448
  text: tail
@@ -193287,9 +193451,8 @@ async function* codexStreamToAnthropic(response, model2, signal) {
193287
193451
  }
193288
193452
  yield {
193289
193453
  type: "content_block_stop",
193290
- index: activeTextBlockIndex
193454
+ index: textBlockIndex
193291
193455
  };
193292
- activeTextBlockIndex = null;
193293
193456
  };
193294
193457
  const startTextBlockIfNeeded = async function* () {
193295
193458
  if (activeTextBlockIndex !== null)
@@ -193301,6 +193464,129 @@ async function* codexStreamToAnthropic(response, model2, signal) {
193301
193464
  content_block: { type: "text", text: "" }
193302
193465
  };
193303
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
+ };
193482
+ const findToolBlockEntry = (item) => {
193483
+ for (const candidate of [item.id, item.call_id]) {
193484
+ if (candidate == null)
193485
+ continue;
193486
+ const itemId = String(candidate);
193487
+ const toolBlock = toolBlocksByItemId.get(itemId);
193488
+ if (toolBlock)
193489
+ return [itemId, toolBlock];
193490
+ for (const entry of toolBlocksByItemId) {
193491
+ if (entry[0] === itemId || entry[1].toolUseId === itemId)
193492
+ return entry;
193493
+ }
193494
+ }
193495
+ return;
193496
+ };
193497
+ const canonicalizeFinalToolName = (toolBlock) => {
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
+ }
193502
+ if (resolvedName)
193503
+ toolBlock.name = resolvedName;
193504
+ };
193505
+ const applyFinalToolItem = (toolBlock, item) => {
193506
+ if (typeof item.name === "string" && item.name) {
193507
+ toolBlock.name = item.name;
193508
+ }
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);
193514
+ }
193515
+ canonicalizeFinalToolName(toolBlock);
193516
+ };
193517
+ const emitPendingToolArguments = async function* (toolBlock) {
193518
+ if (!toolBlock.hasStarted || toolBlock.emittedArgumentsLength >= toolBlock.argumentsBuffer.length) {
193519
+ return;
193520
+ }
193521
+ yield {
193522
+ type: "content_block_delta",
193523
+ index: toolBlock.index,
193524
+ delta: {
193525
+ type: "input_json_delta",
193526
+ partial_json: toolBlock.argumentsBuffer.slice(toolBlock.emittedArgumentsLength)
193527
+ }
193528
+ };
193529
+ toolBlock.emittedArgumentsLength = toolBlock.argumentsBuffer.length;
193530
+ };
193531
+ const startToolBlock = async function* (toolBlock) {
193532
+ if (toolBlock.hasStarted)
193533
+ return;
193534
+ canonicalizeFinalToolName(toolBlock);
193535
+ toolBlock.hasStarted = true;
193536
+ toolBlock.startedName = toolBlock.name || "tool";
193537
+ yield {
193538
+ type: "content_block_start",
193539
+ index: toolBlock.index,
193540
+ content_block: {
193541
+ type: "tool_use",
193542
+ id: toolBlock.toolUseId,
193543
+ name: toolBlock.startedName,
193544
+ input: {}
193545
+ }
193546
+ };
193547
+ yield* emitPendingToolArguments(toolBlock);
193548
+ };
193549
+ const flushToolBlocks = async function* () {
193550
+ const orderedBlocks = [...toolBlocksByItemId.values()].sort((a2, b) => a2.index - b.index);
193551
+ for (const toolBlock of orderedBlocks) {
193552
+ if (toolBlock.hasStopped)
193553
+ continue;
193554
+ if (!toolBlock.hasStarted) {
193555
+ if (!toolBlock.isDone)
193556
+ break;
193557
+ yield* startToolBlock(toolBlock);
193558
+ if (!toolBlock.hasStarted)
193559
+ break;
193560
+ }
193561
+ yield* emitPendingToolArguments(toolBlock);
193562
+ if (toolBlock.isDone) {
193563
+ toolBlock.hasStopped = true;
193564
+ yield {
193565
+ type: "content_block_stop",
193566
+ index: toolBlock.index
193567
+ };
193568
+ }
193569
+ }
193570
+ };
193571
+ const removeStoppedToolBlocks = () => {
193572
+ for (const [itemId, toolBlock] of toolBlocksByItemId) {
193573
+ if (toolBlock.hasStopped) {
193574
+ totalBufferedToolArgumentChars -= toolBlock.argumentsBuffer.length;
193575
+ toolBlocksByItemId.delete(itemId);
193576
+ }
193577
+ }
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
+ };
193304
193590
  yield {
193305
193591
  type: "message_start",
193306
193592
  message: {
@@ -193314,124 +193600,244 @@ async function* codexStreamToAnthropic(response, model2, signal) {
193314
193600
  usage: makeUsage()
193315
193601
  }
193316
193602
  };
193317
- for await (const event of readSseEvents(response, signal)) {
193318
- const payload = event.data;
193319
- if (event.event === "response.output_item.added") {
193320
- const item = payload.item;
193321
- if (item?.type === "function_call") {
193322
- yield* closeActiveTextBlock();
193323
- const blockIndex = nextContentBlockIndex++;
193324
- const toolUseId = item.call_id ?? item.id ?? `call_${blockIndex}`;
193325
- toolBlocksByItemId.set(String(item.id ?? toolUseId), {
193326
- index: blockIndex,
193327
- toolUseId
193328
- });
193329
- sawToolUse = true;
193330
- yield {
193331
- type: "content_block_start",
193332
- index: blockIndex,
193333
- content_block: {
193334
- type: "tool_use",
193335
- id: toolUseId,
193336
- name: item.name ?? "tool",
193337
- input: {}
193338
- }
193339
- };
193340
- if (item.arguments) {
193341
- yield {
193342
- type: "content_block_delta",
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 = {
193343
193613
  index: blockIndex,
193344
- delta: {
193345
- type: "input_json_delta",
193346
- partial_json: item.arguments
193347
- }
193614
+ toolUseId,
193615
+ name: typeof item.name === "string" ? item.name : "",
193616
+ argumentsBuffer: "",
193617
+ emittedArgumentsLength: 0,
193618
+ hasStarted: false,
193619
+ isDone: false,
193620
+ hasStopped: false
193348
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);
193349
193630
  }
193631
+ continue;
193350
193632
  }
193351
- continue;
193352
- }
193353
- if (event.event === "response.content_part.added") {
193354
- if (payload.part?.type === "output_text") {
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
+ }
193355
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;
193356
193662
  }
193357
- continue;
193358
- }
193359
- if (event.event === "response.output_text.delta") {
193360
- yield* startTextBlockIfNeeded();
193361
- if (activeTextBlockIndex !== null) {
193362
- const visible = thinkFilter.feed(payload.delta ?? "");
193363
- if (visible) {
193364
- yield {
193365
- type: "content_block_delta",
193366
- index: activeTextBlockIndex,
193367
- delta: {
193368
- type: "text_delta",
193369
- text: visible
193370
- }
193371
- };
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
+ }
193372
193669
  }
193670
+ continue;
193373
193671
  }
193374
- continue;
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);
193375
193706
  }
193376
- if (event.event === "response.function_call_arguments.delta") {
193377
- const toolBlock = toolBlocksByItemId.get(String(payload.item_id ?? ""));
193378
- if (toolBlock) {
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;
193379
193728
  yield {
193380
193729
  type: "content_block_delta",
193381
- index: toolBlock.index,
193382
- delta: {
193383
- type: "input_json_delta",
193384
- partial_json: payload.delta ?? ""
193385
- }
193730
+ index: activeTextBlockIndex,
193731
+ delta: { type: "text_delta", text: missingSuffix }
193386
193732
  };
193387
193733
  }
193388
- continue;
193734
+ yield* closeActiveTextBlock();
193389
193735
  }
193390
- if (event.event === "response.output_item.done") {
193391
- const item = payload.item;
193392
- if (item?.type === "function_call") {
193393
- const toolBlock = toolBlocksByItemId.get(String(item.id ?? ""));
193394
- if (toolBlock) {
193395
- yield {
193396
- type: "content_block_stop",
193397
- index: toolBlock.index
193398
- };
193399
- toolBlocksByItemId.delete(String(item.id));
193400
- }
193401
- } else if (item?.type === "message") {
193402
- yield* closeActiveTextBlock();
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);
193403
193762
  }
193404
- continue;
193763
+ let toolBlockEntry = findToolBlockEntry(item);
193764
+ if (!toolBlockEntry) {
193765
+ const blockIndex = nextContentBlockIndex++;
193766
+ const { toolUseId } = parseCompletedCodexTool(item);
193767
+ const toolBlock = {
193768
+ index: blockIndex,
193769
+ toolUseId,
193770
+ name: "",
193771
+ argumentsBuffer: "",
193772
+ emittedArgumentsLength: 0,
193773
+ hasStarted: false,
193774
+ isDone: false,
193775
+ hasStopped: false
193776
+ };
193777
+ const itemKey = String(item.id ?? item.call_id ?? toolUseId);
193778
+ toolBlocksByItemId.set(itemKey, toolBlock);
193779
+ toolBlockEntry = [itemKey, toolBlock];
193780
+ }
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);
193784
+ }
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);
193789
+ }
193790
+ completedToolCallIds.add(toolBlockEntry[1].toolUseId);
193791
+ toolBlockEntry[1].isDone = true;
193792
+ completedToolBlocks.add(toolBlockEntry[1]);
193405
193793
  }
193406
- if (event.event === "response.completed" || event.event === "response.incomplete") {
193407
- finalResponse = payload.response;
193408
- break;
193794
+ let authoritativeToolOffset = 0;
193795
+ for (const toolBlock of completedToolBlocks) {
193796
+ toolBlock.index = firstFinalToolIndex + authoritativeToolOffset++;
193409
193797
  }
193410
- if (event.event === "response.failed") {
193411
- const msg = payload?.response?.error?.message ?? payload?.error?.message ?? "Codex response failed";
193412
- throw APIError.generate(500, undefined, msg, new Headers);
193798
+ if (completedToolBlocks.size > 0) {
193799
+ nextContentBlockIndex = firstFinalToolIndex + completedToolBlocks.size;
193413
193800
  }
193414
- }
193415
- yield* closeActiveTextBlock();
193416
- for (const toolBlock of toolBlocksByItemId.values()) {
193801
+ for (const [itemId, toolBlock] of toolBlocksByItemId) {
193802
+ if (!completedToolBlocks.has(toolBlock)) {
193803
+ totalBufferedToolArgumentChars -= toolBlock.argumentsBuffer.length;
193804
+ toolBlocksByItemId.delete(itemId);
193805
+ }
193806
+ }
193807
+ sawToolUse = completedToolBlocks.size > 0;
193808
+ yield* flushToolBlocks();
193809
+ removeStoppedToolBlocks();
193417
193810
  yield {
193418
- type: "content_block_stop",
193419
- index: toolBlock.index
193811
+ type: "message_delta",
193812
+ delta: {
193813
+ stop_reason: determineStopReason(finalResponse, sawToolUse),
193814
+ stop_sequence: null
193815
+ },
193816
+ usage: makeUsage(finalResponse?.usage)
193420
193817
  };
193818
+ yield { type: "message_stop" };
193819
+ } catch (error41) {
193820
+ yield* closeOpenBlocksForFailure();
193821
+ throw error41;
193421
193822
  }
193422
- yield {
193423
- type: "message_delta",
193424
- delta: {
193425
- stop_reason: determineStopReason(finalResponse, sawToolUse),
193426
- stop_sequence: null
193427
- },
193428
- usage: makeUsage(finalResponse?.usage)
193429
- };
193430
- yield { type: "message_stop" };
193431
193823
  }
193432
- function convertCodexResponseToAnthropicMessage(data, model2) {
193824
+ function convertCodexResponseToAnthropicMessage(data, model2, advertisedToolNames = []) {
193433
193825
  const content = [];
193434
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();
193435
193841
  for (const item of output) {
193436
193842
  if (item?.type === "message" && Array.isArray(item.content)) {
193437
193843
  for (const part of item.content) {
@@ -193444,17 +193850,24 @@ function convertCodexResponseToAnthropicMessage(data, model2) {
193444
193850
  }
193445
193851
  continue;
193446
193852
  }
193447
- if (item?.type === "function_call") {
193448
- let input;
193449
- try {
193450
- input = JSON.parse(item.arguments ?? "{}");
193451
- } catch {
193452
- input = { raw: item.arguments ?? "" };
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);
193863
+ const toolName = resolveToolNameByUniquePrefix(advertisedToolNames, item.name ?? "") ?? item.name ?? "tool";
193864
+ if (advertisedToolNames.length > 0 && !resolveToolNameByUniquePrefix(advertisedToolNames, item.name ?? "")) {
193865
+ throw new Error("Codex completed response selected an unadvertised tool; no tool was committed");
193453
193866
  }
193454
193867
  content.push({
193455
193868
  type: "tool_use",
193456
- id: item.call_id ?? item.id ?? makeMessageId(),
193457
- name: item.name ?? "tool",
193869
+ id: toolUseId,
193870
+ name: toolName,
193458
193871
  input
193459
193872
  });
193460
193873
  }
@@ -193470,13 +193883,16 @@ function convertCodexResponseToAnthropicMessage(data, model2) {
193470
193883
  usage: makeUsage(data.usage)
193471
193884
  };
193472
193885
  }
193886
+ var DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS;
193473
193887
  var init_codexShim = __esm(() => {
193474
193888
  init_sdk();
193889
+ init_Tool();
193475
193890
  init_cacheMetrics();
193476
193891
  init_compressToolHistory();
193477
193892
  init_fetchWithProxyRetry();
193478
193893
  init_openaiSchemaSanitizer();
193479
193894
  init_thinkTagSanitizer();
193895
+ DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS = 64 * 1024 * 1024;
193480
193896
  });
193481
193897
 
193482
193898
  // src/services/api/visionDelegate.ts
@@ -194718,22 +195134,87 @@ function convertChunkUsage(usage) {
194718
195134
  return;
194719
195135
  return buildAnthropicUsageFromRawUsage(usage);
194720
195136
  }
194721
- 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;
194722
195149
  try {
194723
- const parsed = JSON.parse(raw);
194724
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? raw : null;
195150
+ JSON.parse(raw);
195151
+ return false;
194725
195152
  } catch {
194726
- for (const combo of JSON_REPAIR_SUFFIXES) {
194727
- try {
194728
- const repaired = raw + combo;
194729
- const parsed = JSON.parse(repaired);
194730
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
194731
- return repaired;
194732
- }
194733
- } 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
+ });
194734
195179
  }
194735
- return null;
194736
195180
  }
195181
+ return merged;
195182
+ }
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 };
195215
+ }
195216
+ function getAdvertisedToolNames(params) {
195217
+ return (params.tools ?? []).flatMap((tool) => typeof tool.name === "string" && tool.name && tool.name !== "ToolSearchTool" ? [tool.name] : []);
194737
195218
  }
194738
195219
  function setOpenAIShimRouterStatusHandler(fn) {
194739
195220
  if (routerStatusHandler && activeWarmingHints.size > 0) {
@@ -194800,7 +195281,7 @@ function createWarmingHintController(signal) {
194800
195281
  }
194801
195282
  return { schedule, showNow, resolve: resolve19 };
194802
195283
  }
194803
- async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
195284
+ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint, advertisedToolNames = []) {
194804
195285
  const messageId = makeMessageId2();
194805
195286
  let contentBlockIndex = 0;
194806
195287
  const activeToolCalls = new Map;
@@ -194811,7 +195292,11 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194811
195292
  let lastStopReason = null;
194812
195293
  let hasEmittedFinalUsage = false;
194813
195294
  let hasProcessedFinishReason = false;
195295
+ let sawDoneMarker = false;
194814
195296
  const streamState = createStreamState();
195297
+ let nextSyntheticProtocolIndex = -2;
195298
+ let totalBufferedToolArgumentChars = 0;
195299
+ const toolArgumentCharsLimit = maxBufferedToolArgumentChars2();
194815
195300
  yield {
194816
195301
  type: "message_start",
194817
195302
  message: {
@@ -194830,9 +195315,11 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194830
195315
  }
194831
195316
  }
194832
195317
  };
194833
- const reader = response.body?.getReader();
194834
- if (!reader)
194835
- 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();
194836
195323
  const decoder = new TextDecoder;
194837
195324
  let buffer = "";
194838
195325
  const STREAM_IDLE_TIMEOUT_MS = (() => {
@@ -194842,32 +195329,61 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194842
195329
  })();
194843
195330
  let lastDataTime = Date.now();
194844
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
+ };
194845
195340
  async function readWithTimeout() {
194846
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
+ };
194847
195366
  const timeoutId = setTimeout(() => {
194848
195367
  const elapsed = Math.round((Date.now() - lastDataTime) / 1000);
194849
- 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);
194850
195371
  }, STREAM_IDLE_TIMEOUT_MS);
194851
- let abortCleanup;
194852
195372
  if (signal) {
194853
195373
  abortCleanup = () => {
194854
- 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);
194855
195379
  };
195380
+ if (signal.aborted) {
195381
+ abortCleanup();
195382
+ return;
195383
+ }
194856
195384
  signal.addEventListener("abort", abortCleanup, { once: true });
194857
195385
  }
194858
- reader.read().then((result) => {
194859
- clearTimeout(timeoutId);
194860
- if (signal && abortCleanup)
194861
- signal.removeEventListener("abort", abortCleanup);
194862
- if (result.value)
194863
- lastDataTime = Date.now();
194864
- resolve19(result);
194865
- }, (err2) => {
194866
- clearTimeout(timeoutId);
194867
- if (signal && abortCleanup)
194868
- signal.removeEventListener("abort", abortCleanup);
194869
- reject(err2);
194870
- });
195386
+ reader.read().then(resolveOnce, rejectOnce);
194871
195387
  });
194872
195388
  }
194873
195389
  const closeActiveContentBlock = async function* () {
@@ -194888,6 +195404,149 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194888
195404
  contentBlockIndex++;
194889
195405
  hasEmittedContentStart = false;
194890
195406
  };
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;
195429
+ };
195430
+ const startToolCall = async function* (toolCall) {
195431
+ if (toolCall.hasStarted || toolCall.index === null || !toolCall.id || !toolCall.name)
195432
+ return;
195433
+ const blockIndex = toolCall.index;
195434
+ toolCall.normalizeAtStop = hasToolFieldMapping(toolCall.name);
195435
+ toolCall.hasStarted = true;
195436
+ yield {
195437
+ type: "content_block_start",
195438
+ index: blockIndex,
195439
+ content_block: {
195440
+ type: "tool_use",
195441
+ id: toolCall.id,
195442
+ name: toolCall.name,
195443
+ input: {},
195444
+ ...toolCall.extra_content ? { extra_content: toolCall.extra_content } : {},
195445
+ ...toolCall.extra_content?.google?.thought_signature ? {
195446
+ signature: (toolCall.extra_content?.google).thought_signature
195447
+ } : {}
195448
+ }
195449
+ };
195450
+ if (!toolCall.normalizeAtStop && toolCall.jsonBuffer) {
195451
+ yield {
195452
+ type: "content_block_delta",
195453
+ index: blockIndex,
195454
+ delta: {
195455
+ type: "input_json_delta",
195456
+ partial_json: toolCall.jsonBuffer
195457
+ }
195458
+ };
195459
+ toolCall.emittedJsonLength = toolCall.jsonBuffer.length;
195460
+ }
195461
+ };
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
+ }
195468
+ if (!toolCall.hasStarted) {
195469
+ yield* startToolCall(toolCall);
195470
+ if (!toolCall.hasStarted) {
195471
+ continue;
195472
+ }
195473
+ }
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) {
195484
+ yield {
195485
+ type: "content_block_delta",
195486
+ index: toolCall.index,
195487
+ delta: {
195488
+ type: "input_json_delta",
195489
+ partial_json: toolCall.jsonBuffer.slice(toolCall.emittedJsonLength)
195490
+ }
195491
+ };
195492
+ toolCall.emittedJsonLength = toolCall.jsonBuffer.length;
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;
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.");
195549
+ };
194891
195550
  try {
194892
195551
  while (true) {
194893
195552
  const { done, value } = await readWithTimeout();
@@ -194901,16 +195560,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194901
195560
  duration_ms: Date.now() - streamStartedAt,
194902
195561
  had_content: hasEmittedContentStart || hasEmittedThinkingStart
194903
195562
  }), { level: "error" });
194904
- if (hasEmittedContentStart) {
194905
- yield* closeActiveContentBlock();
194906
- }
194907
- if (hasEmittedThinkingStart && !hasClosedThinking) {
194908
- yield { type: "content_block_stop", index: contentBlockIndex };
194909
- }
194910
- for (const [, toolCall] of activeToolCalls) {
194911
- yield { type: "content_block_stop", index: toolCall.index };
194912
- }
194913
- activeToolCalls.clear();
195563
+ discardActiveToolCalls("premature_eof");
194914
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.`);
194915
195565
  }
194916
195566
  break;
@@ -194921,15 +195571,23 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194921
195571
  buffer = lines.pop() ?? "";
194922
195572
  for (const line of lines) {
194923
195573
  const trimmed2 = line.trim();
194924
- if (!trimmed2 || trimmed2 === "data: [DONE]")
195574
+ if (!trimmed2)
194925
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
+ }
194926
195584
  if (!trimmed2.startsWith("data: "))
194927
195585
  continue;
194928
195586
  let chunk;
194929
195587
  try {
194930
195588
  chunk = JSON.parse(trimmed2.slice(6));
194931
195589
  } catch {
194932
- continue;
195590
+ throw new Error("Upstream emitted invalid SSE JSON; the response was not committed.");
194933
195591
  }
194934
195592
  const routerStatus = chunk.router_status;
194935
195593
  if (typeof routerStatus === "string") {
@@ -194941,16 +195599,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194941
195599
  const inStreamError = chunk.error;
194942
195600
  if (inStreamError && typeof inStreamError === "object") {
194943
195601
  const message = typeof inStreamError.message === "string" ? inStreamError.message : "Provider returned an in-stream error";
194944
- if (hasEmittedContentStart) {
194945
- yield* closeActiveContentBlock();
194946
- }
194947
- if (hasEmittedThinkingStart && !hasClosedThinking) {
194948
- yield { type: "content_block_stop", index: contentBlockIndex };
194949
- }
194950
- for (const [, toolCall] of activeToolCalls) {
194951
- yield { type: "content_block_stop", index: toolCall.index };
194952
- }
194953
- activeToolCalls.clear();
195602
+ discardActiveToolCalls("in_stream_error");
194954
195603
  const errorPayload = {
194955
195604
  error: {
194956
195605
  message,
@@ -194962,8 +195611,33 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194962
195611
  }
194963
195612
  const chunkUsage = convertChunkUsage(chunk.usage);
194964
195613
  for (const choice of chunk.choices ?? []) {
194965
- 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
+ }
194966
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
+ }
194967
195641
  if (!hasEmittedThinkingStart) {
194968
195642
  yield {
194969
195643
  type: "content_block_start",
@@ -194974,7 +195648,7 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
194974
195648
  }
194975
195649
  yield {
194976
195650
  type: "content_block_delta",
194977
- index: hasClosedThinking ? contentBlockIndex - 1 : contentBlockIndex,
195651
+ index: contentBlockIndex,
194978
195652
  delta: {
194979
195653
  type: "thinking_delta",
194980
195654
  thinking: delta.reasoning_content
@@ -195005,9 +195679,32 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
195005
195679
  }
195006
195680
  processStreamChunk(streamState, delta.content);
195007
195681
  }
195008
- if (delta.tool_calls) {
195009
- for (const tc of delta.tool_calls) {
195010
- if (tc.id && tc.function?.name) {
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);
195707
+ if (!active) {
195011
195708
  if (hasEmittedThinkingStart && !hasClosedThinking) {
195012
195709
  yield {
195013
195710
  type: "content_block_stop",
@@ -195019,81 +195716,53 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
195019
195716
  if (hasEmittedContentStart) {
195020
195717
  yield* closeActiveContentBlock();
195021
195718
  }
195022
- const toolBlockIndex = contentBlockIndex;
195023
- const initialArguments = tc.function.arguments ?? "";
195024
- const normalizeAtStop = hasToolFieldMapping(tc.function.name);
195025
- processStreamChunk(streamState, tc.function.arguments ?? "");
195026
- const topLevelSig = tc.thought_signature;
195027
- const initEC = tc.extra_content ? { ...tc.extra_content } : topLevelSig ? { google: { thought_signature: topLevelSig } } : undefined;
195028
- activeToolCalls.set(tc.index, {
195029
- id: tc.id,
195030
- name: tc.function.name,
195031
- index: toolBlockIndex,
195032
- jsonBuffer: initialArguments,
195033
- normalizeAtStop,
195034
- extra_content: initEC
195035
- });
195036
- yield {
195037
- type: "content_block_start",
195038
- index: toolBlockIndex,
195039
- content_block: {
195040
- type: "tool_use",
195041
- id: tc.id,
195042
- name: tc.function.name,
195043
- input: {},
195044
- ...initEC ? { extra_content: initEC } : {},
195045
- ...initEC?.google?.thought_signature ? { signature: initEC.google.thought_signature } : {}
195046
- }
195719
+ active = {
195720
+ id: "",
195721
+ name: "",
195722
+ idFragments: [],
195723
+ nameFragments: [],
195724
+ index: null,
195725
+ jsonBuffer: "",
195726
+ ambiguousArgumentFraming: false,
195727
+ emittedJsonLength: 0,
195728
+ normalizeAtStop: false,
195729
+ hasStarted: false,
195730
+ hasStopped: false
195047
195731
  };
195048
- contentBlockIndex++;
195049
- if (tc.function.arguments && !normalizeAtStop) {
195050
- yield {
195051
- type: "content_block_delta",
195052
- index: toolBlockIndex,
195053
- delta: {
195054
- type: "input_json_delta",
195055
- partial_json: tc.function.arguments
195056
- }
195057
- };
195058
- }
195059
- } else if (tc.function?.arguments) {
195060
- const active = activeToolCalls.get(tc.index);
195061
- if (active) {
195062
- if (tc.function.arguments) {
195063
- active.jsonBuffer += tc.function.arguments;
195064
- }
195065
- const contSig = tc.thought_signature;
195066
- const contEC = tc.extra_content ? { ...tc.extra_content } : contSig ? { google: { thought_signature: contSig } } : undefined;
195067
- if (contEC) {
195068
- active.extra_content = {
195069
- ...active.extra_content ?? {},
195070
- ...contEC
195071
- };
195072
- }
195073
- if (active.normalizeAtStop) {
195074
- continue;
195075
- }
195076
- yield {
195077
- type: "content_block_delta",
195078
- index: active.index,
195079
- delta: {
195080
- type: "input_json_delta",
195081
- partial_json: tc.function.arguments
195082
- }
195083
- };
195732
+ activeToolCalls.set(protocolIndex, active);
195733
+ }
195734
+ if (tc.id) {
195735
+ active.idFragments.push(tc.id);
195736
+ }
195737
+ const nameFragment = tc.function?.name;
195738
+ if (nameFragment) {
195739
+ active.nameFragments.push(nameFragment);
195740
+ }
195741
+ const argumentFragment = tc.function?.arguments;
195742
+ if (typeof argumentFragment === "string") {
195743
+ if (argumentFragment.length > 0 && active.jsonBuffer.length > 0 && argumentFragment.startsWith(active.jsonBuffer)) {
195744
+ active.ambiguousArgumentFraming = true;
195084
195745
  }
195085
- } else {
195086
- const active = activeToolCalls.get(tc.index);
195087
- if (active) {
195088
- const lateSig = tc.thought_signature;
195089
- const lateEC = tc.extra_content ? { ...tc.extra_content } : lateSig ? { google: { thought_signature: lateSig } } : undefined;
195090
- if (lateEC) {
195091
- active.extra_content = {
195092
- ...active.extra_content ?? {},
195093
- ...lateEC
195094
- };
195095
- }
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.");
195096
195749
  }
195750
+ active.jsonBuffer += argumentFragment;
195751
+ totalBufferedToolArgumentChars += argumentFragment.length;
195752
+ processStreamChunk(streamState, argumentFragment);
195753
+ }
195754
+ const thoughtSignature = tc.thought_signature;
195755
+ let extraContent;
195756
+ if (typeof thoughtSignature === "string" && thoughtSignature) {
195757
+ extraContent = {
195758
+ google: { thought_signature: thoughtSignature }
195759
+ };
195760
+ }
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);
195097
195766
  }
195098
195767
  }
195099
195768
  }
@@ -195107,74 +195776,32 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
195107
195776
  if (hasEmittedContentStart) {
195108
195777
  yield* closeActiveContentBlock();
195109
195778
  }
195110
- for (const [, tc] of activeToolCalls) {
195111
- if (tc.extra_content) {
195112
- yield {
195113
- type: "content_block_start",
195114
- index: tc.index,
195115
- content_block: {
195116
- type: "tool_use",
195117
- id: tc.id,
195118
- name: tc.name,
195119
- input: {},
195120
- extra_content: tc.extra_content,
195121
- ...tc.extra_content.google?.thought_signature ? {
195122
- signature: tc.extra_content.google.thought_signature
195123
- } : {}
195124
- }
195125
- };
195126
- }
195127
- if (tc.normalizeAtStop) {
195128
- let partialJson;
195129
- if (choice.finish_reason === "length") {
195130
- partialJson = tc.jsonBuffer;
195131
- } else {
195132
- const repairedStructuredJson = repairPossiblyTruncatedObjectJson(tc.jsonBuffer);
195133
- if (repairedStructuredJson) {
195134
- partialJson = repairedStructuredJson;
195135
- } else {
195136
- partialJson = JSON.stringify(normalizeToolArguments(tc.name, tc.jsonBuffer));
195137
- }
195138
- }
195139
- yield {
195140
- type: "content_block_delta",
195141
- index: tc.index,
195142
- delta: {
195143
- type: "input_json_delta",
195144
- partial_json: partialJson
195145
- }
195146
- };
195147
- yield { type: "content_block_stop", index: tc.index };
195148
- continue;
195149
- }
195150
- let suffixToAdd = "";
195151
- if (tc.jsonBuffer) {
195152
- try {
195153
- JSON.parse(tc.jsonBuffer);
195154
- } catch {
195155
- const str = tc.jsonBuffer.trimEnd();
195156
- for (const combo of JSON_REPAIR_SUFFIXES) {
195157
- try {
195158
- JSON.parse(str + combo);
195159
- suffixToAdd = combo;
195160
- break;
195161
- } 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");
195162
195792
  }
195793
+ finalizedToolCallIds.add(toolCall.id);
195163
195794
  }
195795
+ toolCall.normalizeAtStop = hasToolFieldMapping(toolCall.name);
195164
195796
  }
195165
- if (suffixToAdd) {
195166
- yield {
195167
- type: "content_block_delta",
195168
- index: tc.index,
195169
- delta: {
195170
- type: "input_json_delta",
195171
- partial_json: suffixToAdd
195172
- }
195173
- };
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.`);
195174
195800
  }
195175
- yield { type: "content_block_stop", index: tc.index };
195801
+ yield* flushReadyToolCalls();
195802
+ activeToolCalls.clear();
195176
195803
  }
195177
- 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";
195178
195805
  if (choice.finish_reason === "content_filter" || choice.finish_reason === "safety") {
195179
195806
  if (!hasEmittedContentStart) {
195180
195807
  yield {
@@ -195214,6 +195841,9 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
195214
195841
  }
195215
195842
  };
195216
195843
  }
195844
+ if (hasEmittedContentStart) {
195845
+ yield* closeActiveContentBlock();
195846
+ }
195217
195847
  lastStopReason = stopReason;
195218
195848
  yield {
195219
195849
  type: "message_delta",
@@ -195234,9 +195864,19 @@ async function* openaiStreamToAnthropic(response, model2, signal, warmingHint) {
195234
195864
  hasEmittedFinalUsage = true;
195235
195865
  }
195236
195866
  }
195867
+ if (sawDoneMarker)
195868
+ break;
195237
195869
  }
195870
+ } catch (error41) {
195871
+ yield* closeOpenBlocksForFailure();
195872
+ throw error41;
195238
195873
  } finally {
195239
- 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
+ }
195240
195880
  }
195241
195881
  const stats = getStreamStats(streamState);
195242
195882
  if (stats.totalChunks > 0) {
@@ -195303,6 +195943,7 @@ class OpenAIShimMessages {
195303
195943
  }
195304
195944
  create(params, options2) {
195305
195945
  const self2 = this;
195946
+ const advertisedToolNames = getAdvertisedToolNames(params);
195306
195947
  let httpResponse;
195307
195948
  const promise3 = (async () => {
195308
195949
  const request = resolveProviderRequest({
@@ -195323,12 +195964,12 @@ class OpenAIShimMessages {
195323
195964
  httpResponse = response;
195324
195965
  if (params.stream) {
195325
195966
  const isResponsesStream = response.url?.includes("/responses");
195326
- return new OpenAIShimStream(request.transport === "codex_responses" || request.transport === "responses" || isResponsesStream ? codexStreamToAnthropic(response, request.resolvedModel, options2?.signal) : openaiStreamToAnthropic(response, request.resolvedModel, options2?.signal, warmingHint), warmingHint);
195967
+ return new OpenAIShimStream(request.transport === "codex_responses" || request.transport === "responses" || isResponsesStream ? codexStreamToAnthropic(response, request.resolvedModel, options2?.signal, advertisedToolNames) : openaiStreamToAnthropic(response, request.resolvedModel, options2?.signal, warmingHint, advertisedToolNames), warmingHint);
195327
195968
  }
195328
195969
  warmingHint.resolve();
195329
195970
  if (request.transport === "codex_responses") {
195330
195971
  const data = await collectCodexCompletedResponse(response, options2?.signal);
195331
- return convertCodexResponseToAnthropicMessage(data, request.resolvedModel);
195972
+ return convertCodexResponseToAnthropicMessage(data, request.resolvedModel, advertisedToolNames);
195332
195973
  }
195333
195974
  const isResponsesNonStream = response.url?.includes("/responses");
195334
195975
  if (request.transport === "responses" || isResponsesNonStream || request.transport === "chat_completions" && isGithubModelsMode()) {
@@ -195336,15 +195977,15 @@ class OpenAIShimMessages {
195336
195977
  if (contentType2.includes("application/json")) {
195337
195978
  const parsed = await response.json();
195338
195979
  if (parsed && typeof parsed === "object" && (("output" in parsed) || ("incomplete_details" in parsed))) {
195339
- return convertCodexResponseToAnthropicMessage(parsed, request.resolvedModel);
195980
+ return convertCodexResponseToAnthropicMessage(parsed, request.resolvedModel, advertisedToolNames);
195340
195981
  }
195341
- return self2._convertNonStreamingResponse(parsed, request.resolvedModel);
195982
+ return self2._convertNonStreamingResponse(parsed, request.resolvedModel, advertisedToolNames);
195342
195983
  }
195343
195984
  }
195344
195985
  const contentType = response.headers.get("content-type") ?? "";
195345
195986
  if (contentType.includes("application/json")) {
195346
195987
  const data = await response.json();
195347
- return self2._convertNonStreamingResponse(data, request.resolvedModel);
195988
+ return self2._convertNonStreamingResponse(data, request.resolvedModel, advertisedToolNames);
195348
195989
  }
195349
195990
  const textBody = await response.text().catch(() => "");
195350
195991
  throw APIError.generate(response.status, undefined, `OpenAI API error ${response.status}: unexpected response: ${textBody.slice(0, 500)}`, response.headers);
@@ -195839,8 +196480,12 @@ class OpenAIShimMessages {
195839
196480
  }
195840
196481
  throw APIError.generate(500, undefined, "OpenAI shim: request loop exited unexpectedly", new Headers);
195841
196482
  }
195842
- _convertNonStreamingResponse(data, model2) {
196483
+ _convertNonStreamingResponse(data, model2, advertisedToolNames = []) {
195843
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
+ }
195844
196489
  const content = [];
195845
196490
  const reasoningText = choice?.message?.reasoning_content;
195846
196491
  if (typeof reasoningText === "string" && reasoningText) {
@@ -195868,20 +196513,56 @@ class OpenAIShimMessages {
195868
196513
  });
195869
196514
  }
195870
196515
  }
195871
- if (choice?.message?.tool_calls) {
195872
- for (const tc of choice.message.tool_calls) {
195873
- const input = normalizeToolArguments(tc.function.name, tc.function.arguments);
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) {
196544
+ const toolName = resolveToolNameByUniquePrefix(advertisedToolNames, tc.function.name) ?? tc.function.name;
196545
+ const input = normalizeToolArguments(toolName, tc.function.arguments);
195874
196546
  content.push({
195875
196547
  type: "tool_use",
195876
196548
  id: tc.id,
195877
- name: tc.function.name,
196549
+ name: toolName,
195878
196550
  input,
195879
196551
  ...tc.extra_content ? { extra_content: tc.extra_content } : {},
195880
- ...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
+ } : {}
195881
196555
  });
195882
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" });
195883
196564
  }
195884
- 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";
195885
196566
  if (choice?.finish_reason === "content_filter" || choice?.finish_reason === "safety") {
195886
196567
  content.push({
195887
196568
  type: "text",
@@ -195923,9 +196604,10 @@ function createOpenAIShimClient(options2) {
195923
196604
  messages: beta.messages
195924
196605
  };
195925
196606
  }
195926
- 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;
195927
196608
  var init_openaiShim = __esm(() => {
195928
196609
  init_sdk();
196610
+ init_Tool();
195929
196611
  init_state();
195930
196612
  init_oauth();
195931
196613
  init_codexCredentials();
@@ -195956,18 +196638,7 @@ var init_openaiShim = __esm(() => {
195956
196638
  "Editor-Plugin-Version": "copilot-chat/0.26.7",
195957
196639
  "Copilot-Integration-Id": "vscode-chat"
195958
196640
  };
195959
- JSON_REPAIR_SUFFIXES = [
195960
- "}",
195961
- '"}',
195962
- "]}",
195963
- '"]}',
195964
- "}}",
195965
- '"}}',
195966
- "]}}",
195967
- '"]}}',
195968
- '"]}]}',
195969
- "}]}"
195970
- ];
196641
+ DEFAULT_MAX_BUFFERED_TOOL_ARGUMENT_CHARS2 = 64 * 1024 * 1024;
195971
196642
  activeWarmingHints = new Set;
195972
196643
  OpenAIShimStream = class OpenAIShimStream {
195973
196644
  generator;
@@ -297859,7 +298530,7 @@ async function* runTools(toolUseMessages, assistantMessages, canUseTool, toolUse
297859
298530
  }
297860
298531
  function partitionToolCalls(toolUseMessages, toolUseContext) {
297861
298532
  return toolUseMessages.reduce((acc, toolUse) => {
297862
- const tool = findToolByName(toolUseContext.options.tools, toolUse.name);
298533
+ const tool = findToolByNameOrUniquePrefix(toolUseContext.options.tools, toolUse.name);
297863
298534
  const parsedInput = tool?.inputSchema.safeParse(toolUse.input);
297864
298535
  const isConcurrencySafe = parsedInput?.success ? (() => {
297865
298536
  try {
@@ -298044,7 +298715,7 @@ async function* handleOrphanedPermission(orphanedPermission, tools, mutableMessa
298044
298715
  }
298045
298716
  const toolName = toolUseBlock.name;
298046
298717
  const toolInput = toolUseBlock.input;
298047
- const toolDefinition = findToolByName(tools, toolName);
298718
+ const toolDefinition = findToolByNameOrUniquePrefix(tools, toolName);
298048
298719
  if (!toolDefinition) {
298049
298720
  return;
298050
298721
  }
@@ -300196,7 +300867,7 @@ function getToolSearchOrReadInfo(toolName, toolInput, tools) {
300196
300867
  isAbsorbedSilently: true
300197
300868
  };
300198
300869
  }
300199
- const tool = findToolByName(tools, toolName) ?? findToolByName(getReplPrimitiveTools(), toolName);
300870
+ const tool = findToolByNameOrUniquePrefix(tools, toolName) ?? findToolByNameOrUniquePrefix(getReplPrimitiveTools(), toolName);
300200
300871
  if (!tool?.isSearchOrReadCommand) {
300201
300872
  return {
300202
300873
  isCollapsible: false,
@@ -300775,7 +301446,7 @@ function getProgressUpdate(tracker) {
300775
301446
  }
300776
301447
  function createActivityDescriptionResolver(tools) {
300777
301448
  return (toolName, input) => {
300778
- const tool = findToolByName(tools, toolName);
301449
+ const tool = findToolByNameOrUniquePrefix(tools, toolName);
300779
301450
  return tool?.getActivityDescription?.(input) ?? undefined;
300780
301451
  };
300781
301452
  }
@@ -323064,7 +323735,17 @@ async function resendCardlessTrialCode(accessToken, verificationId) {
323064
323735
  }
323065
323736
  async function isGroupSubscriptionActive(accessToken, groupId, opts = {}) {
323066
323737
  const subscriptions = await fetchSubscriptions(accessToken, opts);
323067
- return subscriptions.some((sub) => sub.groupId === groupId && (sub.status === "active" || sub.status === "trialing"));
323738
+ return hasGroupSubscriptionEntitlement(subscriptions, groupId, opts.requirement ?? "access");
323739
+ }
323740
+ function hasGroupSubscriptionEntitlement(subscriptions, groupId, requirement) {
323741
+ return subscriptions.some((subscription) => {
323742
+ if (subscription.groupId !== groupId)
323743
+ return false;
323744
+ if (requirement === "access") {
323745
+ return subscription.status === "active" || subscription.status === "trialing";
323746
+ }
323747
+ return subscription.status === "active" && (subscription.source === "stripe" || subscription.source === "stripe_trial" || subscription.source === "woovi");
323748
+ });
323068
323749
  }
323069
323750
  var httpUrlSchema, checkoutResultSchema, checkoutInputSchema, whatsappProfileSchema, cardlessTrialInputSchema, verificationRequiredSchema, trialActivatedSchema, cardlessTrialResultSchema;
323070
323751
  var init_verbooCheckout = __esm(() => {
@@ -323077,9 +323758,14 @@ var init_verbooCheckout = __esm(() => {
323077
323758
  init_verbooSubscriptions();
323078
323759
  httpUrlSchema = exports_external2.string().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"));
323079
323760
  checkoutResultSchema = exports_external2.discriminatedUnion("mode", [
323080
- exports_external2.object({ mode: exports_external2.literal("stripe"), url: httpUrlSchema }).passthrough(),
323761
+ exports_external2.object({
323762
+ mode: exports_external2.literal("stripe"),
323763
+ attemptId: exports_external2.string().uuid(),
323764
+ url: httpUrlSchema
323765
+ }).passthrough(),
323081
323766
  exports_external2.object({
323082
323767
  mode: exports_external2.literal("woovi"),
323768
+ attemptId: exports_external2.string().uuid(),
323083
323769
  wooviQrCode: exports_external2.string().min(1),
323084
323770
  wooviSubscriptionId: exports_external2.string().min(1)
323085
323771
  }).passthrough(),
@@ -323320,6 +324006,9 @@ function movePlanFocus(index, direction, count3, columns) {
323320
324006
  function isCurrentLocalTrial(subscription) {
323321
324007
  return subscription?.status === "trialing" && ["trial", "stripe_trial"].includes(subscription.source ?? "");
323322
324008
  }
324009
+ function getStripeTrialConversionUrl(subscriptionId, billingInterval) {
324010
+ return `${VERBOO_FRONT_BASE_URL}/pt/settings/billing?subscription=${encodeURIComponent(subscriptionId)}&action=convert&billingInterval=${encodeURIComponent(billingInterval)}`;
324011
+ }
323323
324012
  function filterCliPurchasablePlans(groups, subscriptions) {
323324
324013
  const subscriptionsByGroup = new Map(subscriptions.filter((subscription) => ["active", "trialing", "past_due"].includes(subscription.status)).map((subscription) => [subscription.groupId, subscription]));
323325
324014
  return groups.filter((group) => {
@@ -323377,10 +324066,10 @@ function paymentProviderLabel(group) {
323377
324066
  }
323378
324067
  }
323379
324068
  function hasCardlessTrial(group) {
323380
- return Boolean(group.trialEligible && group.trialDays && group.trialPaymentMethodRequired === false && group.paymentProvider !== "woovi");
324069
+ return Boolean(group.trialEligible && group.trialDays && group.billingInterval === "month" && group.trialPaymentMethodRequired === false && group.paymentProvider !== "woovi");
323381
324070
  }
323382
324071
  function hasCardTrial(group) {
323383
- return Boolean(group.trialEligible && group.trialDays && group.trialPaymentMethodRequired !== false && group.paymentProvider !== "woovi");
324072
+ return Boolean(group.trialEligible && group.trialDays && group.billingInterval === "month" && group.trialPaymentMethodRequired !== false && group.paymentProvider !== "woovi");
323384
324073
  }
323385
324074
  function getPlanDetailOptions(plan) {
323386
324075
  if (hasCardlessTrial(plan)) {
@@ -323831,12 +324520,15 @@ function PurchaseFlowView({
323831
324520
  const columnCount = getPlanColumnCount(terminalColumns);
323832
324521
  const [step, setStep] = import_react66.useState("splash");
323833
324522
  const [plans, setPlans] = import_react66.useState([]);
324523
+ const [subscriptionsByGroup, setSubscriptionsByGroup] = import_react66.useState(new Map);
323834
324524
  const [selectedPlan, setSelectedPlan] = import_react66.useState(null);
323835
324525
  const [focusIndex, setFocusIndex] = import_react66.useState(0);
323836
324526
  const [inlineMessage, setInlineMessage] = import_react66.useState(null);
323837
324527
  const [flowError, setFlowError] = import_react66.useState(null);
323838
324528
  const [wooviPayment, setWooviPayment] = import_react66.useState(null);
323839
324529
  const [manualCheckoutUrl, setManualCheckoutUrl] = import_react66.useState(null);
324530
+ const [manualEntitlementRequirement, setManualEntitlementRequirement] = import_react66.useState("paid");
324531
+ const [successRequirement, setSuccessRequirement] = import_react66.useState("paid");
323840
324532
  const [whatsappProfile, setWhatsAppProfile] = import_react66.useState(null);
323841
324533
  const [cardlessVerification, setCardlessVerification] = import_react66.useState(null);
323842
324534
  const plansRequestRef = import_react66.default.useRef(null);
@@ -323853,7 +324545,8 @@ function PurchaseFlowView({
323853
324545
  setFlowError({ message, backStep, retryLabel: retry?.label });
323854
324546
  setStep("error");
323855
324547
  }, []);
323856
- const complete = import_react66.useCallback(() => {
324548
+ const complete = import_react66.useCallback((requirement) => {
324549
+ setSuccessRequirement(requirement);
323857
324550
  setStep("success");
323858
324551
  if (successTimerRef.current)
323859
324552
  clearTimeout(successTimerRef.current);
@@ -323883,6 +324576,7 @@ function PurchaseFlowView({
323883
324576
  if (plansRequestRef.current !== controller)
323884
324577
  return;
323885
324578
  const eligible2 = filterCliPurchasablePlans(groups, subscriptions);
324579
+ setSubscriptionsByGroup(new Map(subscriptions.filter((subscription) => ["active", "trialing", "past_due"].includes(subscription.status)).map((subscription) => [subscription.groupId, subscription])));
323886
324580
  if (eligible2.length === 0) {
323887
324581
  const message = groups.length === 0 ? "Nenhum plano está disponível no momento." : "Não há novos planos compatíveis com a CLI para esta conta.";
323888
324582
  showError(message, "splash", {
@@ -323907,7 +324601,7 @@ function PurchaseFlowView({
323907
324601
  plansRequestRef.current = null;
323908
324602
  }
323909
324603
  }, [accessToken, showError]);
323910
- const startEntitlementPolling = import_react66.useCallback(async function pollEntitlement(groupId, displayStep = "polling") {
324604
+ const startEntitlementPolling = import_react66.useCallback(async function pollEntitlement(groupId, displayStep = "polling", requirement = "paid") {
323911
324605
  pollingRef.current?.abort();
323912
324606
  const controller = new AbortController;
323913
324607
  pollingRef.current = controller;
@@ -323916,11 +324610,12 @@ function PurchaseFlowView({
323916
324610
  while (!controller.signal.aborted && Date.now() - startedAt < POLL_TIMEOUT_MS) {
323917
324611
  try {
323918
324612
  const active = await isGroupSubscriptionActive(accessToken, groupId, {
323919
- signal: controller.signal
324613
+ signal: controller.signal,
324614
+ requirement
323920
324615
  });
323921
324616
  if (active) {
323922
324617
  if (pollingRef.current === controller)
323923
- complete();
324618
+ complete(requirement);
323924
324619
  return;
323925
324620
  }
323926
324621
  } catch (error42) {
@@ -323941,7 +324636,7 @@ function PurchaseFlowView({
323941
324636
  if (pollingRef.current === controller && !controller.signal.aborted) {
323942
324637
  showError("A assinatura foi iniciada, mas os modelos ainda não foram liberados.", "plan-detail", {
323943
324638
  label: "Verificar novamente",
323944
- run: () => void pollEntitlement(groupId, displayStep)
324639
+ run: () => void pollEntitlement(groupId, displayStep, requirement)
323945
324640
  });
323946
324641
  }
323947
324642
  }, [accessToken, complete, showError]);
@@ -323980,7 +324675,7 @@ function PurchaseFlowView({
323980
324675
  setCardlessVerification(result);
323981
324676
  setStep("whatsapp-code");
323982
324677
  } else {
323983
- startEntitlementPolling(group.id, "cardless-polling");
324678
+ startEntitlementPolling(group.id, "cardless-polling", "access");
323984
324679
  }
323985
324680
  } catch (error42) {
323986
324681
  const presentation = describePurchaseError(error42, "Não foi possível iniciar o teste sem cartão.");
@@ -324016,7 +324711,7 @@ function PurchaseFlowView({
324016
324711
  try {
324017
324712
  const result = await confirmCardlessTrial(accessToken, cardlessVerification.verificationId, code);
324018
324713
  if (result.mode === "trial_activated") {
324019
- startEntitlementPolling(group.id, "cardless-polling");
324714
+ startEntitlementPolling(group.id, "cardless-polling", "access");
324020
324715
  } else {
324021
324716
  setCardlessVerification(result);
324022
324717
  setStep("whatsapp-code");
@@ -324064,7 +324759,7 @@ function PurchaseFlowView({
324064
324759
  setStep("whatsapp-code");
324065
324760
  }
324066
324761
  }, [accessToken, cardlessVerification, verificationWaitSeconds]);
324067
- const handleCheckout = import_react66.useCallback(async function runCheckout(group, paymentMethod, woovi) {
324762
+ const handleCheckout = import_react66.useCallback(async function runCheckout(group, paymentMethod, woovi, requirement = "paid") {
324068
324763
  setInlineMessage(null);
324069
324764
  setStep("checkout");
324070
324765
  try {
@@ -324073,7 +324768,7 @@ function PurchaseFlowView({
324073
324768
  woovi
324074
324769
  });
324075
324770
  if (result.mode === "reactivated") {
324076
- startEntitlementPolling(group.id);
324771
+ startEntitlementPolling(group.id, "polling", requirement);
324077
324772
  return;
324078
324773
  }
324079
324774
  if (result.mode === "woovi") {
@@ -324085,8 +324780,9 @@ function PurchaseFlowView({
324085
324780
  return;
324086
324781
  }
324087
324782
  setManualCheckoutUrl(result.url);
324783
+ setManualEntitlementRequirement(requirement);
324088
324784
  if (await openBrowser(result.url)) {
324089
- startEntitlementPolling(group.id);
324785
+ startEntitlementPolling(group.id, "polling", requirement);
324090
324786
  } else {
324091
324787
  setStep("manual-browser");
324092
324788
  }
@@ -324107,7 +324803,7 @@ function PurchaseFlowView({
324107
324803
  }
324108
324804
  if (presentation.code === "already_subscribed" || presentation.code === "manual_access_active") {
324109
324805
  setInlineMessage(presentation.message);
324110
- startEntitlementPolling(group.id);
324806
+ startEntitlementPolling(group.id, "polling", requirement);
324111
324807
  return;
324112
324808
  }
324113
324809
  if (presentation.code === "payment_method_required" || presentation.code === "payment_method_unavailable") {
@@ -324117,20 +324813,33 @@ function PurchaseFlowView({
324117
324813
  }
324118
324814
  showError(presentation.message, "plan-detail", {
324119
324815
  label: "Tentar checkout novamente",
324120
- run: () => void runCheckout(group, paymentMethod, woovi)
324816
+ run: () => void runCheckout(group, paymentMethod, woovi, requirement)
324121
324817
  });
324122
324818
  }
324123
324819
  }, [accessToken, fetchPlans, showError, startEntitlementPolling]);
324124
- const startPaidPurchase = import_react66.useCallback((group) => {
324820
+ const startPaidPurchase = import_react66.useCallback(async (group) => {
324125
324821
  setSelectedPlan(group);
324126
324822
  setInlineMessage(null);
324823
+ const currentSubscription = subscriptionsByGroup.get(group.id);
324824
+ if (currentSubscription?.source === "stripe_trial" && currentSubscription.status === "trialing") {
324825
+ const conversionUrl = getStripeTrialConversionUrl(currentSubscription.id, group.billingInterval);
324826
+ setManualCheckoutUrl(conversionUrl);
324827
+ setManualEntitlementRequirement("paid");
324828
+ setStep("checkout");
324829
+ if (await openBrowser(conversionUrl)) {
324830
+ startEntitlementPolling(group.id, "polling", "paid");
324831
+ } else {
324832
+ setStep("manual-browser");
324833
+ }
324834
+ return;
324835
+ }
324127
324836
  if (group.paymentProvider === "both")
324128
324837
  setStep("payment-method");
324129
324838
  else if (group.paymentProvider === "woovi")
324130
324839
  setStep("woovi-form");
324131
324840
  else
324132
324841
  handleCheckout(group, "stripe");
324133
- }, [handleCheckout]);
324842
+ }, [handleCheckout, startEntitlementPolling, subscriptionsByGroup]);
324134
324843
  const cancelPlansLoading = import_react66.useCallback(() => {
324135
324844
  plansRequestRef.current?.abort();
324136
324845
  plansRequestRef.current = null;
@@ -324281,7 +324990,7 @@ function PurchaseFlowView({
324281
324990
  dimColor: true,
324282
324991
  children: paymentProviderLabel(plan)
324283
324992
  }),
324284
- plan.trialEligible && plan.trialDays ? /* @__PURE__ */ jsx_runtime86.jsxs(ThemedText, {
324993
+ (hasCardlessTrial(plan) || hasCardTrial(plan)) && plan.trialDays ? /* @__PURE__ */ jsx_runtime86.jsxs(ThemedText, {
324285
324994
  color: "success",
324286
324995
  children: [
324287
324996
  plan.trialDays,
@@ -324347,7 +325056,7 @@ function PurchaseFlowView({
324347
325056
  paymentProviderLabel(plan)
324348
325057
  ]
324349
325058
  }),
324350
- plan.trialEligible && plan.trialDays ? /* @__PURE__ */ jsx_runtime86.jsxs(ThemedText, {
325059
+ (hasCardlessTrial(plan) || hasCardTrial(plan)) && plan.trialDays ? /* @__PURE__ */ jsx_runtime86.jsxs(ThemedText, {
324351
325060
  color: "success",
324352
325061
  children: [
324353
325062
  "Teste: ",
@@ -324368,7 +325077,7 @@ function PurchaseFlowView({
324368
325077
  if (value === "trial")
324369
325078
  prepareCardlessTrial(plan);
324370
325079
  else if (value === "card-trial")
324371
- handleCheckout(plan, "stripe");
325080
+ handleCheckout(plan, "stripe", undefined, "access");
324372
325081
  else if (value === "pix")
324373
325082
  setStep("woovi-form");
324374
325083
  else if (value === "buy")
@@ -324602,7 +325311,7 @@ function PurchaseFlowView({
324602
325311
  ],
324603
325312
  onChange: (value) => {
324604
325313
  if (value === "verify" && selectedPlan) {
324605
- startEntitlementPolling(selectedPlan.id);
325314
+ startEntitlementPolling(selectedPlan.id, "polling", manualEntitlementRequirement);
324606
325315
  } else if (value === "back")
324607
325316
  setStep("plan-detail");
324608
325317
  else
@@ -324626,7 +325335,7 @@ function PurchaseFlowView({
324626
325335
  case "success":
324627
325336
  return /* @__PURE__ */ jsx_runtime86.jsx(ThemedText, {
324628
325337
  color: "success",
324629
- children: "Assinatura confirmada! Modelos disponíveis."
325338
+ children: successRequirement === "access" ? "Trial ativado! Modelos disponíveis." : "Assinatura paga confirmada! Modelos disponíveis."
324630
325339
  });
324631
325340
  case "error":
324632
325341
  return /* @__PURE__ */ jsx_runtime86.jsxs(ThemedBox_default, {
@@ -324698,6 +325407,7 @@ var init_purchaseFlow = __esm(() => {
324698
325407
  init_select();
324699
325408
  init_Spinner2();
324700
325409
  init_TextInput();
325410
+ init_oauth();
324701
325411
  init_useTerminalSize();
324702
325412
  init_ink2();
324703
325413
  init_AppState();
@@ -334198,7 +334908,7 @@ function AssistantToolUseMessage(t0) {
334198
334908
  t1 = null;
334199
334909
  break bb0;
334200
334910
  }
334201
- const tool = findToolByName(tools, param.name);
334911
+ const tool = findToolByNameOrUniquePrefix(tools, param.name);
334202
334912
  if (!tool) {
334203
334913
  t1 = null;
334204
334914
  break bb0;
@@ -338231,7 +338941,7 @@ function VerboseToolUse(t0) {
338231
338941
  if ($2[0] !== bg || $2[1] !== content.id || $2[2] !== content.input || $2[3] !== content.name || $2[4] !== inProgressToolUseIDs || $2[5] !== lookups || $2[6] !== shouldAnimate || $2[7] !== theme || $2[8] !== tools) {
338232
338942
  t2 = Symbol.for("react.early_return_sentinel");
338233
338943
  bb0: {
338234
- const tool = findToolByName(tools, content.name) ?? findToolByName(getReplPrimitiveTools(), content.name);
338944
+ const tool = findToolByNameOrUniquePrefix(tools, content.name) ?? findToolByNameOrUniquePrefix(getReplPrimitiveTools(), content.name);
338235
338945
  if (!tool) {
338236
338946
  t2 = null;
338237
338947
  break bb0;
@@ -338912,7 +339622,7 @@ function GroupedToolUseContent({
338912
339622
  inProgressToolUseIDs,
338913
339623
  shouldAnimate
338914
339624
  }) {
338915
- const tool = findToolByName(tools, message.toolName);
339625
+ const tool = findToolByNameOrUniquePrefix(tools, message.toolName);
338916
339626
  if (!tool?.renderGroupedToolUse) {
338917
339627
  return null;
338918
339628
  }
@@ -340746,7 +341456,7 @@ function useGetToolFromMessages(toolUseID, tools, lookups) {
340746
341456
  t0 = null;
340747
341457
  break bb0;
340748
341458
  }
340749
- const tool = findToolByName(tools, toolUse.name);
341459
+ const tool = findToolByNameOrUniquePrefix(tools, toolUse.name);
340750
341460
  if (!tool) {
340751
341461
  t0 = null;
340752
341462
  break bb0;
@@ -342406,7 +343116,7 @@ function extractLastToolInfo(progressMessages, tools) {
342406
343116
  if (toolResultBlock?.type === "tool_result") {
342407
343117
  const toolUseBlock = toolUseByID.get(toolResultBlock.tool_use_id);
342408
343118
  if (toolUseBlock) {
342409
- const tool = findToolByName(tools, toolUseBlock.name);
343119
+ const tool = findToolByNameOrUniquePrefix(tools, toolUseBlock.name);
342410
343120
  if (!tool) {
342411
343121
  return toolUseBlock.name;
342412
343122
  }
@@ -387889,6 +388599,7 @@ class StreamingToolExecutor {
387889
388599
  this.tools.push({
387890
388600
  id: block2.id,
387891
388601
  block: block2,
388602
+ canonicalName: block2.name,
387892
388603
  assistantMessage: assistantMessage2,
387893
388604
  status: "completed",
387894
388605
  isConcurrencySafe: true,
@@ -387921,6 +388632,7 @@ class StreamingToolExecutor {
387921
388632
  this.tools.push({
387922
388633
  id: block2.id,
387923
388634
  block: block2,
388635
+ canonicalName: toolDefinition.name,
387924
388636
  assistantMessage: assistantMessage2,
387925
388637
  status: "queued",
387926
388638
  isConcurrencySafe,
@@ -388013,7 +388725,7 @@ class StreamingToolExecutor {
388013
388725
  return null;
388014
388726
  }
388015
388727
  getToolInterruptBehavior(tool) {
388016
- const definition = findToolByName(this.toolDefinitions, tool.block.name);
388728
+ const definition = findToolByNameOrUniquePrefix(this.toolDefinitions, tool.canonicalName);
388017
388729
  if (!definition?.interruptBehavior)
388018
388730
  return "block";
388019
388731
  try {
@@ -388027,9 +388739,9 @@ class StreamingToolExecutor {
388027
388739
  const summary = input?.command ?? input?.file_path ?? input?.pattern ?? "";
388028
388740
  if (typeof summary === "string" && summary.length > 0) {
388029
388741
  const truncated = summary.length > 40 ? summary.slice(0, 40) + "…" : summary;
388030
- return `${tool.block.name}(${truncated})`;
388742
+ return `${tool.canonicalName}(${truncated})`;
388031
388743
  }
388032
- return tool.block.name;
388744
+ return tool.canonicalName;
388033
388745
  }
388034
388746
  updateInterruptibleState() {
388035
388747
  const executing = this.tools.filter((t) => t.status === "executing");
@@ -388068,7 +388780,7 @@ class StreamingToolExecutor {
388068
388780
  const isErrorResult = update.message.type === "user" && Array.isArray(update.message.message.content) && update.message.message.content.some((_) => _.type === "tool_result" && _.is_error === true);
388069
388781
  if (isErrorResult) {
388070
388782
  thisToolErrored = true;
388071
- if (tool.block.name === BASH_TOOL_NAME) {
388783
+ if (tool.canonicalName === BASH_TOOL_NAME) {
388072
388784
  this.hasErrored = true;
388073
388785
  this.erroredToolDescription = this.getToolDescription(tool);
388074
388786
  this.siblingAbortController.abort("sibling_error");
@@ -390295,7 +391007,7 @@ async function* queryLoop(params, consumedCommandUuids) {
390295
391007
  for (let i3 = 0;i3 < message.message.content.length; i3++) {
390296
391008
  const block2 = message.message.content[i3];
390297
391009
  if (block2.type === "tool_use" && typeof block2.input === "object" && block2.input !== null) {
390298
- const tool = findToolByName(toolUseContext.options.tools, block2.name);
391010
+ const tool = findToolByNameOrUniquePrefix(toolUseContext.options.tools, block2.name);
390299
391011
  if (tool?.backfillObservableInput) {
390300
391012
  const originalInput = block2.input;
390301
391013
  const inputCopy = { ...originalInput };
@@ -391072,7 +391784,7 @@ function getAnthropicEnvMetadata() {
391072
391784
  function getBuildAgeMinutes() {
391073
391785
  if (false)
391074
391786
  ;
391075
- const buildTime = new Date("2026-08-16T02:41:36.697Z").getTime();
391787
+ const buildTime = new Date("2026-08-25T17:16:19.335Z").getTime();
391076
391788
  if (isNaN(buildTime))
391077
391789
  return;
391078
391790
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -405139,7 +405851,7 @@ function normalizeMessagesForAPI(messages, tools = []) {
405139
405851
  ...message.message,
405140
405852
  content: message.message.content.map((block2) => {
405141
405853
  if (block2.type === "tool_use") {
405142
- const tool = tools.find((t) => toolMatchesName(t, block2.name));
405854
+ const tool = findToolByNameOrUniquePrefix(tools, block2.name);
405143
405855
  const normalizedInput = tool ? normalizeToolInputForAPI(tool, block2.input) : block2.input;
405144
405856
  const canonicalName = tool?.name ?? block2.name;
405145
405857
  if (toolSearchEnabled) {
@@ -405377,11 +406089,12 @@ function normalizeContentFromAPI(contentBlocks, tools, agentId) {
405377
406089
  } else {
405378
406090
  normalizedInput = contentBlock.input;
405379
406091
  }
406092
+ const resolvedTool = findToolByNameOrUniquePrefix(tools, 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;
405380
406094
  if (typeof normalizedInput === "object" && normalizedInput !== null) {
405381
- const tool = findToolByName(tools, contentBlock.name);
405382
- if (tool) {
406095
+ if (resolvedTool) {
405383
406096
  try {
405384
- normalizedInput = normalizeToolInput(tool, normalizedInput, agentId);
406097
+ normalizedInput = normalizeToolInput(resolvedTool, normalizedInput, agentId);
405385
406098
  } catch (error42) {
405386
406099
  logError2(new Error("Error normalizing tool input: " + error42));
405387
406100
  }
@@ -405389,6 +406102,7 @@ function normalizeContentFromAPI(contentBlocks, tools, agentId) {
405389
406102
  }
405390
406103
  return {
405391
406104
  ...contentBlock,
406105
+ name: normalizedToolName,
405392
406106
  input: normalizedInput
405393
406107
  };
405394
406108
  }
@@ -405552,6 +406266,7 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405552
406266
  }
405553
406267
  if (isSyntheticApiErrorMessage(message)) {
405554
406268
  onSetStreamMode("tool-use");
406269
+ onStreamingToolUses(() => []);
405555
406270
  }
405556
406271
  }
405557
406272
  onStreamingText?.(() => null);
@@ -405563,6 +406278,7 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405563
406278
  return;
405564
406279
  }
405565
406280
  if (message.event.type === "message_start") {
406281
+ onStreamingToolUses(() => []);
405566
406282
  if (message.ttftMs != null) {
405567
406283
  onApiMetrics?.({ ttftMs: message.ttftMs });
405568
406284
  }
@@ -405588,14 +406304,28 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405588
406304
  onSetStreamMode("tool-input");
405589
406305
  const contentBlock = message.event.content_block;
405590
406306
  const index = message.event.index;
405591
- onStreamingToolUses((_) => [
405592
- ..._,
405593
- {
405594
- index,
405595
- contentBlock,
405596
- 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);
405597
406319
  }
405598
- ]);
406320
+ return [
406321
+ ...current,
406322
+ {
406323
+ index,
406324
+ contentBlock,
406325
+ unparsedToolInput: ""
406326
+ }
406327
+ ];
406328
+ });
405599
406329
  return;
405600
406330
  }
405601
406331
  case "server_tool_use":
@@ -405630,13 +406360,10 @@ function handleMessageFromStream(message, onMessage2, onUpdateLength, onSetStrea
405630
406360
  if (!element) {
405631
406361
  return _;
405632
406362
  }
405633
- return [
405634
- ..._.filter((_2) => _2 !== element),
405635
- {
405636
- ...element,
405637
- unparsedToolInput: element.unparsedToolInput + delta
405638
- }
405639
- ];
406363
+ return _.map((toolUse) => toolUse === element ? {
406364
+ ...toolUse,
406365
+ unparsedToolInput: toolUse.unparsedToolInput + delta
406366
+ } : toolUse);
405640
406367
  });
405641
406368
  return;
405642
406369
  }
@@ -410613,6 +411340,15 @@ import { mkdir as mkdir26 } from "fs/promises";
410613
411340
  import { createServer as createServer4 } from "http";
410614
411341
  import { join as join100 } from "path";
410615
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
+ }
410616
411352
  function redactSensitiveUrlParams(url3) {
410617
411353
  try {
410618
411354
  const parsedUrl = new URL(url3);
@@ -411419,7 +412155,7 @@ class ClaudeAuthProvider {
411419
412155
  const storage3 = getSecureStorage();
411420
412156
  const data = await storage3.readAsync();
411421
412157
  const serverKey = getServerKey(this.serverName, this.serverConfig);
411422
- const tokenData = data?.mcpOAuth?.[serverKey];
412158
+ let tokenData = data?.mcpOAuth?.[serverKey];
411423
412159
  if (isXaaEnabled() && this.serverConfig.oauth?.xaa && !tokenData?.refreshToken && (!tokenData?.accessToken || (tokenData.expiresAt - Date.now()) / 1000 <= 300)) {
411424
412160
  if (!this._refreshInProgress) {
411425
412161
  logMCPDebug(this.serverName, tokenData ? `XAA: access_token expiring, attempting silent exchange` : `XAA: no access_token yet, attempting silent exchange`);
@@ -411464,22 +412200,31 @@ class ClaudeAuthProvider {
411464
412200
  logMCPDebug(this.serverName, `Token refreshed successfully`);
411465
412201
  return refreshed;
411466
412202
  }
411467
- 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
+ }
411468
412209
  } catch (error42) {
411469
412210
  logMCPDebug(this.serverName, `Token refresh error: ${errorMessage(error42)}`);
411470
412211
  }
411471
412212
  }
412213
+ if (!tokenData) {
412214
+ return;
412215
+ }
412216
+ const currentExpiresIn = (tokenData.expiresAt - Date.now()) / 1000;
411472
412217
  const tokens = {
411473
412218
  access_token: tokenData.accessToken,
411474
412219
  refresh_token: needsStepUp ? undefined : tokenData.refreshToken,
411475
- expires_in: expiresIn,
412220
+ expires_in: currentExpiresIn,
411476
412221
  scope: tokenData.scope,
411477
412222
  token_type: "Bearer"
411478
412223
  };
411479
412224
  logMCPDebug(this.serverName, `Returning tokens`);
411480
412225
  logMCPDebug(this.serverName, `Token length: ${tokens.access_token?.length}`);
411481
412226
  logMCPDebug(this.serverName, `Has refresh token: ${!!tokens.refresh_token}`);
411482
- logMCPDebug(this.serverName, `Expires in: ${Math.floor(expiresIn)}s`);
412227
+ logMCPDebug(this.serverName, `Expires in: ${Math.floor(currentExpiresIn)}s`);
411483
412228
  return tokens;
411484
412229
  }
411485
412230
  async saveTokens(tokens) {
@@ -411856,8 +412601,10 @@ class ClaudeAuthProvider {
411856
412601
  emitRefreshEvent("failure", "no_tokens_returned");
411857
412602
  return;
411858
412603
  } catch (error42) {
411859
- if (error42 instanceof InvalidGrantError) {
411860
- 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)}`);
411861
412608
  clearKeychainCache();
411862
412609
  const storage3 = getSecureStorage();
411863
412610
  const data = storage3.read();
@@ -411876,9 +412623,9 @@ class ClaudeAuthProvider {
411876
412623
  };
411877
412624
  }
411878
412625
  }
411879
- logMCPDebug(this.serverName, `No valid tokens in storage, clearing stored tokens`);
411880
- await this.invalidateCredentials("tokens");
411881
- 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");
411882
412629
  return;
411883
412630
  }
411884
412631
  const isTimeoutError = error42 instanceof Error && /timeout|timed out|etimedout|econnreset/i.test(error42.message);
@@ -432873,7 +433620,7 @@ function buildPrimarySection() {
432873
433620
  });
432874
433621
  return [{
432875
433622
  label: "Version",
432876
- value: "0.15.15"
433623
+ value: "0.15.17"
432877
433624
  }, {
432878
433625
  label: "Session name",
432879
433626
  value: nameValue
@@ -446803,7 +447550,7 @@ function getReleaseTagUrl(version2 = publicBuildVersion) {
446803
447550
  return `${VERBOO_RELEASES_URL}/tag/v${normalizePublicVersion(version2)}`;
446804
447551
  }
446805
447552
  function getPublicBuildVersion() {
446806
- return "0.15.15";
447553
+ return "0.15.17";
446807
447554
  }
446808
447555
  var import_semver10, VERBOO_RELEASES_URL = "https://github.com/verbeux-ai/code/releases", fallbackBuildVersion, publicBuildVersion;
446809
447556
  var init_version = __esm(() => {
@@ -473801,7 +474548,7 @@ var import_react_compiler_runtime198, React97, import_react155, jsx_runtime267,
473801
474548
  const msg_1 = createAssistantMessage({
473802
474549
  content: [streamingToolUse.contentBlock]
473803
474550
  });
473804
- msg_1.uuid = deriveUUID(streamingToolUse.contentBlock.id, 0);
474551
+ msg_1.uuid = deriveUUID(streamingToolUse.contentBlock.id, streamingToolUse.index);
473805
474552
  return normalizeMessages([msg_1]);
473806
474553
  }), [streamingToolUsesWithoutInProgress]);
473807
474554
  const isTranscriptMode = screen === "transcript";
@@ -473882,7 +474629,7 @@ var import_react_compiler_runtime198, React97, import_react155, jsx_runtime267,
473882
474629
  if (b_0?.type !== "tool_result" || b_0.is_error || !msg_6.toolUseResult)
473883
474630
  return false;
473884
474631
  const name = lookupsRef.current.toolUseByToolUseID.get(b_0.tool_use_id)?.name;
473885
- const tool = name ? findToolByName(tools, name) : undefined;
474632
+ const tool = name ? findToolByNameOrUniquePrefix(tools, name) : undefined;
473886
474633
  return tool?.isResultTruncated?.(msg_6.toolUseResult) ?? false;
473887
474634
  }, [tools]);
473888
474635
  const canAnimate = (!toolJSX || !!toolJSX.shouldContinueAnimation) && !toolUseConfirmQueue.length && !isMessageSelectorVisible;
@@ -473952,7 +474699,7 @@ var import_react_compiler_runtime198, React97, import_react155, jsx_runtime267,
473952
474699
  const tr = msg_9.message.content.find((b_1) => b_1.type === "tool_result");
473953
474700
  if (tr && "tool_use_id" in tr) {
473954
474701
  const tu = lookups_0.toolUseByToolUseID.get(tr.tool_use_id);
473955
- const tool_0 = tu && findToolByName(tools, tu.name);
474702
+ const tool_0 = tu && findToolByNameOrUniquePrefix(tools, tu.name);
473956
474703
  const extracted = tool_0?.extractSearchText?.(msg_9.toolUseResult);
473957
474704
  if (extracted !== undefined)
473958
474705
  text_0 = extracted;
@@ -478860,7 +479607,7 @@ var init_ultraplan = __esm(() => {
478860
479607
 
478861
479608
  // src/components/tasks/renderToolActivity.tsx
478862
479609
  function renderToolActivity(activity, tools, theme) {
478863
- const tool = findToolByName(tools, activity.toolName);
479610
+ const tool = findToolByNameOrUniquePrefix(tools, activity.toolName);
478864
479611
  if (!tool) {
478865
479612
  return activity.toolName;
478866
479613
  }
@@ -498197,7 +498944,7 @@ var init_bridge_kick = __esm(() => {
498197
498944
  var call66 = async () => {
498198
498945
  return {
498199
498946
  type: "text",
498200
- value: `${"99.0.0"} (built ${"2026-08-16T02:41:36.697Z"})`
498947
+ value: `${"99.0.0"} (built ${"2026-08-25T17:16:19.335Z"})`
498201
498948
  };
498202
498949
  }, version2, version_default;
498203
498950
  var init_version2 = __esm(() => {
@@ -500019,6 +500766,8 @@ var init_verbooInChrome = __esm(() => {
500019
500766
  VERBOO_IN_CHROME_TOOL_NAMES = [
500020
500767
  "navigate",
500021
500768
  "read_page",
500769
+ "find",
500770
+ "extract_page_content",
500022
500771
  "structured_extract",
500023
500772
  "click",
500024
500773
  "type",
@@ -517800,8 +518549,8 @@ async function prepareIfConditionMatcher(hookInput, tools) {
517800
518549
  if (hookInput.hook_event_name !== "PreToolUse" && hookInput.hook_event_name !== "PostToolUse" && hookInput.hook_event_name !== "PostToolUseFailure" && hookInput.hook_event_name !== "PermissionRequest") {
517801
518550
  return;
517802
518551
  }
517803
- const toolName = normalizeLegacyToolName(hookInput.tool_name);
517804
- const tool = tools && findToolByName(tools, hookInput.tool_name);
518552
+ const tool = tools && findToolByNameOrUniquePrefix(tools, hookInput.tool_name);
518553
+ const toolName = normalizeLegacyToolName(tool?.name ?? hookInput.tool_name);
517805
518554
  const input = tool?.inputSchema.safeParse(hookInput.tool_input);
517806
518555
  const patternMatcher = input?.success && tool?.preparePermissionMatcher ? await tool.preparePermissionMatcher(input.data) : undefined;
517807
518556
  return (ifCondition) => {
@@ -521933,7 +522682,7 @@ function printStartupScreen(modelOverride) {
521933
522682
  const home = process.env.HOME || process.env.USERPROFILE || "";
521934
522683
  const cwd2 = process.cwd();
521935
522684
  const displayCwd = home && cwd2.startsWith(home) ? `~${cwd2.slice(home.length)}` : cwd2;
521936
- const version3 = "0.15.15";
522685
+ const version3 = "0.15.17";
521937
522686
  const columns = process.stdout.columns ?? STARTUP_DEFAULT_COLUMNS;
521938
522687
  process.stdout.write(renderStartupScreen(p, version3, displayCwd, columns));
521939
522688
  }
@@ -541162,7 +541911,7 @@ var init_routerRateLimitHook = __esm(() => {
541162
541911
  function getSemverPart(version3) {
541163
541912
  return `${import_semver13.major(version3, { loose: true })}.${import_semver13.minor(version3, { loose: true })}.${import_semver13.patch(version3, { loose: true })}`;
541164
541913
  }
541165
- function useUpdateNotification(updatedVersion, initialVersion = "0.15.15") {
541914
+ function useUpdateNotification(updatedVersion, initialVersion = "0.15.17") {
541166
541915
  const [lastNotifiedSemver, setLastNotifiedSemver] = import_react226.useState(() => getSemverPart(initialVersion));
541167
541916
  const [pendingNotification2, setPendingNotification] = import_react226.useState(null);
541168
541917
  if (updatedVersion) {
@@ -541202,7 +541951,7 @@ function AutoUpdater({
541202
541951
  return;
541203
541952
  }
541204
541953
  if (false) {}
541205
- const currentVersion = "0.15.15";
541954
+ const currentVersion = "0.15.17";
541206
541955
  const channel2 = getInitialSettings()?.autoUpdatesChannel ?? "latest";
541207
541956
  let latestVersion = await getLatestVersion(channel2);
541208
541957
  const isDisabled = isAutoUpdaterDisabled();
@@ -541555,17 +542304,17 @@ function PackageManagerAutoUpdater(t0) {
541555
542304
  const maxVersion = await getMaxVersion();
541556
542305
  if (maxVersion && latest && gt(latest, maxVersion)) {
541557
542306
  logForDebugging(`PackageManagerAutoUpdater: maxVersion ${maxVersion} is set, capping update from ${latest} to ${maxVersion}`);
541558
- if (gte("0.15.15", maxVersion)) {
541559
- logForDebugging(`PackageManagerAutoUpdater: current version ${"0.15.15"} 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`);
541560
542309
  setUpdateAvailable(false);
541561
542310
  return;
541562
542311
  }
541563
542312
  latest = maxVersion;
541564
542313
  }
541565
- const hasUpdate = latest && !gte("0.15.15", latest) && !shouldSkipVersion(latest);
542314
+ const hasUpdate = latest && !gte("0.15.17", latest) && !shouldSkipVersion(latest);
541566
542315
  setUpdateAvailable(!!hasUpdate);
541567
542316
  if (hasUpdate) {
541568
- logForDebugging(`PackageManagerAutoUpdater: Update available ${"0.15.15"} -> ${latest}`);
542317
+ logForDebugging(`PackageManagerAutoUpdater: Update available ${"0.15.17"} -> ${latest}`);
541569
542318
  }
541570
542319
  };
541571
542320
  $2[0] = t1;
@@ -541599,7 +542348,7 @@ function PackageManagerAutoUpdater(t0) {
541599
542348
  wrap: "truncate",
541600
542349
  children: [
541601
542350
  "currentVersion: ",
541602
- "0.15.15"
542351
+ "0.15.17"
541603
542352
  ]
541604
542353
  });
541605
542354
  $2[3] = verbose;
@@ -556135,7 +556884,7 @@ function useRemoteSession({
556135
556884
  },
556136
556885
  onPermissionRequest: (request, requestId) => {
556137
556886
  logForDebugging(`[useRemoteSession] Permission request for tool: ${request.tool_name}`);
556138
- const tool = findToolByName(toolsRef.current, request.tool_name) ?? createToolStub(request.tool_name);
556887
+ const tool = findToolByNameOrUniquePrefix(toolsRef.current, request.tool_name) ?? createToolStub(request.tool_name);
556139
556888
  const syntheticMessage = createSyntheticAssistantMessage(request, requestId);
556140
556889
  const permissionResult = {
556141
556890
  behavior: "ask",
@@ -556486,7 +557235,7 @@ function useDirectConnect({
556486
557235
  },
556487
557236
  onPermissionRequest: (request, requestId) => {
556488
557237
  logForDebugging(`[useDirectConnect] Permission request for tool: ${request.tool_name}`);
556489
- const tool = findToolByName(toolsRef.current, request.tool_name) ?? createToolStub(request.tool_name);
557238
+ const tool = findToolByNameOrUniquePrefix(toolsRef.current, request.tool_name) ?? createToolStub(request.tool_name);
556490
557239
  const syntheticMessage = createSyntheticAssistantMessage(request, requestId);
556491
557240
  const permissionResult = {
556492
557241
  behavior: "ask",
@@ -556636,7 +557385,7 @@ function useSSHSession({
556636
557385
  },
556637
557386
  onPermissionRequest: (request, requestId) => {
556638
557387
  logForDebugging(`[useSSHSession] permission request: ${request.tool_name}`);
556639
- const tool = findToolByName(toolsRef.current, request.tool_name) ?? createToolStub(request.tool_name);
557388
+ const tool = findToolByNameOrUniquePrefix(toolsRef.current, request.tool_name) ?? createToolStub(request.tool_name);
556640
557389
  const syntheticMessage = createSyntheticAssistantMessage(request, requestId);
556641
557390
  const permissionResult = {
556642
557391
  behavior: "ask",
@@ -557289,10 +558038,10 @@ async function autoUpdateCliInBackground() {
557289
558038
  return;
557290
558039
  const channel2 = getInitialSettings()?.autoUpdatesChannel ?? "latest";
557291
558040
  const latest = await getLatestVersion(channel2);
557292
- if (!latest || gte("0.15.15", latest))
558041
+ if (!latest || gte("0.15.17", latest))
557293
558042
  return;
557294
558043
  writeToStdout(`
557295
- Nova versão disponível: ${latest} (atual: ${"0.15.15"})
558044
+ Nova versão disponível: ${latest} (atual: ${"0.15.17"})
557296
558045
  `);
557297
558046
  writeToStdout(`Atualizando automaticamente...
557298
558047
  `);
@@ -560943,7 +561692,7 @@ function useInboxPoller({
560943
561692
  if (!parsed)
560944
561693
  continue;
560945
561694
  if (setToolUseConfirmQueue) {
560946
- const tool = findToolByName(getAllBaseTools(), parsed.tool_name);
561695
+ const tool = findToolByNameOrUniquePrefix(getAllBaseTools(), parsed.tool_name);
560947
561696
  if (!tool) {
560948
561697
  logForDebugging(`[InboxPoller] Unknown tool ${parsed.tool_name}, skipping permission request`);
560949
561698
  continue;
@@ -574730,7 +575479,7 @@ var init_ApproveApiKey = __esm(() => {
574730
575479
 
574731
575480
  // src/components/LogoV2/WelcomeV2.tsx
574732
575481
  function WelcomeV2() {
574733
- const version3 = "0.15.15";
575482
+ const version3 = "0.15.17";
574734
575483
  return /* @__PURE__ */ jsx_runtime476.jsxs(ThemedBox_default, {
574735
575484
  flexDirection: "column",
574736
575485
  marginY: 1,
@@ -593678,7 +594427,7 @@ __export(exports_update, {
593678
594427
  });
593679
594428
  async function update() {
593680
594429
  logEvent("tengu_update_check", {});
593681
- writeToStdout(`Current version: ${"0.15.15"}
594430
+ writeToStdout(`Current version: ${"0.15.17"}
593682
594431
  `);
593683
594432
  const channel2 = getInitialSettings()?.autoUpdatesChannel ?? "latest";
593684
594433
  writeToStdout(`Checking for updates to ${channel2} version...
@@ -593763,8 +594512,8 @@ async function update() {
593763
594512
  writeToStdout(`Verboo Code is managed by Homebrew.
593764
594513
  `);
593765
594514
  const latest = await getLatestVersion(channel2);
593766
- if (latest && !gte("0.15.15", latest)) {
593767
- writeToStdout(`Update available: ${"0.15.15"} → ${latest}
594515
+ if (latest && !gte("0.15.17", latest)) {
594516
+ writeToStdout(`Update available: ${"0.15.17"} → ${latest}
593768
594517
  `);
593769
594518
  writeToStdout(`
593770
594519
  `);
@@ -593780,8 +594529,8 @@ async function update() {
593780
594529
  writeToStdout(`Verboo Code is managed by winget.
593781
594530
  `);
593782
594531
  const latest = await getLatestVersion(channel2);
593783
- if (latest && !gte("0.15.15", latest)) {
593784
- writeToStdout(`Update available: ${"0.15.15"} → ${latest}
594532
+ if (latest && !gte("0.15.17", latest)) {
594533
+ writeToStdout(`Update available: ${"0.15.17"} → ${latest}
593785
594534
  `);
593786
594535
  writeToStdout(`
593787
594536
  `);
@@ -593797,8 +594546,8 @@ async function update() {
593797
594546
  writeToStdout(`Verboo Code is managed by apk.
593798
594547
  `);
593799
594548
  const latest = await getLatestVersion(channel2);
593800
- if (latest && !gte("0.15.15", latest)) {
593801
- writeToStdout(`Update available: ${"0.15.15"} → ${latest}
594549
+ if (latest && !gte("0.15.17", latest)) {
594550
+ writeToStdout(`Update available: ${"0.15.17"} → ${latest}
593802
594551
  `);
593803
594552
  writeToStdout(`
593804
594553
  `);
@@ -593851,11 +594600,11 @@ async function update() {
593851
594600
  `);
593852
594601
  await gracefulShutdown(1);
593853
594602
  }
593854
- if (result.latestVersion === "0.15.15") {
593855
- writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.15"})`) + `
594603
+ if (result.latestVersion === "0.15.17") {
594604
+ writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.17"})`) + `
593856
594605
  `);
593857
594606
  } else {
593858
- writeToStdout(source_default.green(`Successfully updated from ${"0.15.15"} to version ${result.latestVersion}`) + `
594607
+ writeToStdout(source_default.green(`Successfully updated from ${"0.15.17"} to version ${result.latestVersion}`) + `
593859
594608
  `);
593860
594609
  await regenerateCompletionCache();
593861
594610
  }
@@ -593915,12 +594664,12 @@ async function update() {
593915
594664
  `);
593916
594665
  await gracefulShutdown(1);
593917
594666
  }
593918
- if (latestVersion === "0.15.15") {
593919
- writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.15"})`) + `
594667
+ if (latestVersion === "0.15.17") {
594668
+ writeToStdout(source_default.green(`Verboo Code is up to date (${"0.15.17"})`) + `
593920
594669
  `);
593921
594670
  await gracefulShutdown(0);
593922
594671
  }
593923
- writeToStdout(`New version available: ${latestVersion} (current: ${"0.15.15"})
594672
+ writeToStdout(`New version available: ${latestVersion} (current: ${"0.15.17"})
593924
594673
  `);
593925
594674
  writeToStdout(`Installing update...
593926
594675
  `);
@@ -593965,7 +594714,7 @@ async function update() {
593965
594714
  logForDebugging(`update: Installation status: ${status2}`);
593966
594715
  switch (status2) {
593967
594716
  case "success":
593968
- writeToStdout(source_default.green(`Successfully updated from ${"0.15.15"} to version ${latestVersion}`) + `
594717
+ writeToStdout(source_default.green(`Successfully updated from ${"0.15.17"} to version ${latestVersion}`) + `
593969
594718
  `);
593970
594719
  await regenerateCompletionCache();
593971
594720
  break;
@@ -595304,7 +596053,7 @@ ${chromeSystemPrompt}` : chromeSystemPrompt;
595304
596053
  is_native_binary: isInBundledMode()
595305
596054
  });
595306
596055
  logMemoryDiagnostics("start", {
595307
- version: "0.15.15",
596056
+ version: "0.15.17",
595308
596057
  debug: debug2,
595309
596058
  debugToStderr,
595310
596059
  print: print ?? false,
@@ -596115,7 +596864,7 @@ Usage: verboo --remote "your task description"`, () => gracefulShutdown(1));
596115
596864
  pendingHookMessages
596116
596865
  }, renderAndRun);
596117
596866
  }
596118
- }).version(`0.15.15 (${cliDesc})`, "-v, --version", "Output the version number");
596867
+ }).version(`0.15.17 (${cliDesc})`, "-v, --version", "Output the version number");
596119
596868
  program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
596120
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.");
596121
596870
  if (canUserConfigureAdvisor()) {
@@ -596730,7 +597479,7 @@ if (false) {}
596730
597479
  async function main2() {
596731
597480
  const args = process.argv.slice(2);
596732
597481
  if (args.length === 1 && (args[0] === "--version" || args[0] === "-v" || args[0] === "-V")) {
596733
- console.log(`${"0.15.15"} (Verboo Code)`);
597482
+ console.log(`${"0.15.17"} (Verboo Code)`);
596734
597483
  return;
596735
597484
  }
596736
597485
  if (!IS_VERBOO_CLI && args.includes("--provider")) {
@@ -596904,4 +597653,4 @@ async function main2() {
596904
597653
  }
596905
597654
  main2();
596906
597655
 
596907
- //# debugId=BA1810B7F2F5F50A64756E2164756E21
597656
+ //# debugId=5CD1B0C278B044C764756E2164756E21