@ljoukov/llm 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,10 +30,39 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION: () => CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
34
+ CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION: () => CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
35
+ CODEX_APPLY_PATCH_LARK_GRAMMAR: () => CODEX_APPLY_PATCH_LARK_GRAMMAR,
36
+ FIREWORKS_DEFAULT_GLM_MODEL: () => FIREWORKS_DEFAULT_GLM_MODEL,
37
+ FIREWORKS_DEFAULT_KIMI_MODEL: () => FIREWORKS_DEFAULT_KIMI_MODEL,
38
+ FIREWORKS_DEFAULT_MINIMAX_MODEL: () => FIREWORKS_DEFAULT_MINIMAX_MODEL,
39
+ FIREWORKS_MODEL_IDS: () => FIREWORKS_MODEL_IDS,
40
+ InMemoryAgentFilesystem: () => InMemoryAgentFilesystem,
33
41
  LlmJsonCallError: () => LlmJsonCallError,
34
42
  appendMarkdownSourcesSection: () => appendMarkdownSourcesSection,
43
+ applyPatch: () => applyPatch,
35
44
  configureGemini: () => configureGemini,
36
45
  convertGooglePartsToLlmParts: () => convertGooglePartsToLlmParts,
46
+ createApplyPatchTool: () => createApplyPatchTool,
47
+ createCodexApplyPatchTool: () => createCodexApplyPatchTool,
48
+ createCodexFilesystemToolSet: () => createCodexFilesystemToolSet,
49
+ createCodexReadFileTool: () => createCodexReadFileTool,
50
+ createFilesystemToolSetForModel: () => createFilesystemToolSetForModel,
51
+ createGeminiFilesystemToolSet: () => createGeminiFilesystemToolSet,
52
+ createGeminiReadFileTool: () => createGeminiReadFileTool,
53
+ createGlobTool: () => createGlobTool,
54
+ createGrepFilesTool: () => createGrepFilesTool,
55
+ createGrepSearchTool: () => createGrepSearchTool,
56
+ createInMemoryAgentFilesystem: () => createInMemoryAgentFilesystem,
57
+ createListDirTool: () => createListDirTool,
58
+ createListDirectoryTool: () => createListDirectoryTool,
59
+ createModelAgnosticFilesystemToolSet: () => createModelAgnosticFilesystemToolSet,
60
+ createNodeAgentFilesystem: () => createNodeAgentFilesystem,
61
+ createReadFilesTool: () => createReadFilesTool,
62
+ createReplaceTool: () => createReplaceTool,
63
+ createRgSearchTool: () => createRgSearchTool,
64
+ createWriteFileTool: () => createWriteFileTool,
65
+ customTool: () => customTool,
37
66
  encodeChatGptAuthJson: () => encodeChatGptAuthJson,
38
67
  encodeChatGptAuthJsonB64: () => encodeChatGptAuthJsonB64,
39
68
  estimateCallCostUsd: () => estimateCallCostUsd,
@@ -44,11 +73,15 @@ __export(index_exports, {
44
73
  generateText: () => generateText,
45
74
  getChatGptAuthProfile: () => getChatGptAuthProfile,
46
75
  getCurrentToolCallContext: () => getCurrentToolCallContext,
76
+ isFireworksModelId: () => isFireworksModelId,
47
77
  isGeminiModelId: () => isGeminiModelId,
48
78
  loadEnvFromFile: () => loadEnvFromFile,
49
79
  loadLocalEnv: () => loadLocalEnv,
50
80
  parseJsonFromLlmText: () => parseJsonFromLlmText,
51
81
  refreshChatGptOauthToken: () => refreshChatGptOauthToken,
82
+ resolveFilesystemToolProfile: () => resolveFilesystemToolProfile,
83
+ resolveFireworksModelId: () => resolveFireworksModelId,
84
+ runAgentLoop: () => runAgentLoop,
52
85
  runToolLoop: () => runToolLoop,
53
86
  sanitisePartForLogging: () => sanitisePartForLogging,
54
87
  streamJson: () => streamJson,
@@ -131,6 +164,35 @@ function createAsyncQueue() {
131
164
  return { push, close, fail, iterable: iterator() };
132
165
  }
133
166
 
167
+ // src/fireworks/pricing.ts
168
+ var FIREWORKS_KIMI_K25_PRICING = {
169
+ inputRate: 0.6 / 1e6,
170
+ cachedRate: 0.1 / 1e6,
171
+ outputRate: 3 / 1e6
172
+ };
173
+ var FIREWORKS_GLM_5_PRICING = {
174
+ inputRate: 1 / 1e6,
175
+ cachedRate: 0.2 / 1e6,
176
+ outputRate: 3.2 / 1e6
177
+ };
178
+ var FIREWORKS_MINIMAX_M21_PRICING = {
179
+ inputRate: 0.3 / 1e6,
180
+ cachedRate: 0.15 / 1e6,
181
+ outputRate: 1.2 / 1e6
182
+ };
183
+ function getFireworksPricing(modelId) {
184
+ if (modelId.includes("kimi-k2.5") || modelId.includes("kimi-k2p5")) {
185
+ return FIREWORKS_KIMI_K25_PRICING;
186
+ }
187
+ if (modelId.includes("glm-5")) {
188
+ return FIREWORKS_GLM_5_PRICING;
189
+ }
190
+ if (modelId.includes("minimax-m2.1") || modelId.includes("minimax-m2p1")) {
191
+ return FIREWORKS_MINIMAX_M21_PRICING;
192
+ }
193
+ return void 0;
194
+ }
195
+
134
196
  // src/google/pricing.ts
135
197
  var GEMINI_3_PRO_PREVIEW_PRICING = {
136
198
  threshold: 2e5,
@@ -183,17 +245,34 @@ var OPENAI_GPT_52_PRICING = {
183
245
  cachedRate: 0.175 / 1e6,
184
246
  outputRate: 14 / 1e6
185
247
  };
186
- var OPENAI_GPT_51_CODEX_MINI_PRICING = {
248
+ var OPENAI_GPT_53_CODEX_PRICING = {
249
+ inputRate: 1.25 / 1e6,
250
+ cachedRate: 0.125 / 1e6,
251
+ outputRate: 10 / 1e6
252
+ };
253
+ var OPENAI_GPT_5_MINI_PRICING = {
187
254
  inputRate: 0.25 / 1e6,
188
255
  cachedRate: 0.025 / 1e6,
189
256
  outputRate: 2 / 1e6
190
257
  };
191
258
  function getOpenAiPricing(modelId) {
259
+ if (modelId.includes("gpt-5.3-codex-spark")) {
260
+ return OPENAI_GPT_5_MINI_PRICING;
261
+ }
262
+ if (modelId.includes("gpt-5.3-codex")) {
263
+ return OPENAI_GPT_53_CODEX_PRICING;
264
+ }
265
+ if (modelId.includes("gpt-5-codex")) {
266
+ return OPENAI_GPT_53_CODEX_PRICING;
267
+ }
192
268
  if (modelId.includes("gpt-5.2")) {
193
269
  return OPENAI_GPT_52_PRICING;
194
270
  }
271
+ if (modelId.includes("gpt-5-mini")) {
272
+ return OPENAI_GPT_5_MINI_PRICING;
273
+ }
195
274
  if (modelId.includes("gpt-5.1-codex-mini")) {
196
- return OPENAI_GPT_51_CODEX_MINI_PRICING;
275
+ return OPENAI_GPT_5_MINI_PRICING;
197
276
  }
198
277
  return void 0;
199
278
  }
@@ -254,6 +333,14 @@ function estimateCallCostUsd({
254
333
  const outputCost = outputTokens * outputRate;
255
334
  return inputCost + cachedCost + outputCost;
256
335
  }
336
+ const fireworksPricing = getFireworksPricing(modelId);
337
+ if (fireworksPricing) {
338
+ const inputCost = nonCachedPrompt * fireworksPricing.inputRate;
339
+ const cachedCost = cachedTokens * fireworksPricing.cachedRate;
340
+ const outputTokens = responseTokens + thinkingTokens;
341
+ const outputCost = outputTokens * fireworksPricing.outputRate;
342
+ return inputCost + cachedCost + outputCost;
343
+ }
257
344
  const openAiPricing = getOpenAiPricing(modelId);
258
345
  if (openAiPricing) {
259
346
  const inputCost = nonCachedPrompt * openAiPricing.inputRate;
@@ -266,11 +353,14 @@ function estimateCallCostUsd({
266
353
  }
267
354
 
268
355
  // src/openai/chatgpt-codex.ts
269
- var import_node_os = __toESM(require("os"), 1);
356
+ var import_node_os2 = __toESM(require("os"), 1);
270
357
  var import_node_util = require("util");
271
358
 
272
359
  // src/openai/chatgpt-auth.ts
273
360
  var import_node_buffer = require("buffer");
361
+ var import_node_fs2 = __toESM(require("fs"), 1);
362
+ var import_node_os = __toESM(require("os"), 1);
363
+ var import_node_path2 = __toESM(require("path"), 1);
274
364
  var import_zod = require("zod");
275
365
 
276
366
  // src/utils/env.ts
@@ -335,34 +425,30 @@ function parseEnvLine(line) {
335
425
  }
336
426
 
337
427
  // src/openai/chatgpt-auth.ts
338
- var CHATGPT_AUTH_JSON_ENV = "CHATGPT_AUTH_JSON";
339
- var CHATGPT_AUTH_JSON_B64_ENV = "CHATGPT_AUTH_JSON_B64";
340
- var CHATGPT_ACCESS_ENV = "CHATGPT_ACCESS";
341
- var CHATGPT_REFRESH_ENV = "CHATGPT_REFRESH";
342
- var CHATGPT_EXPIRES_ENV = "CHATGPT_EXPIRES";
343
- var CHATGPT_ACCOUNT_ID_ENV = "CHATGPT_ACCOUNT_ID";
344
- var CHATGPT_ID_TOKEN_ENV = "CHATGPT_ID_TOKEN";
345
- var CHATGPT_ACCESS_TOKEN_ENV = "CHATGPT_ACCESS_TOKEN";
346
- var CHATGPT_REFRESH_TOKEN_ENV = "CHATGPT_REFRESH_TOKEN";
347
- var CHATGPT_EXPIRES_AT_ENV = "CHATGPT_EXPIRES_AT";
428
+ var CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_URL";
429
+ var CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_STORE";
430
+ var CHATGPT_AUTH_SERVER_URL_ENV = "CHATGPT_AUTH_SERVER_URL";
431
+ var CHATGPT_AUTH_SERVER_STORE_ENV = "CHATGPT_AUTH_SERVER_STORE";
432
+ var CHATGPT_AUTH_API_KEY_ENV = "CHATGPT_AUTH_API_KEY";
433
+ var CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY";
348
434
  var CHATGPT_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
349
435
  var CHATGPT_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
350
436
  var CHATGPT_OAUTH_REDIRECT_URI = "http://localhost:1455/auth/callback";
351
437
  var TOKEN_EXPIRY_BUFFER_MS = 3e4;
352
- var AuthInputSchema = import_zod.z.object({
353
- access: import_zod.z.string().min(1).optional(),
354
- access_token: import_zod.z.string().min(1).optional(),
355
- accessToken: import_zod.z.string().min(1).optional(),
356
- refresh: import_zod.z.string().min(1).optional(),
357
- refresh_token: import_zod.z.string().min(1).optional(),
358
- refreshToken: import_zod.z.string().min(1).optional(),
359
- expires: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]).optional(),
360
- expires_at: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]).optional(),
361
- expiresAt: import_zod.z.union([import_zod.z.number(), import_zod.z.string()]).optional(),
362
- accountId: import_zod.z.string().min(1).optional(),
363
- account_id: import_zod.z.string().min(1).optional(),
364
- id_token: import_zod.z.string().optional(),
365
- idToken: import_zod.z.string().optional()
438
+ var CodexAuthFileSchema = import_zod.z.object({
439
+ OPENAI_API_KEY: import_zod.z.string().nullable().optional(),
440
+ last_refresh: import_zod.z.string().optional(),
441
+ tokens: import_zod.z.object({
442
+ access_token: import_zod.z.string().min(1).optional(),
443
+ refresh_token: import_zod.z.string().min(1).optional(),
444
+ id_token: import_zod.z.string().min(1).optional(),
445
+ account_id: import_zod.z.string().min(1).optional(),
446
+ // Allow a bit of flexibility if the file format changes.
447
+ accessToken: import_zod.z.string().min(1).optional(),
448
+ refreshToken: import_zod.z.string().min(1).optional(),
449
+ idToken: import_zod.z.string().min(1).optional(),
450
+ accountId: import_zod.z.string().min(1).optional()
451
+ }).optional()
366
452
  }).loose();
367
453
  var RefreshResponseSchema = import_zod.z.object({
368
454
  access_token: import_zod.z.string().min(1),
@@ -377,6 +463,44 @@ var ExchangeResponseSchema = import_zod.z.object({
377
463
  });
378
464
  var cachedProfile = null;
379
465
  var refreshPromise = null;
466
+ async function fetchChatGptAuthProfileFromTokenProvider(options) {
467
+ const base = options.baseUrl.replace(/\/+$/u, "");
468
+ const store = options.store?.trim() ? options.store.trim() : "kv";
469
+ const url = new URL(`${base}/v1/token`);
470
+ url.searchParams.set("store", store);
471
+ const response = await fetch(url.toString(), {
472
+ method: "GET",
473
+ headers: {
474
+ Authorization: `Bearer ${options.apiKey}`,
475
+ "x-chatgpt-auth": options.apiKey,
476
+ Accept: "application/json"
477
+ }
478
+ });
479
+ if (!response.ok) {
480
+ const body = await response.text();
481
+ throw new Error(`ChatGPT token provider request failed (${response.status}): ${body}`);
482
+ }
483
+ const payload = await response.json();
484
+ if (!payload || typeof payload !== "object") {
485
+ throw new Error("ChatGPT token provider returned invalid JSON.");
486
+ }
487
+ const accessToken = payload.accessToken ?? payload.access_token;
488
+ const accountId = payload.accountId ?? payload.account_id;
489
+ const expiresAt = payload.expiresAt ?? payload.expires_at;
490
+ if (typeof accessToken !== "string" || accessToken.trim().length === 0) {
491
+ throw new Error("ChatGPT token provider response missing accessToken.");
492
+ }
493
+ if (typeof accountId !== "string" || accountId.trim().length === 0) {
494
+ throw new Error("ChatGPT token provider response missing accountId.");
495
+ }
496
+ const expires = normalizeEpochMillis(expiresAt) ?? Date.now() + 5 * 6e4;
497
+ return {
498
+ access: accessToken,
499
+ refresh: "token_provider",
500
+ expires,
501
+ accountId
502
+ };
503
+ }
380
504
  function encodeChatGptAuthJson(profile) {
381
505
  const payload = {
382
506
  access: profile.access,
@@ -415,7 +539,7 @@ async function exchangeChatGptOauthCode({
415
539
  const payload = ExchangeResponseSchema.parse(await response.json());
416
540
  return profileFromTokenResponse(payload);
417
541
  }
418
- async function refreshChatGptOauthToken(refreshToken) {
542
+ async function refreshChatGptOauthToken(refreshToken, fallback) {
419
543
  const params = new URLSearchParams();
420
544
  params.set("grant_type", "refresh_token");
421
545
  params.set("client_id", CHATGPT_OAUTH_CLIENT_ID);
@@ -432,9 +556,35 @@ async function refreshChatGptOauthToken(refreshToken) {
432
556
  throw new Error(`ChatGPT OAuth refresh failed (${response.status}): ${body}`);
433
557
  }
434
558
  const payload = RefreshResponseSchema.parse(await response.json());
435
- return profileFromTokenResponse(payload);
559
+ return profileFromTokenResponse(payload, fallback);
436
560
  }
437
561
  async function getChatGptAuthProfile() {
562
+ loadLocalEnv();
563
+ const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV] ?? process.env[CHATGPT_AUTH_SERVER_URL_ENV];
564
+ const tokenProviderKey = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV] ?? process.env[CHATGPT_AUTH_API_KEY_ENV];
565
+ if (tokenProviderUrl && tokenProviderUrl.trim().length > 0 && tokenProviderKey && tokenProviderKey.trim().length > 0) {
566
+ if (cachedProfile && !isExpired(cachedProfile)) {
567
+ return cachedProfile;
568
+ }
569
+ if (refreshPromise) {
570
+ return refreshPromise;
571
+ }
572
+ refreshPromise = (async () => {
573
+ try {
574
+ const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV] ?? process.env[CHATGPT_AUTH_SERVER_STORE_ENV];
575
+ const profile = await fetchChatGptAuthProfileFromTokenProvider({
576
+ baseUrl: tokenProviderUrl,
577
+ apiKey: tokenProviderKey,
578
+ store: store ?? void 0
579
+ });
580
+ cachedProfile = profile;
581
+ return profile;
582
+ } finally {
583
+ refreshPromise = null;
584
+ }
585
+ })();
586
+ return refreshPromise;
587
+ }
438
588
  if (cachedProfile && !isExpired(cachedProfile)) {
439
589
  return cachedProfile;
440
590
  }
@@ -443,8 +593,8 @@ async function getChatGptAuthProfile() {
443
593
  }
444
594
  refreshPromise = (async () => {
445
595
  try {
446
- const baseProfile = cachedProfile ?? loadAuthProfileFromEnv();
447
- const profile = isExpired(baseProfile) ? await refreshChatGptOauthToken(baseProfile.refresh) : baseProfile;
596
+ const baseProfile = cachedProfile ?? loadAuthProfileFromCodexStore();
597
+ const profile = isExpired(baseProfile) ? await refreshAndPersistCodexProfile(baseProfile) : baseProfile;
448
598
  cachedProfile = profile;
449
599
  return profile;
450
600
  } finally {
@@ -453,39 +603,111 @@ async function getChatGptAuthProfile() {
453
603
  })();
454
604
  return refreshPromise;
455
605
  }
456
- function profileFromTokenResponse(payload) {
457
- const expires = Date.now() + normalizeNumber(payload.expires_in) * 1e3;
458
- const accountId = extractChatGptAccountId(payload.id_token ?? "") ?? extractChatGptAccountId(payload.access_token);
606
+ function resolveCodexHome() {
607
+ const codexHome = process.env.CODEX_HOME;
608
+ if (codexHome && codexHome.trim().length > 0) {
609
+ return codexHome.trim();
610
+ }
611
+ return import_node_path2.default.join(import_node_os.default.homedir(), ".codex");
612
+ }
613
+ function resolveCodexAuthJsonPath() {
614
+ return import_node_path2.default.join(resolveCodexHome(), "auth.json");
615
+ }
616
+ function loadAuthProfileFromCodexStore() {
617
+ const authPath = resolveCodexAuthJsonPath();
618
+ let raw;
619
+ try {
620
+ raw = import_node_fs2.default.readFileSync(authPath, "utf8");
621
+ } catch {
622
+ throw new Error(
623
+ `ChatGPT auth not configured. Set ${CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV}+${CHATGPT_AUTH_API_KEY_ENV} or login via Codex to create ${authPath}.`
624
+ );
625
+ }
626
+ let parsed;
627
+ try {
628
+ parsed = CodexAuthFileSchema.parse(JSON.parse(raw));
629
+ } catch (e) {
630
+ throw new Error(
631
+ `Failed to parse Codex auth store at ${authPath}. (${e?.message ?? e})`
632
+ );
633
+ }
634
+ const tokens = parsed.tokens;
635
+ if (!tokens) {
636
+ throw new Error(
637
+ `Codex auth store at ${authPath} is missing tokens. Re-login via Codex, or configure ${CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV}.`
638
+ );
639
+ }
640
+ const access = tokens.access_token ?? tokens.accessToken ?? void 0;
641
+ const refresh = tokens.refresh_token ?? tokens.refreshToken ?? void 0;
642
+ const idToken = tokens.id_token ?? tokens.idToken ?? void 0;
643
+ if (!access || !refresh) {
644
+ throw new Error(
645
+ `Codex auth store at ${authPath} is missing access_token/refresh_token. Re-login via Codex, or configure ${CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV}.`
646
+ );
647
+ }
648
+ const expires = extractJwtExpiry(access) ?? extractJwtExpiry(idToken ?? "") ?? Date.now() + 5 * 6e4;
649
+ const accountId = tokens.account_id ?? tokens.accountId ?? extractChatGptAccountId(idToken ?? "") ?? extractChatGptAccountId(access);
459
650
  if (!accountId) {
460
- throw new Error("Failed to extract chatgpt_account_id from access token.");
651
+ throw new Error(`Codex auth store at ${authPath} is missing chatgpt_account_id/account_id.`);
461
652
  }
462
653
  return {
463
- access: payload.access_token,
464
- refresh: payload.refresh_token,
654
+ access,
655
+ refresh,
465
656
  expires,
466
657
  accountId,
467
- idToken: payload.id_token
658
+ idToken: idToken ?? void 0
468
659
  };
469
660
  }
470
- function normalizeAuthProfile(data) {
471
- const access = data.access ?? data.access_token ?? data.accessToken ?? void 0;
472
- const refresh = data.refresh ?? data.refresh_token ?? data.refreshToken ?? void 0;
473
- if (!access || !refresh) {
474
- throw new Error("ChatGPT credentials must include access and refresh.");
661
+ async function refreshAndPersistCodexProfile(baseProfile) {
662
+ const refreshed = await refreshChatGptOauthToken(baseProfile.refresh, {
663
+ accountId: baseProfile.accountId,
664
+ idToken: baseProfile.idToken
665
+ });
666
+ persistCodexTokens(refreshed);
667
+ return refreshed;
668
+ }
669
+ function persistCodexTokens(profile) {
670
+ const authPath = resolveCodexAuthJsonPath();
671
+ const codexHome = import_node_path2.default.dirname(authPath);
672
+ let doc = {};
673
+ try {
674
+ doc = JSON.parse(import_node_fs2.default.readFileSync(authPath, "utf8"));
675
+ } catch {
676
+ doc = {};
475
677
  }
476
- const expiresRaw = data.expires ?? data.expires_at ?? data.expiresAt;
477
- const idToken = data.idToken ?? data.id_token ?? void 0;
478
- const expires = normalizeEpochMillis(expiresRaw) ?? extractJwtExpiry(idToken ?? access) ?? Date.now() + 5 * 6e4;
479
- const accountId = data.accountId ?? data.account_id ?? extractChatGptAccountId(idToken ?? "") ?? extractChatGptAccountId(access);
678
+ if (!doc || typeof doc !== "object") {
679
+ doc = {};
680
+ }
681
+ if (!doc.tokens || typeof doc.tokens !== "object") {
682
+ doc.tokens = {};
683
+ }
684
+ doc.tokens.access_token = profile.access;
685
+ doc.tokens.refresh_token = profile.refresh;
686
+ doc.tokens.account_id = profile.accountId;
687
+ if (profile.idToken) {
688
+ doc.tokens.id_token = profile.idToken;
689
+ }
690
+ doc.last_refresh = (/* @__PURE__ */ new Date()).toISOString();
691
+ import_node_fs2.default.mkdirSync(codexHome, { recursive: true, mode: 448 });
692
+ const tmpPath = `${authPath}.tmp.${process.pid}.${Math.random().toString(16).slice(2)}`;
693
+ import_node_fs2.default.writeFileSync(tmpPath, `${JSON.stringify(doc, null, 2)}
694
+ `, { mode: 384 });
695
+ import_node_fs2.default.renameSync(tmpPath, authPath);
696
+ }
697
+ function profileFromTokenResponse(payload, fallback) {
698
+ const expires = Date.now() + normalizeNumber(payload.expires_in) * 1e3;
699
+ const fallbackAccountId = fallback?.accountId;
700
+ const fallbackIdToken = fallback?.idToken;
701
+ const accountId = extractChatGptAccountId(payload.id_token ?? "") ?? extractChatGptAccountId(payload.access_token) ?? fallbackAccountId;
480
702
  if (!accountId) {
481
- throw new Error("ChatGPT credentials missing chatgpt_account_id.");
703
+ throw new Error("Failed to extract chatgpt_account_id from access token.");
482
704
  }
483
705
  return {
484
- access,
485
- refresh,
706
+ access: payload.access_token,
707
+ refresh: payload.refresh_token,
486
708
  expires,
487
709
  accountId,
488
- idToken: idToken ?? void 0
710
+ idToken: payload.id_token ?? fallbackIdToken
489
711
  };
490
712
  }
491
713
  function normalizeEpochMillis(value) {
@@ -514,31 +736,6 @@ function isExpired(profile) {
514
736
  }
515
737
  return Date.now() + TOKEN_EXPIRY_BUFFER_MS >= expires;
516
738
  }
517
- function loadAuthProfileFromEnv() {
518
- loadLocalEnv();
519
- const rawJson = process.env[CHATGPT_AUTH_JSON_ENV];
520
- if (rawJson && rawJson.trim().length > 0) {
521
- return normalizeAuthProfile(AuthInputSchema.parse(JSON.parse(rawJson)));
522
- }
523
- const rawB64 = process.env[CHATGPT_AUTH_JSON_B64_ENV];
524
- if (rawB64 && rawB64.trim().length > 0) {
525
- const decoded = import_node_buffer.Buffer.from(rawB64.trim(), "base64url").toString("utf8");
526
- return normalizeAuthProfile(AuthInputSchema.parse(JSON.parse(decoded)));
527
- }
528
- const access = process.env[CHATGPT_ACCESS_ENV] ?? process.env[CHATGPT_ACCESS_TOKEN_ENV] ?? void 0;
529
- const refresh = process.env[CHATGPT_REFRESH_ENV] ?? process.env[CHATGPT_REFRESH_TOKEN_ENV] ?? void 0;
530
- const expires = process.env[CHATGPT_EXPIRES_ENV] ?? process.env[CHATGPT_EXPIRES_AT_ENV] ?? void 0;
531
- const accountId = process.env[CHATGPT_ACCOUNT_ID_ENV] ?? void 0;
532
- const idToken = process.env[CHATGPT_ID_TOKEN_ENV] ?? void 0;
533
- const parsed = AuthInputSchema.parse({
534
- access,
535
- refresh,
536
- expires,
537
- accountId,
538
- idToken
539
- });
540
- return normalizeAuthProfile(parsed);
541
- }
542
739
  function decodeJwtPayload(token) {
543
740
  const segments = token.split(".");
544
741
  if (segments.length < 2) {
@@ -569,8 +766,12 @@ function extractChatGptAccountId(token) {
569
766
  if (!payload || typeof payload !== "object") {
570
767
  return void 0;
571
768
  }
572
- const accountId = payload.chatgpt_account_id;
573
- return typeof accountId === "string" && accountId.length > 0 ? accountId : void 0;
769
+ const direct = payload.chatgpt_account_id;
770
+ if (typeof direct === "string" && direct.length > 0) {
771
+ return direct;
772
+ }
773
+ const namespaced = payload["https://api.openai.com/auth"]?.chatgpt_account_id;
774
+ return typeof namespaced === "string" && namespaced.length > 0 ? namespaced : void 0;
574
775
  }
575
776
 
576
777
  // src/openai/chatgpt-codex.ts
@@ -606,7 +807,19 @@ async function streamChatGptCodexResponse(options) {
606
807
  return parseEventStream(body);
607
808
  }
608
809
  async function collectChatGptCodexResponse(options) {
609
- const stream = await streamChatGptCodexResponse(options);
810
+ let stream;
811
+ try {
812
+ stream = await streamChatGptCodexResponse(options);
813
+ } catch (error) {
814
+ if (shouldRetryWithoutReasoningSummary(options.request, error)) {
815
+ stream = await streamChatGptCodexResponse({
816
+ ...options,
817
+ request: removeReasoningSummary(options.request)
818
+ });
819
+ } else {
820
+ throw error;
821
+ }
822
+ }
610
823
  const toolCalls = /* @__PURE__ */ new Map();
611
824
  const toolCallOrder = [];
612
825
  const webSearchCalls = /* @__PURE__ */ new Map();
@@ -615,6 +828,7 @@ async function collectChatGptCodexResponse(options) {
615
828
  const reasoningText = "";
616
829
  let reasoningSummaryText = "";
617
830
  let usage;
831
+ let responseId;
618
832
  let model;
619
833
  let status;
620
834
  let blocked = false;
@@ -655,7 +869,18 @@ async function collectChatGptCodexResponse(options) {
655
869
  if (!toolCalls.has(callId)) {
656
870
  toolCallOrder.push(callId);
657
871
  }
658
- toolCalls.set(callId, { id, callId, name, arguments: args });
872
+ toolCalls.set(callId, { kind: "function", id, callId, name, arguments: args });
873
+ }
874
+ } else if (item.type === "custom_tool_call") {
875
+ const id = typeof item.id === "string" ? item.id : "";
876
+ const callId = typeof item.call_id === "string" ? item.call_id : id;
877
+ const name = typeof item.name === "string" ? item.name : "";
878
+ const input = typeof item.input === "string" ? item.input : "";
879
+ if (callId) {
880
+ if (!toolCalls.has(callId)) {
881
+ toolCallOrder.push(callId);
882
+ }
883
+ toolCalls.set(callId, { kind: "custom", id, callId, name, input });
659
884
  }
660
885
  } else if (item.type === "web_search_call") {
661
886
  const id = typeof item.id === "string" ? item.id : "";
@@ -677,6 +902,7 @@ async function collectChatGptCodexResponse(options) {
677
902
  const response = event.response;
678
903
  if (response) {
679
904
  usage = response.usage;
905
+ responseId = typeof response.id === "string" ? response.id : responseId;
680
906
  model = typeof response.model === "string" ? response.model : void 0;
681
907
  status = typeof response.status === "string" ? response.status : void 0;
682
908
  }
@@ -686,6 +912,7 @@ async function collectChatGptCodexResponse(options) {
686
912
  const response = event.response;
687
913
  if (response) {
688
914
  usage = response.usage;
915
+ responseId = typeof response.id === "string" ? response.id : responseId;
689
916
  model = typeof response.model === "string" ? response.model : void 0;
690
917
  status = typeof response.status === "string" ? response.status : void 0;
691
918
  }
@@ -695,6 +922,7 @@ async function collectChatGptCodexResponse(options) {
695
922
  const response = event.response;
696
923
  if (response) {
697
924
  usage = response.usage;
925
+ responseId = typeof response.id === "string" ? response.id : responseId;
698
926
  model = typeof response.model === "string" ? response.model : void 0;
699
927
  status = typeof response.status === "string" ? response.status : void 0;
700
928
  }
@@ -712,15 +940,38 @@ async function collectChatGptCodexResponse(options) {
712
940
  toolCalls: orderedToolCalls,
713
941
  webSearchCalls: orderedWebSearchCalls,
714
942
  usage,
943
+ id: responseId,
715
944
  model,
716
945
  status,
717
946
  blocked
718
947
  };
719
948
  }
949
+ function shouldRetryWithoutReasoningSummary(request, error) {
950
+ if (!request.reasoning?.summary) {
951
+ return false;
952
+ }
953
+ if (!(error instanceof Error)) {
954
+ return false;
955
+ }
956
+ const message = error.message.toLowerCase();
957
+ return message.includes("unsupported parameter") && message.includes("reasoning.summary");
958
+ }
959
+ function removeReasoningSummary(request) {
960
+ const reasoning = request.reasoning;
961
+ if (!reasoning?.summary) {
962
+ return request;
963
+ }
964
+ return {
965
+ ...request,
966
+ reasoning: {
967
+ effort: reasoning.effort
968
+ }
969
+ };
970
+ }
720
971
  function buildUserAgent() {
721
972
  const node = process.version;
722
- const platform = import_node_os.default.platform();
723
- const release = import_node_os.default.release();
973
+ const platform = import_node_os2.default.platform();
974
+ const release = import_node_os2.default.release();
724
975
  return `@ljoukov/llm (node ${node}; ${platform} ${release})`;
725
976
  }
726
977
  async function* parseEventStream(stream) {
@@ -868,6 +1119,110 @@ function createCallScheduler(options = {}) {
868
1119
  return { run };
869
1120
  }
870
1121
 
1122
+ // src/fireworks/client.ts
1123
+ var import_openai = __toESM(require("openai"), 1);
1124
+ var import_undici = require("undici");
1125
+ var DEFAULT_FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1";
1126
+ var DEFAULT_FIREWORKS_TIMEOUT_MS = 15 * 6e4;
1127
+ var cachedClient = null;
1128
+ var cachedFetch = null;
1129
+ var cachedBaseUrl = null;
1130
+ var cachedApiKey = null;
1131
+ var cachedTimeoutMs = null;
1132
+ function resolveTimeoutMs() {
1133
+ if (cachedTimeoutMs !== null) {
1134
+ return cachedTimeoutMs;
1135
+ }
1136
+ const raw = process.env.FIREWORKS_TIMEOUT_MS;
1137
+ const parsed = raw ? Number(raw) : Number.NaN;
1138
+ cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_FIREWORKS_TIMEOUT_MS;
1139
+ return cachedTimeoutMs;
1140
+ }
1141
+ function resolveBaseUrl() {
1142
+ if (cachedBaseUrl !== null) {
1143
+ return cachedBaseUrl;
1144
+ }
1145
+ loadLocalEnv();
1146
+ const raw = process.env.FIREWORKS_BASE_URL?.trim();
1147
+ cachedBaseUrl = raw && raw.length > 0 ? raw : DEFAULT_FIREWORKS_BASE_URL;
1148
+ return cachedBaseUrl;
1149
+ }
1150
+ function resolveApiKey() {
1151
+ if (cachedApiKey !== null) {
1152
+ return cachedApiKey;
1153
+ }
1154
+ loadLocalEnv();
1155
+ const raw = process.env.FIREWORKS_TOKEN ?? process.env.FIREWORKS_API_KEY;
1156
+ const token = raw?.trim();
1157
+ if (!token) {
1158
+ throw new Error(
1159
+ "FIREWORKS_TOKEN (or FIREWORKS_API_KEY) must be provided to access Fireworks APIs."
1160
+ );
1161
+ }
1162
+ cachedApiKey = token;
1163
+ return cachedApiKey;
1164
+ }
1165
+ function getFireworksFetch() {
1166
+ if (cachedFetch) {
1167
+ return cachedFetch;
1168
+ }
1169
+ const timeoutMs = resolveTimeoutMs();
1170
+ const dispatcher = new import_undici.Agent({
1171
+ bodyTimeout: timeoutMs,
1172
+ headersTimeout: timeoutMs
1173
+ });
1174
+ cachedFetch = ((input, init) => {
1175
+ return (0, import_undici.fetch)(input, {
1176
+ ...init ?? {},
1177
+ dispatcher
1178
+ });
1179
+ });
1180
+ return cachedFetch;
1181
+ }
1182
+ function getFireworksClient() {
1183
+ if (cachedClient) {
1184
+ return cachedClient;
1185
+ }
1186
+ cachedClient = new import_openai.default({
1187
+ apiKey: resolveApiKey(),
1188
+ baseURL: resolveBaseUrl(),
1189
+ timeout: resolveTimeoutMs(),
1190
+ fetch: getFireworksFetch()
1191
+ });
1192
+ return cachedClient;
1193
+ }
1194
+
1195
+ // src/fireworks/calls.ts
1196
+ var scheduler = createCallScheduler({
1197
+ maxParallelRequests: 3,
1198
+ minIntervalBetweenStartMs: 200,
1199
+ startJitterMs: 200
1200
+ });
1201
+ async function runFireworksCall(fn) {
1202
+ return scheduler.run(async () => fn(getFireworksClient()));
1203
+ }
1204
+
1205
+ // src/fireworks/models.ts
1206
+ var FIREWORKS_MODEL_IDS = ["kimi-k2.5", "glm-5", "minimax-m2.1"];
1207
+ var FIREWORKS_DEFAULT_KIMI_MODEL = "kimi-k2.5";
1208
+ var FIREWORKS_DEFAULT_GLM_MODEL = "glm-5";
1209
+ var FIREWORKS_DEFAULT_MINIMAX_MODEL = "minimax-m2.1";
1210
+ var FIREWORKS_CANONICAL_MODEL_IDS = {
1211
+ "kimi-k2.5": "accounts/fireworks/models/kimi-k2p5",
1212
+ "glm-5": "accounts/fireworks/models/glm-5",
1213
+ "minimax-m2.1": "accounts/fireworks/models/minimax-m2p1"
1214
+ };
1215
+ function isFireworksModelId(value) {
1216
+ return FIREWORKS_MODEL_IDS.includes(value.trim());
1217
+ }
1218
+ function resolveFireworksModelId(model) {
1219
+ const trimmed = model.trim();
1220
+ if (!isFireworksModelId(trimmed)) {
1221
+ return void 0;
1222
+ }
1223
+ return FIREWORKS_CANONICAL_MODEL_IDS[trimmed];
1224
+ }
1225
+
871
1226
  // src/google/client.ts
872
1227
  var import_genai = require("@google/genai");
873
1228
 
@@ -936,6 +1291,7 @@ function getGoogleAuthOptions(scopes) {
936
1291
  // src/google/client.ts
937
1292
  var GEMINI_MODEL_IDS = [
938
1293
  "gemini-3-pro-preview",
1294
+ "gemini-3-flash-preview",
939
1295
  "gemini-2.5-pro",
940
1296
  "gemini-flash-latest",
941
1297
  "gemini-flash-lite-latest"
@@ -1175,7 +1531,7 @@ function retryDelayMs(attempt) {
1175
1531
  const jitter = Math.floor(Math.random() * 200);
1176
1532
  return base + jitter;
1177
1533
  }
1178
- var scheduler = createCallScheduler({
1534
+ var scheduler2 = createCallScheduler({
1179
1535
  maxParallelRequests: 3,
1180
1536
  minIntervalBetweenStartMs: 200,
1181
1537
  startJitterMs: 200,
@@ -1191,46 +1547,46 @@ var scheduler = createCallScheduler({
1191
1547
  }
1192
1548
  });
1193
1549
  async function runGeminiCall(fn) {
1194
- return scheduler.run(async () => fn(await getGeminiClient()));
1550
+ return scheduler2.run(async () => fn(await getGeminiClient()));
1195
1551
  }
1196
1552
 
1197
1553
  // src/openai/client.ts
1198
- var import_openai = __toESM(require("openai"), 1);
1199
- var import_undici = require("undici");
1200
- var cachedApiKey = null;
1201
- var cachedClient = null;
1202
- var cachedFetch = null;
1203
- var cachedTimeoutMs = null;
1554
+ var import_openai2 = __toESM(require("openai"), 1);
1555
+ var import_undici2 = require("undici");
1556
+ var cachedApiKey2 = null;
1557
+ var cachedClient2 = null;
1558
+ var cachedFetch2 = null;
1559
+ var cachedTimeoutMs2 = null;
1204
1560
  var DEFAULT_OPENAI_TIMEOUT_MS = 15 * 6e4;
1205
1561
  function resolveOpenAiTimeoutMs() {
1206
- if (cachedTimeoutMs !== null) {
1207
- return cachedTimeoutMs;
1562
+ if (cachedTimeoutMs2 !== null) {
1563
+ return cachedTimeoutMs2;
1208
1564
  }
1209
1565
  const raw = process.env.OPENAI_STREAM_TIMEOUT_MS ?? process.env.OPENAI_TIMEOUT_MS;
1210
1566
  const parsed = raw ? Number(raw) : Number.NaN;
1211
- cachedTimeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_OPENAI_TIMEOUT_MS;
1212
- return cachedTimeoutMs;
1567
+ cachedTimeoutMs2 = Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_OPENAI_TIMEOUT_MS;
1568
+ return cachedTimeoutMs2;
1213
1569
  }
1214
1570
  function getOpenAiFetch() {
1215
- if (cachedFetch) {
1216
- return cachedFetch;
1571
+ if (cachedFetch2) {
1572
+ return cachedFetch2;
1217
1573
  }
1218
1574
  const timeoutMs = resolveOpenAiTimeoutMs();
1219
- const dispatcher = new import_undici.Agent({
1575
+ const dispatcher = new import_undici2.Agent({
1220
1576
  bodyTimeout: timeoutMs,
1221
1577
  headersTimeout: timeoutMs
1222
1578
  });
1223
- cachedFetch = ((input, init) => {
1224
- return (0, import_undici.fetch)(input, {
1579
+ cachedFetch2 = ((input, init) => {
1580
+ return (0, import_undici2.fetch)(input, {
1225
1581
  ...init ?? {},
1226
1582
  dispatcher
1227
1583
  });
1228
1584
  });
1229
- return cachedFetch;
1585
+ return cachedFetch2;
1230
1586
  }
1231
1587
  function getOpenAiApiKey() {
1232
- if (cachedApiKey !== null) {
1233
- return cachedApiKey;
1588
+ if (cachedApiKey2 !== null) {
1589
+ return cachedApiKey2;
1234
1590
  }
1235
1591
  loadLocalEnv();
1236
1592
  const raw = process.env.OPENAI_API_KEY;
@@ -1238,32 +1594,32 @@ function getOpenAiApiKey() {
1238
1594
  if (!value) {
1239
1595
  throw new Error("OPENAI_API_KEY must be provided to access OpenAI APIs.");
1240
1596
  }
1241
- cachedApiKey = value;
1242
- return cachedApiKey;
1597
+ cachedApiKey2 = value;
1598
+ return cachedApiKey2;
1243
1599
  }
1244
1600
  function getOpenAiClient() {
1245
- if (cachedClient) {
1246
- return cachedClient;
1601
+ if (cachedClient2) {
1602
+ return cachedClient2;
1247
1603
  }
1248
1604
  const apiKey = getOpenAiApiKey();
1249
1605
  const timeoutMs = resolveOpenAiTimeoutMs();
1250
- cachedClient = new import_openai.default({
1606
+ cachedClient2 = new import_openai2.default({
1251
1607
  apiKey,
1252
1608
  fetch: getOpenAiFetch(),
1253
1609
  timeout: timeoutMs
1254
1610
  });
1255
- return cachedClient;
1611
+ return cachedClient2;
1256
1612
  }
1257
1613
 
1258
1614
  // src/openai/calls.ts
1259
1615
  var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
1260
- var scheduler2 = createCallScheduler({
1616
+ var scheduler3 = createCallScheduler({
1261
1617
  maxParallelRequests: 3,
1262
1618
  minIntervalBetweenStartMs: 200,
1263
1619
  startJitterMs: 200
1264
1620
  });
1265
1621
  async function runOpenAiCall(fn) {
1266
- return scheduler2.run(async () => fn(getOpenAiClient()));
1622
+ return scheduler3.run(async () => fn(getOpenAiClient()));
1267
1623
  }
1268
1624
 
1269
1625
  // src/llm.ts
@@ -1279,7 +1635,16 @@ var LlmJsonCallError = class extends Error {
1279
1635
  }
1280
1636
  };
1281
1637
  function tool(options) {
1282
- return options;
1638
+ return {
1639
+ type: "function",
1640
+ ...options
1641
+ };
1642
+ }
1643
+ function customTool(options) {
1644
+ return {
1645
+ type: "custom",
1646
+ ...options
1647
+ };
1283
1648
  }
1284
1649
  function isPlainRecord(value) {
1285
1650
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1625,6 +1990,10 @@ function resolveProvider(model) {
1625
1990
  if (model.startsWith("gemini-")) {
1626
1991
  return { provider: "gemini", model };
1627
1992
  }
1993
+ const fireworksModel = resolveFireworksModelId(model);
1994
+ if (fireworksModel) {
1995
+ return { provider: "fireworks", model: fireworksModel };
1996
+ }
1628
1997
  return { provider: "openai", model };
1629
1998
  }
1630
1999
  function isOpenAiCodexModel(modelId) {
@@ -1654,6 +2023,27 @@ function toOpenAiReasoningEffort(effort) {
1654
2023
  function resolveOpenAiVerbosity(modelId) {
1655
2024
  return isOpenAiCodexModel(modelId) ? "medium" : "high";
1656
2025
  }
2026
+ function isRetryableChatGptTransportError(error) {
2027
+ if (!(error instanceof Error)) {
2028
+ return false;
2029
+ }
2030
+ const message = error.message.toLowerCase();
2031
+ return message === "terminated" || message.includes("socket hang up") || message.includes("fetch failed") || message.includes("network");
2032
+ }
2033
+ async function collectChatGptCodexResponseWithRetry(options, maxAttempts = 2) {
2034
+ let attempt = 1;
2035
+ while (true) {
2036
+ try {
2037
+ return await collectChatGptCodexResponse(options);
2038
+ } catch (error) {
2039
+ if (attempt >= maxAttempts || !isRetryableChatGptTransportError(error)) {
2040
+ throw error;
2041
+ }
2042
+ await new Promise((resolve) => setTimeout(resolve, 250 * attempt));
2043
+ attempt += 1;
2044
+ }
2045
+ }
2046
+ }
1657
2047
  function isInlineImageMime(mimeType) {
1658
2048
  if (!mimeType) {
1659
2049
  return false;
@@ -2236,6 +2626,53 @@ function toChatGptInput(contents) {
2236
2626
  input
2237
2627
  };
2238
2628
  }
2629
+ function toFireworksMessages(contents, options) {
2630
+ const systemMessages = [];
2631
+ const messages = [];
2632
+ if (options?.responseMimeType === "application/json") {
2633
+ systemMessages.push("Return valid JSON only. Do not include markdown or prose outside JSON.");
2634
+ }
2635
+ if (options?.responseJsonSchema) {
2636
+ systemMessages.push(`Target JSON schema:
2637
+ ${JSON.stringify(options.responseJsonSchema)}`);
2638
+ }
2639
+ for (const content of contents) {
2640
+ const text = content.parts.map((part) => {
2641
+ if (part.type === "text") {
2642
+ return part.text;
2643
+ }
2644
+ const mimeType = part.mimeType ?? "application/octet-stream";
2645
+ if (isInlineImageMime(mimeType)) {
2646
+ return `[image:${mimeType}]`;
2647
+ }
2648
+ return `[file:${mimeType}]`;
2649
+ }).join("\n").trim();
2650
+ if (content.role === "system" || content.role === "developer") {
2651
+ if (text.length > 0) {
2652
+ systemMessages.push(text);
2653
+ }
2654
+ continue;
2655
+ }
2656
+ if (content.role === "tool" || content.role === "assistant") {
2657
+ messages.push({
2658
+ role: "assistant",
2659
+ content: text.length > 0 ? text : "(empty content)"
2660
+ });
2661
+ continue;
2662
+ }
2663
+ messages.push({
2664
+ role: "user",
2665
+ content: text.length > 0 ? text : "(empty content)"
2666
+ });
2667
+ }
2668
+ if (systemMessages.length > 0) {
2669
+ messages.unshift({
2670
+ role: "system",
2671
+ content: systemMessages.join("\n\n")
2672
+ });
2673
+ }
2674
+ return messages;
2675
+ }
2239
2676
  function toGeminiTools(tools) {
2240
2677
  if (!tools || tools.length === 0) {
2241
2678
  return void 0;
@@ -2409,21 +2846,56 @@ function extractChatGptUsageTokens(usage) {
2409
2846
  totalTokens
2410
2847
  };
2411
2848
  }
2412
- var MODERATION_FINISH_REASONS = /* @__PURE__ */ new Set([
2413
- import_genai2.FinishReason.SAFETY,
2414
- import_genai2.FinishReason.BLOCKLIST,
2415
- import_genai2.FinishReason.PROHIBITED_CONTENT,
2416
- import_genai2.FinishReason.SPII
2417
- ]);
2418
- function isModerationFinish(reason) {
2419
- if (!reason) {
2420
- return false;
2849
+ function extractFireworksUsageTokens(usage) {
2850
+ if (!usage || typeof usage !== "object") {
2851
+ return void 0;
2421
2852
  }
2422
- return MODERATION_FINISH_REASONS.has(reason);
2423
- }
2424
- function mergeToolOutput(value) {
2425
- if (typeof value === "string") {
2426
- return value;
2853
+ const promptTokens = toMaybeNumber(
2854
+ usage.prompt_tokens ?? usage.input_tokens
2855
+ );
2856
+ const cachedTokens = toMaybeNumber(
2857
+ usage.prompt_tokens_details?.cached_tokens ?? usage.input_tokens_details?.cached_tokens
2858
+ );
2859
+ const outputTokensRaw = toMaybeNumber(
2860
+ usage.completion_tokens ?? usage.output_tokens
2861
+ );
2862
+ const reasoningTokens = toMaybeNumber(
2863
+ usage.completion_tokens_details?.reasoning_tokens ?? usage.output_tokens_details?.reasoning_tokens
2864
+ );
2865
+ const totalTokens = toMaybeNumber(
2866
+ usage.total_tokens ?? usage.totalTokenCount
2867
+ );
2868
+ let responseTokens;
2869
+ if (outputTokensRaw !== void 0) {
2870
+ const adjusted = outputTokensRaw - (reasoningTokens ?? 0);
2871
+ responseTokens = adjusted >= 0 ? adjusted : 0;
2872
+ }
2873
+ if (promptTokens === void 0 && cachedTokens === void 0 && responseTokens === void 0 && reasoningTokens === void 0 && totalTokens === void 0) {
2874
+ return void 0;
2875
+ }
2876
+ return {
2877
+ promptTokens,
2878
+ cachedTokens,
2879
+ responseTokens,
2880
+ thinkingTokens: reasoningTokens,
2881
+ totalTokens
2882
+ };
2883
+ }
2884
+ var MODERATION_FINISH_REASONS = /* @__PURE__ */ new Set([
2885
+ import_genai2.FinishReason.SAFETY,
2886
+ import_genai2.FinishReason.BLOCKLIST,
2887
+ import_genai2.FinishReason.PROHIBITED_CONTENT,
2888
+ import_genai2.FinishReason.SPII
2889
+ ]);
2890
+ function isModerationFinish(reason) {
2891
+ if (!reason) {
2892
+ return false;
2893
+ }
2894
+ return MODERATION_FINISH_REASONS.has(reason);
2895
+ }
2896
+ function mergeToolOutput(value) {
2897
+ if (typeof value === "string") {
2898
+ return value;
2427
2899
  }
2428
2900
  try {
2429
2901
  return JSON.stringify(value);
@@ -2447,8 +2919,8 @@ function parseOpenAiToolArguments(raw) {
2447
2919
  function formatZodIssues(issues) {
2448
2920
  const messages = [];
2449
2921
  for (const issue of issues) {
2450
- const path2 = issue.path.length > 0 ? issue.path.map(String).join(".") : "input";
2451
- messages.push(`${path2}: ${issue.message}`);
2922
+ const path6 = issue.path.length > 0 ? issue.path.map(String).join(".") : "input";
2923
+ messages.push(`${path6}: ${issue.message}`);
2452
2924
  }
2453
2925
  return messages.join("; ");
2454
2926
  }
@@ -2464,7 +2936,7 @@ function buildToolErrorOutput(message, issues) {
2464
2936
  return output;
2465
2937
  }
2466
2938
  async function executeToolCall(params) {
2467
- const { toolName, tool: tool2, rawInput, parseError } = params;
2939
+ const { callKind, toolName, tool: tool2, rawInput, parseError } = params;
2468
2940
  if (!tool2) {
2469
2941
  const message = `Unknown tool: ${toolName}`;
2470
2942
  return {
@@ -2472,6 +2944,39 @@ async function executeToolCall(params) {
2472
2944
  outputPayload: buildToolErrorOutput(message)
2473
2945
  };
2474
2946
  }
2947
+ if (callKind === "custom") {
2948
+ if (!isCustomTool(tool2)) {
2949
+ const message = `Tool ${toolName} was called as custom_tool_call but is declared as function.`;
2950
+ const outputPayload = buildToolErrorOutput(message);
2951
+ return {
2952
+ result: { toolName, input: rawInput, output: outputPayload, error: message },
2953
+ outputPayload
2954
+ };
2955
+ }
2956
+ const input = typeof rawInput === "string" ? rawInput : String(rawInput ?? "");
2957
+ try {
2958
+ const output = await tool2.execute(input);
2959
+ return {
2960
+ result: { toolName, input, output },
2961
+ outputPayload: output
2962
+ };
2963
+ } catch (error) {
2964
+ const message = error instanceof Error ? error.message : String(error);
2965
+ const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
2966
+ return {
2967
+ result: { toolName, input, output: outputPayload, error: message },
2968
+ outputPayload
2969
+ };
2970
+ }
2971
+ }
2972
+ if (isCustomTool(tool2)) {
2973
+ const message = `Tool ${toolName} was called as function_call but is declared as custom.`;
2974
+ const outputPayload = buildToolErrorOutput(message);
2975
+ return {
2976
+ result: { toolName, input: rawInput, output: outputPayload, error: message },
2977
+ outputPayload
2978
+ };
2979
+ }
2475
2980
  if (parseError) {
2476
2981
  const message = `Invalid JSON for tool ${toolName}: ${parseError}`;
2477
2982
  return {
@@ -2528,8 +3033,12 @@ function normalizeChatGptToolIds(params) {
2528
3033
  rawItemId = nextItemId ?? rawItemId;
2529
3034
  }
2530
3035
  const callValue = sanitizeChatGptToolId(rawCallId || rawItemId || (0, import_node_crypto.randomBytes)(8).toString("hex"));
2531
- let itemValue = sanitizeChatGptToolId(rawItemId || `fc-${callValue}`);
2532
- if (!itemValue.startsWith("fc")) {
3036
+ let itemValue = sanitizeChatGptToolId(rawItemId || callValue);
3037
+ if (params.callKind === "custom") {
3038
+ if (!itemValue.startsWith("ctc")) {
3039
+ itemValue = `ctc_${itemValue}`;
3040
+ }
3041
+ } else if (!itemValue.startsWith("fc")) {
2533
3042
  itemValue = `fc-${itemValue}`;
2534
3043
  }
2535
3044
  return { callId: callValue, itemId: itemValue };
@@ -2594,7 +3103,7 @@ function extractOpenAiResponseParts(response) {
2594
3103
  }
2595
3104
  return { parts, blocked };
2596
3105
  }
2597
- function extractOpenAiFunctionCalls(output) {
3106
+ function extractOpenAiToolCalls(output) {
2598
3107
  const calls = [];
2599
3108
  if (!Array.isArray(output)) {
2600
3109
  return calls;
@@ -2603,22 +3112,78 @@ function extractOpenAiFunctionCalls(output) {
2603
3112
  if (!item || typeof item !== "object") {
2604
3113
  continue;
2605
3114
  }
2606
- if (item.type === "function_call") {
3115
+ const itemType = item.type;
3116
+ if (itemType === "function_call") {
2607
3117
  const name = typeof item.name === "string" ? item.name : "";
2608
3118
  const args = typeof item.arguments === "string" ? item.arguments : "";
2609
3119
  const call_id = typeof item.call_id === "string" ? item.call_id : "";
2610
3120
  const id = typeof item.id === "string" ? item.id : void 0;
2611
3121
  if (name && call_id) {
2612
- calls.push({ name, arguments: args, call_id, id });
3122
+ calls.push({ kind: "function", name, arguments: args, call_id, id });
3123
+ }
3124
+ continue;
3125
+ }
3126
+ if (itemType === "custom_tool_call") {
3127
+ const name = typeof item.name === "string" ? item.name : "";
3128
+ const input = typeof item.input === "string" ? item.input : "";
3129
+ const call_id = typeof item.call_id === "string" ? item.call_id : "";
3130
+ const id = typeof item.id === "string" ? item.id : void 0;
3131
+ if (name && call_id) {
3132
+ calls.push({ kind: "custom", name, input, call_id, id });
2613
3133
  }
2614
3134
  }
2615
3135
  }
2616
3136
  return calls;
2617
3137
  }
3138
+ function extractFireworksMessageText(message) {
3139
+ if (!message || typeof message !== "object") {
3140
+ return "";
3141
+ }
3142
+ const content = message.content;
3143
+ if (typeof content === "string") {
3144
+ return content;
3145
+ }
3146
+ if (!Array.isArray(content)) {
3147
+ return "";
3148
+ }
3149
+ let text = "";
3150
+ for (const part of content) {
3151
+ const textPart = part.text;
3152
+ if (typeof textPart === "string") {
3153
+ text += textPart;
3154
+ }
3155
+ }
3156
+ return text;
3157
+ }
3158
+ function extractFireworksToolCalls(message) {
3159
+ if (!message || typeof message !== "object") {
3160
+ return [];
3161
+ }
3162
+ const toolCalls = message.tool_calls;
3163
+ if (!Array.isArray(toolCalls)) {
3164
+ return [];
3165
+ }
3166
+ const calls = [];
3167
+ for (const call of toolCalls) {
3168
+ if (!call || typeof call !== "object") {
3169
+ continue;
3170
+ }
3171
+ const id = typeof call.id === "string" ? call.id : "";
3172
+ const fn = call.function;
3173
+ const name = fn && typeof fn === "object" && typeof fn.name === "string" ? fn.name ?? "" : "";
3174
+ const args = fn && typeof fn === "object" && typeof fn.arguments === "string" ? fn.arguments ?? "" : "";
3175
+ if (id && name) {
3176
+ calls.push({ id, name, arguments: args });
3177
+ }
3178
+ }
3179
+ return calls;
3180
+ }
2618
3181
  function resolveGeminiThinkingConfig(modelId) {
2619
3182
  switch (modelId) {
2620
3183
  case "gemini-3-pro-preview":
2621
3184
  return { includeThoughts: true };
3185
+ case "gemini-3-flash-preview":
3186
+ return { includeThoughts: true, thinkingBudget: 16384 };
2622
3187
  case "gemini-2.5-pro":
2623
3188
  return { includeThoughts: true, thinkingBudget: 32768 };
2624
3189
  case "gemini-flash-latest":
@@ -2794,7 +3359,7 @@ async function runTextCall(params) {
2794
3359
  };
2795
3360
  let sawResponseDelta = false;
2796
3361
  let sawThoughtDelta = false;
2797
- const result = await collectChatGptCodexResponse({
3362
+ const result = await collectChatGptCodexResponseWithRetry({
2798
3363
  request: requestPayload,
2799
3364
  signal,
2800
3365
  onDelta: (delta) => {
@@ -2825,6 +3390,47 @@ async function runTextCall(params) {
2825
3390
  if (!sawResponseDelta && fallbackText.length > 0) {
2826
3391
  pushDelta("response", fallbackText);
2827
3392
  }
3393
+ } else if (provider === "fireworks") {
3394
+ if (request.tools && request.tools.length > 0) {
3395
+ throw new Error(
3396
+ "Fireworks provider does not support provider-native tools in generateText; use runToolLoop for function tools."
3397
+ );
3398
+ }
3399
+ const fireworksMessages = toFireworksMessages(contents, {
3400
+ responseMimeType: request.responseMimeType,
3401
+ responseJsonSchema: request.responseJsonSchema
3402
+ });
3403
+ await runFireworksCall(async (client) => {
3404
+ const responseFormat = request.responseJsonSchema ? {
3405
+ type: "json_schema",
3406
+ json_schema: {
3407
+ name: "llm-response",
3408
+ schema: request.responseJsonSchema
3409
+ }
3410
+ } : request.responseMimeType === "application/json" ? { type: "json_object" } : void 0;
3411
+ const response = await client.chat.completions.create(
3412
+ {
3413
+ model: modelForProvider,
3414
+ messages: fireworksMessages,
3415
+ ...responseFormat ? { response_format: responseFormat } : {}
3416
+ },
3417
+ { signal }
3418
+ );
3419
+ modelVersion = typeof response.model === "string" ? response.model : request.model;
3420
+ queue.push({ type: "model", modelVersion });
3421
+ const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
3422
+ if (choice?.finish_reason === "content_filter") {
3423
+ blocked = true;
3424
+ queue.push({ type: "blocked" });
3425
+ }
3426
+ const textOutput = extractFireworksMessageText(
3427
+ choice?.message
3428
+ );
3429
+ if (textOutput.length > 0) {
3430
+ pushDelta("response", textOutput);
3431
+ }
3432
+ latestUsage = extractFireworksUsageTokens(response.usage);
3433
+ });
2828
3434
  } else {
2829
3435
  const geminiContents = contents.map(convertLlmContentToGeminiContent);
2830
3436
  const config = {
@@ -2949,11 +3555,12 @@ function buildJsonSchemaConfig(request) {
2949
3555
  const schemaName = (request.openAiSchemaName ?? "llm-response").trim() || "llm-response";
2950
3556
  const providerInfo = resolveProvider(request.model);
2951
3557
  const isOpenAiVariant = providerInfo.provider === "openai" || providerInfo.provider === "chatgpt";
3558
+ const isGeminiVariant = providerInfo.provider === "gemini";
2952
3559
  const baseJsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(request.schema, {
2953
3560
  name: schemaName,
2954
3561
  target: isOpenAiVariant ? "openAi" : "jsonSchema7"
2955
3562
  });
2956
- const responseJsonSchema = isOpenAiVariant ? resolveOpenAiSchemaRoot(baseJsonSchema) : addGeminiPropertyOrdering(baseJsonSchema);
3563
+ const responseJsonSchema = isOpenAiVariant ? resolveOpenAiSchemaRoot(baseJsonSchema) : isGeminiVariant ? addGeminiPropertyOrdering(baseJsonSchema) : resolveOpenAiSchemaRoot(baseJsonSchema);
2957
3564
  if (isOpenAiVariant && !isJsonSchemaObject(responseJsonSchema)) {
2958
3565
  throw new Error("OpenAI structured outputs require a JSON object schema at the root.");
2959
3566
  }
@@ -3120,15 +3727,46 @@ var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
3120
3727
  function resolveToolLoopContents(input) {
3121
3728
  return resolveTextContents(input);
3122
3729
  }
3123
- function buildOpenAiFunctionTools(tools) {
3730
+ function isCustomTool(toolDef) {
3731
+ return toolDef.type === "custom";
3732
+ }
3733
+ function buildOpenAiToolsFromToolSet(tools) {
3124
3734
  const toolEntries = Object.entries(tools);
3125
- return toolEntries.map(([name, toolDef]) => ({
3126
- type: "function",
3127
- name,
3128
- description: toolDef.description ?? void 0,
3129
- parameters: buildOpenAiToolSchema(toolDef.inputSchema, name),
3130
- strict: true
3131
- }));
3735
+ return toolEntries.map(([name, toolDef]) => {
3736
+ if (isCustomTool(toolDef)) {
3737
+ return {
3738
+ type: "custom",
3739
+ name,
3740
+ description: toolDef.description ?? void 0,
3741
+ ...toolDef.format ? { format: toolDef.format } : {}
3742
+ };
3743
+ }
3744
+ return {
3745
+ type: "function",
3746
+ name,
3747
+ description: toolDef.description ?? void 0,
3748
+ parameters: buildOpenAiToolSchema(toolDef.inputSchema, name),
3749
+ strict: true
3750
+ };
3751
+ });
3752
+ }
3753
+ function buildFireworksToolsFromToolSet(tools) {
3754
+ const toolEntries = Object.entries(tools);
3755
+ return toolEntries.map(([name, toolDef]) => {
3756
+ if (isCustomTool(toolDef)) {
3757
+ throw new Error(
3758
+ `Fireworks provider does not support custom/freeform tools (${name}). Use JSON function tools instead.`
3759
+ );
3760
+ }
3761
+ return {
3762
+ type: "function",
3763
+ function: {
3764
+ name,
3765
+ description: toolDef.description ?? void 0,
3766
+ parameters: buildOpenAiToolSchema(toolDef.inputSchema, name)
3767
+ }
3768
+ };
3769
+ });
3132
3770
  }
3133
3771
  function buildOpenAiToolSchema(schema, name) {
3134
3772
  const rawSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(schema, { name, target: "openAi" });
@@ -3140,11 +3778,18 @@ function buildOpenAiToolSchema(schema, name) {
3140
3778
  }
3141
3779
  function buildGeminiFunctionDeclarations(tools) {
3142
3780
  const toolEntries = Object.entries(tools);
3143
- const functionDeclarations = toolEntries.map(([name, toolDef]) => ({
3144
- name,
3145
- description: toolDef.description ?? "",
3146
- parametersJsonSchema: buildGeminiToolSchema(toolDef.inputSchema, name)
3147
- }));
3781
+ const functionDeclarations = toolEntries.map(([name, toolDef]) => {
3782
+ if (isCustomTool(toolDef)) {
3783
+ throw new Error(
3784
+ `Gemini provider does not support custom/freeform tools (${name}). Use JSON function tools instead.`
3785
+ );
3786
+ }
3787
+ return {
3788
+ name,
3789
+ description: toolDef.description ?? "",
3790
+ parametersJsonSchema: buildGeminiToolSchema(toolDef.inputSchema, name)
3791
+ };
3792
+ });
3148
3793
  return [{ functionDeclarations }];
3149
3794
  }
3150
3795
  function buildGeminiToolSchema(schema, name) {
@@ -3205,9 +3850,9 @@ async function runToolLoop(request) {
3205
3850
  let finalText = "";
3206
3851
  let finalThoughts = "";
3207
3852
  if (providerInfo.provider === "openai") {
3208
- const openAiFunctionTools = buildOpenAiFunctionTools(request.tools);
3853
+ const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
3209
3854
  const openAiNativeTools = toOpenAiTools(request.modelTools);
3210
- const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiFunctionTools] : [...openAiFunctionTools];
3855
+ const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
3211
3856
  const reasoningEffort = resolveOpenAiReasoningEffort(
3212
3857
  providerInfo.model,
3213
3858
  request.openAiReasoningEffort
@@ -3299,9 +3944,9 @@ async function runToolLoop(request) {
3299
3944
  if (usageTokens) {
3300
3945
  emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
3301
3946
  }
3302
- const functionCalls = extractOpenAiFunctionCalls(finalResponse.output);
3947
+ const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
3303
3948
  const stepToolCalls = [];
3304
- if (functionCalls.length === 0) {
3949
+ if (responseToolCalls.length === 0) {
3305
3950
  finalText = responseText;
3306
3951
  finalThoughts = reasoningSummary;
3307
3952
  steps.push({
@@ -3315,10 +3960,21 @@ async function runToolLoop(request) {
3315
3960
  });
3316
3961
  return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
3317
3962
  }
3318
- const callInputs = functionCalls.map((call, index) => {
3963
+ const callInputs = responseToolCalls.map((call, index) => {
3319
3964
  const toolIndex = index + 1;
3320
3965
  const toolId = buildToolLogId(turn, toolIndex);
3321
3966
  const toolName = call.name;
3967
+ if (call.kind === "custom") {
3968
+ return {
3969
+ call,
3970
+ toolName,
3971
+ value: call.input,
3972
+ parseError: void 0,
3973
+ toolId,
3974
+ turn,
3975
+ toolIndex
3976
+ };
3977
+ }
3322
3978
  const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
3323
3979
  return { call, toolName, value, parseError, toolId, turn, toolIndex };
3324
3980
  });
@@ -3333,6 +3989,7 @@ async function runToolLoop(request) {
3333
3989
  },
3334
3990
  async () => {
3335
3991
  const { result, outputPayload } = await executeToolCall({
3992
+ callKind: entry.call.kind,
3336
3993
  toolName: entry.toolName,
3337
3994
  tool: request.tools[entry.toolName],
3338
3995
  rawInput: entry.value,
@@ -3346,11 +4003,19 @@ async function runToolLoop(request) {
3346
4003
  const toolOutputs = [];
3347
4004
  for (const { entry, result, outputPayload } of callResults) {
3348
4005
  stepToolCalls.push({ ...result, callId: entry.call.call_id });
3349
- toolOutputs.push({
3350
- type: "function_call_output",
3351
- call_id: entry.call.call_id,
3352
- output: mergeToolOutput(outputPayload)
3353
- });
4006
+ if (entry.call.kind === "custom") {
4007
+ toolOutputs.push({
4008
+ type: "custom_tool_call_output",
4009
+ call_id: entry.call.call_id,
4010
+ output: mergeToolOutput(outputPayload)
4011
+ });
4012
+ } else {
4013
+ toolOutputs.push({
4014
+ type: "function_call_output",
4015
+ call_id: entry.call.call_id,
4016
+ output: mergeToolOutput(outputPayload)
4017
+ });
4018
+ }
3354
4019
  }
3355
4020
  steps.push({
3356
4021
  step: steps.length + 1,
@@ -3367,24 +4032,28 @@ async function runToolLoop(request) {
3367
4032
  throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
3368
4033
  }
3369
4034
  if (providerInfo.provider === "chatgpt") {
3370
- const openAiFunctionTools = buildOpenAiFunctionTools(request.tools);
4035
+ const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
3371
4036
  const openAiNativeTools = toOpenAiTools(request.modelTools);
3372
- const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiFunctionTools] : [...openAiFunctionTools];
4037
+ const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
3373
4038
  const reasoningEffort = resolveOpenAiReasoningEffort(
3374
4039
  request.model,
3375
4040
  request.openAiReasoningEffort
3376
4041
  );
3377
4042
  const toolLoopInput = toChatGptInput(contents);
4043
+ const conversationId = `tool-loop-${(0, import_node_crypto.randomBytes)(8).toString("hex")}`;
4044
+ const promptCacheKey = conversationId;
3378
4045
  let input = [...toolLoopInput.input];
3379
4046
  for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
3380
4047
  const turn = stepIndex + 1;
3381
- const response = await collectChatGptCodexResponse({
4048
+ const response = await collectChatGptCodexResponseWithRetry({
4049
+ sessionId: conversationId,
3382
4050
  request: {
3383
4051
  model: providerInfo.model,
3384
4052
  store: false,
3385
4053
  stream: true,
3386
4054
  instructions: toolLoopInput.instructions ?? "You are a helpful assistant.",
3387
4055
  input,
4056
+ prompt_cache_key: promptCacheKey,
3388
4057
  include: ["reasoning.encrypted_content"],
3389
4058
  tools: openAiTools,
3390
4059
  tool_choice: "auto",
@@ -3415,8 +4084,8 @@ async function runToolLoop(request) {
3415
4084
  totalCostUsd += stepCostUsd;
3416
4085
  const responseText = (response.text ?? "").trim();
3417
4086
  const reasoningSummaryText = (response.reasoningSummaryText ?? "").trim();
3418
- const functionCalls = response.toolCalls ?? [];
3419
- if (functionCalls.length === 0) {
4087
+ const responseToolCalls = response.toolCalls ?? [];
4088
+ if (responseToolCalls.length === 0) {
3420
4089
  finalText = responseText;
3421
4090
  finalThoughts = reasoningSummaryText;
3422
4091
  steps.push({
@@ -3432,12 +4101,16 @@ async function runToolLoop(request) {
3432
4101
  }
3433
4102
  const toolCalls = [];
3434
4103
  const toolOutputs = [];
3435
- const callInputs = functionCalls.map((call, index) => {
4104
+ const callInputs = responseToolCalls.map((call, index) => {
3436
4105
  const toolIndex = index + 1;
3437
4106
  const toolId = buildToolLogId(turn, toolIndex);
3438
4107
  const toolName = call.name;
3439
- const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
3440
- const ids = normalizeChatGptToolIds({ callId: call.callId, itemId: call.id });
4108
+ const { value, error: parseError } = call.kind === "custom" ? { value: call.input, error: void 0 } : parseOpenAiToolArguments(call.arguments);
4109
+ const ids = normalizeChatGptToolIds({
4110
+ callKind: call.kind,
4111
+ callId: call.callId,
4112
+ itemId: call.id
4113
+ });
3441
4114
  return { call, toolName, value, parseError, ids, toolId, turn, toolIndex };
3442
4115
  });
3443
4116
  const callResults = await Promise.all(
@@ -3451,6 +4124,7 @@ async function runToolLoop(request) {
3451
4124
  },
3452
4125
  async () => {
3453
4126
  const { result, outputPayload } = await executeToolCall({
4127
+ callKind: entry.call.kind,
3454
4128
  toolName: entry.toolName,
3455
4129
  tool: request.tools[entry.toolName],
3456
4130
  rawInput: entry.value,
@@ -3463,19 +4137,35 @@ async function runToolLoop(request) {
3463
4137
  );
3464
4138
  for (const { entry, result, outputPayload } of callResults) {
3465
4139
  toolCalls.push({ ...result, callId: entry.ids.callId });
3466
- toolOutputs.push({
3467
- type: "function_call",
3468
- id: entry.ids.itemId,
3469
- call_id: entry.ids.callId,
3470
- name: entry.toolName,
3471
- arguments: entry.call.arguments,
3472
- status: "completed"
3473
- });
3474
- toolOutputs.push({
3475
- type: "function_call_output",
3476
- call_id: entry.ids.callId,
3477
- output: mergeToolOutput(outputPayload)
3478
- });
4140
+ if (entry.call.kind === "custom") {
4141
+ toolOutputs.push({
4142
+ type: "custom_tool_call",
4143
+ id: entry.ids.itemId,
4144
+ call_id: entry.ids.callId,
4145
+ name: entry.toolName,
4146
+ input: entry.call.input,
4147
+ status: "completed"
4148
+ });
4149
+ toolOutputs.push({
4150
+ type: "custom_tool_call_output",
4151
+ call_id: entry.ids.callId,
4152
+ output: mergeToolOutput(outputPayload)
4153
+ });
4154
+ } else {
4155
+ toolOutputs.push({
4156
+ type: "function_call",
4157
+ id: entry.ids.itemId,
4158
+ call_id: entry.ids.callId,
4159
+ name: entry.toolName,
4160
+ arguments: entry.call.arguments,
4161
+ status: "completed"
4162
+ });
4163
+ toolOutputs.push({
4164
+ type: "function_call_output",
4165
+ call_id: entry.ids.callId,
4166
+ output: mergeToolOutput(outputPayload)
4167
+ });
4168
+ }
3479
4169
  }
3480
4170
  steps.push({
3481
4171
  step: steps.length + 1,
@@ -3490,6 +4180,134 @@ async function runToolLoop(request) {
3490
4180
  }
3491
4181
  throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
3492
4182
  }
4183
+ if (providerInfo.provider === "fireworks") {
4184
+ if (request.modelTools && request.modelTools.length > 0) {
4185
+ throw new Error(
4186
+ "Fireworks provider does not support provider-native modelTools in runToolLoop."
4187
+ );
4188
+ }
4189
+ const fireworksTools = buildFireworksToolsFromToolSet(request.tools);
4190
+ const messages = toFireworksMessages(contents);
4191
+ for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
4192
+ const turn = stepIndex + 1;
4193
+ const response = await runFireworksCall(async (client) => {
4194
+ return await client.chat.completions.create(
4195
+ {
4196
+ model: providerInfo.model,
4197
+ messages,
4198
+ tools: fireworksTools,
4199
+ tool_choice: "auto",
4200
+ parallel_tool_calls: true
4201
+ },
4202
+ { signal: request.signal }
4203
+ );
4204
+ });
4205
+ const modelVersion = typeof response.model === "string" ? response.model : request.model;
4206
+ request.onEvent?.({ type: "model", modelVersion });
4207
+ const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
4208
+ if (choice?.finish_reason === "content_filter") {
4209
+ request.onEvent?.({ type: "blocked" });
4210
+ }
4211
+ const message = choice?.message;
4212
+ const responseText = extractFireworksMessageText(message).trim();
4213
+ if (responseText.length > 0) {
4214
+ request.onEvent?.({ type: "delta", channel: "response", text: responseText });
4215
+ }
4216
+ const usageTokens = extractFireworksUsageTokens(response.usage);
4217
+ const stepCostUsd = estimateCallCostUsd({
4218
+ modelId: modelVersion,
4219
+ tokens: usageTokens,
4220
+ responseImages: 0
4221
+ });
4222
+ totalCostUsd += stepCostUsd;
4223
+ if (usageTokens) {
4224
+ request.onEvent?.({
4225
+ type: "usage",
4226
+ usage: usageTokens,
4227
+ costUsd: stepCostUsd,
4228
+ modelVersion
4229
+ });
4230
+ }
4231
+ const responseToolCalls = extractFireworksToolCalls(message);
4232
+ if (responseToolCalls.length === 0) {
4233
+ finalText = responseText;
4234
+ finalThoughts = "";
4235
+ steps.push({
4236
+ step: steps.length + 1,
4237
+ modelVersion,
4238
+ text: responseText || void 0,
4239
+ thoughts: void 0,
4240
+ toolCalls: [],
4241
+ usage: usageTokens,
4242
+ costUsd: stepCostUsd
4243
+ });
4244
+ return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
4245
+ }
4246
+ const stepToolCalls = [];
4247
+ const callInputs = responseToolCalls.map((call, index) => {
4248
+ const toolIndex = index + 1;
4249
+ const toolId = buildToolLogId(turn, toolIndex);
4250
+ const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
4251
+ return { call, toolName: call.name, value, parseError, toolId, turn, toolIndex };
4252
+ });
4253
+ const callResults = await Promise.all(
4254
+ callInputs.map(async (entry) => {
4255
+ return await toolCallContextStorage.run(
4256
+ {
4257
+ toolName: entry.toolName,
4258
+ toolId: entry.toolId,
4259
+ turn: entry.turn,
4260
+ toolIndex: entry.toolIndex
4261
+ },
4262
+ async () => {
4263
+ const { result, outputPayload } = await executeToolCall({
4264
+ callKind: "function",
4265
+ toolName: entry.toolName,
4266
+ tool: request.tools[entry.toolName],
4267
+ rawInput: entry.value,
4268
+ parseError: entry.parseError
4269
+ });
4270
+ return { entry, result, outputPayload };
4271
+ }
4272
+ );
4273
+ })
4274
+ );
4275
+ const assistantToolCalls = [];
4276
+ const toolMessages = [];
4277
+ for (const { entry, result, outputPayload } of callResults) {
4278
+ stepToolCalls.push({ ...result, callId: entry.call.id });
4279
+ assistantToolCalls.push({
4280
+ id: entry.call.id,
4281
+ type: "function",
4282
+ function: {
4283
+ name: entry.toolName,
4284
+ arguments: entry.call.arguments
4285
+ }
4286
+ });
4287
+ toolMessages.push({
4288
+ role: "tool",
4289
+ tool_call_id: entry.call.id,
4290
+ content: mergeToolOutput(outputPayload)
4291
+ });
4292
+ }
4293
+ steps.push({
4294
+ step: steps.length + 1,
4295
+ modelVersion,
4296
+ text: responseText || void 0,
4297
+ thoughts: void 0,
4298
+ toolCalls: stepToolCalls,
4299
+ usage: usageTokens,
4300
+ costUsd: stepCostUsd
4301
+ });
4302
+ messages.push({
4303
+ role: "assistant",
4304
+ ...responseText.length > 0 ? { content: responseText } : {},
4305
+ tool_calls: assistantToolCalls
4306
+ });
4307
+ messages.push(...toolMessages);
4308
+ }
4309
+ throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
4310
+ }
3493
4311
  const geminiFunctionTools = buildGeminiFunctionDeclarations(request.tools);
3494
4312
  const geminiNativeTools = toGeminiTools(request.modelTools);
3495
4313
  const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
@@ -3639,6 +4457,7 @@ async function runToolLoop(request) {
3639
4457
  },
3640
4458
  async () => {
3641
4459
  const { result, outputPayload } = await executeToolCall({
4460
+ callKind: "function",
3642
4461
  toolName: entry.toolName,
3643
4462
  tool: request.tools[entry.toolName],
3644
4463
  rawInput: entry.rawInput
@@ -3916,12 +4735,1881 @@ function appendMarkdownSourcesSection(value, sources) {
3916
4735
  ## Sources
3917
4736
  ${lines}`;
3918
4737
  }
4738
+
4739
+ // src/tools/filesystemTools.ts
4740
+ var import_node_path5 = __toESM(require("path"), 1);
4741
+ var import_zod5 = require("zod");
4742
+
4743
+ // src/tools/applyPatch.ts
4744
+ var import_node_path4 = __toESM(require("path"), 1);
4745
+ var import_zod4 = require("zod");
4746
+
4747
+ // src/tools/filesystem.ts
4748
+ var import_node_fs3 = require("fs");
4749
+ var import_node_path3 = __toESM(require("path"), 1);
4750
+ var InMemoryAgentFilesystem = class {
4751
+ #files = /* @__PURE__ */ new Map();
4752
+ #dirs = /* @__PURE__ */ new Map();
4753
+ #clock = 0;
4754
+ constructor(initialFiles = {}) {
4755
+ const root = import_node_path3.default.resolve("/");
4756
+ this.#dirs.set(root, { mtimeMs: this.#nextMtime() });
4757
+ for (const [filePath, content] of Object.entries(initialFiles)) {
4758
+ const absolutePath = import_node_path3.default.resolve(filePath);
4759
+ this.#ensureDirSync(import_node_path3.default.dirname(absolutePath));
4760
+ this.#files.set(absolutePath, {
4761
+ content,
4762
+ mtimeMs: this.#nextMtime()
4763
+ });
4764
+ }
4765
+ }
4766
+ async readTextFile(filePath) {
4767
+ const absolutePath = import_node_path3.default.resolve(filePath);
4768
+ const file = this.#files.get(absolutePath);
4769
+ if (!file) {
4770
+ throw createNoSuchFileError("open", absolutePath);
4771
+ }
4772
+ return file.content;
4773
+ }
4774
+ async writeTextFile(filePath, content) {
4775
+ const absolutePath = import_node_path3.default.resolve(filePath);
4776
+ const parentPath = import_node_path3.default.dirname(absolutePath);
4777
+ if (!this.#dirs.has(parentPath)) {
4778
+ throw createNoSuchFileError("open", parentPath);
4779
+ }
4780
+ this.#files.set(absolutePath, { content, mtimeMs: this.#nextMtime() });
4781
+ }
4782
+ async deleteFile(filePath) {
4783
+ const absolutePath = import_node_path3.default.resolve(filePath);
4784
+ if (!this.#files.delete(absolutePath)) {
4785
+ throw createNoSuchFileError("unlink", absolutePath);
4786
+ }
4787
+ }
4788
+ async ensureDir(directoryPath) {
4789
+ this.#ensureDirSync(import_node_path3.default.resolve(directoryPath));
4790
+ }
4791
+ async readDir(directoryPath) {
4792
+ const absolutePath = import_node_path3.default.resolve(directoryPath);
4793
+ const directory = this.#dirs.get(absolutePath);
4794
+ if (!directory) {
4795
+ throw createNoSuchFileError("scandir", absolutePath);
4796
+ }
4797
+ const entries = [];
4798
+ const seenNames = /* @__PURE__ */ new Set();
4799
+ for (const [dirPath, dirRecord] of this.#dirs.entries()) {
4800
+ if (dirPath === absolutePath) {
4801
+ continue;
4802
+ }
4803
+ if (import_node_path3.default.dirname(dirPath) !== absolutePath) {
4804
+ continue;
4805
+ }
4806
+ const name = import_node_path3.default.basename(dirPath);
4807
+ if (seenNames.has(name)) {
4808
+ continue;
4809
+ }
4810
+ seenNames.add(name);
4811
+ entries.push({
4812
+ name,
4813
+ path: dirPath,
4814
+ kind: "directory",
4815
+ mtimeMs: dirRecord.mtimeMs
4816
+ });
4817
+ }
4818
+ for (const [filePath, fileRecord] of this.#files.entries()) {
4819
+ if (import_node_path3.default.dirname(filePath) !== absolutePath) {
4820
+ continue;
4821
+ }
4822
+ const name = import_node_path3.default.basename(filePath);
4823
+ if (seenNames.has(name)) {
4824
+ continue;
4825
+ }
4826
+ seenNames.add(name);
4827
+ entries.push({
4828
+ name,
4829
+ path: filePath,
4830
+ kind: "file",
4831
+ mtimeMs: fileRecord.mtimeMs
4832
+ });
4833
+ }
4834
+ entries.sort((left, right) => left.name.localeCompare(right.name));
4835
+ return entries;
4836
+ }
4837
+ async stat(entryPath) {
4838
+ const absolutePath = import_node_path3.default.resolve(entryPath);
4839
+ const file = this.#files.get(absolutePath);
4840
+ if (file) {
4841
+ return { kind: "file", mtimeMs: file.mtimeMs };
4842
+ }
4843
+ const directory = this.#dirs.get(absolutePath);
4844
+ if (directory) {
4845
+ return { kind: "directory", mtimeMs: directory.mtimeMs };
4846
+ }
4847
+ throw createNoSuchFileError("stat", absolutePath);
4848
+ }
4849
+ snapshot() {
4850
+ const entries = [...this.#files.entries()].sort(([left], [right]) => left.localeCompare(right));
4851
+ return Object.fromEntries(entries.map(([filePath, record]) => [filePath, record.content]));
4852
+ }
4853
+ #ensureDirSync(directoryPath) {
4854
+ const absolutePath = import_node_path3.default.resolve(directoryPath);
4855
+ const parts = [];
4856
+ let cursor = absolutePath;
4857
+ for (; ; ) {
4858
+ if (this.#dirs.has(cursor)) {
4859
+ break;
4860
+ }
4861
+ parts.push(cursor);
4862
+ const parent = import_node_path3.default.dirname(cursor);
4863
+ if (parent === cursor) {
4864
+ break;
4865
+ }
4866
+ cursor = parent;
4867
+ }
4868
+ for (let index = parts.length - 1; index >= 0; index -= 1) {
4869
+ const nextDir = parts[index];
4870
+ if (nextDir === void 0) {
4871
+ continue;
4872
+ }
4873
+ if (!this.#dirs.has(nextDir)) {
4874
+ this.#dirs.set(nextDir, { mtimeMs: this.#nextMtime() });
4875
+ }
4876
+ }
4877
+ }
4878
+ #nextMtime() {
4879
+ this.#clock += 1;
4880
+ return this.#clock;
4881
+ }
4882
+ };
4883
+ function createNodeAgentFilesystem() {
4884
+ return {
4885
+ readTextFile: async (filePath) => import_node_fs3.promises.readFile(filePath, "utf8"),
4886
+ writeTextFile: async (filePath, content) => import_node_fs3.promises.writeFile(filePath, content, "utf8"),
4887
+ deleteFile: async (filePath) => import_node_fs3.promises.unlink(filePath),
4888
+ ensureDir: async (directoryPath) => {
4889
+ await import_node_fs3.promises.mkdir(directoryPath, { recursive: true });
4890
+ },
4891
+ readDir: async (directoryPath) => {
4892
+ const entries = await import_node_fs3.promises.readdir(directoryPath, { withFileTypes: true });
4893
+ const result = [];
4894
+ for (const entry of entries) {
4895
+ const entryPath = import_node_path3.default.resolve(directoryPath, entry.name);
4896
+ const stats = await import_node_fs3.promises.lstat(entryPath);
4897
+ result.push({
4898
+ name: entry.name,
4899
+ path: entryPath,
4900
+ kind: statsToKind(stats),
4901
+ mtimeMs: stats.mtimeMs
4902
+ });
4903
+ }
4904
+ return result;
4905
+ },
4906
+ stat: async (entryPath) => {
4907
+ const stats = await import_node_fs3.promises.lstat(entryPath);
4908
+ return {
4909
+ kind: statsToKind(stats),
4910
+ mtimeMs: stats.mtimeMs
4911
+ };
4912
+ }
4913
+ };
4914
+ }
4915
+ function createInMemoryAgentFilesystem(initialFiles = {}) {
4916
+ return new InMemoryAgentFilesystem(initialFiles);
4917
+ }
4918
+ function statsToKind(stats) {
4919
+ if (stats.isSymbolicLink()) {
4920
+ return "symlink";
4921
+ }
4922
+ if (stats.isDirectory()) {
4923
+ return "directory";
4924
+ }
4925
+ if (stats.isFile()) {
4926
+ return "file";
4927
+ }
4928
+ return "other";
4929
+ }
4930
+ function createNoSuchFileError(syscall, filePath) {
4931
+ const error = new Error(
4932
+ `ENOENT: no such file or directory, ${syscall} '${filePath}'`
4933
+ );
4934
+ error.code = "ENOENT";
4935
+ error.syscall = syscall;
4936
+ error.path = filePath;
4937
+ return error;
4938
+ }
4939
+
4940
+ // src/tools/applyPatch.ts
4941
+ var BEGIN_PATCH_LINE = "*** Begin Patch";
4942
+ var END_PATCH_LINE = "*** End Patch";
4943
+ var ADD_FILE_PREFIX = "*** Add File: ";
4944
+ var DELETE_FILE_PREFIX = "*** Delete File: ";
4945
+ var UPDATE_FILE_PREFIX = "*** Update File: ";
4946
+ var MOVE_TO_PREFIX = "*** Move to: ";
4947
+ var END_OF_FILE_LINE = "*** End of File";
4948
+ var DEFAULT_MAX_PATCH_BYTES = 1024 * 1024;
4949
+ var CODEX_APPLY_PATCH_INPUT_DESCRIPTION = "The entire contents of the apply_patch command";
4950
+ var CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION = "Use the `apply_patch` tool to edit files. This is a FREEFORM tool, so do not wrap the patch in JSON.";
4951
+ var CODEX_APPLY_PATCH_LARK_GRAMMAR = [
4952
+ "start: begin_patch hunk+ end_patch",
4953
+ 'begin_patch: "*** Begin Patch" LF',
4954
+ 'end_patch: "*** End Patch" LF?',
4955
+ "",
4956
+ "hunk: add_hunk | delete_hunk | update_hunk",
4957
+ 'add_hunk: "*** Add File: " filename LF add_line+',
4958
+ 'delete_hunk: "*** Delete File: " filename LF',
4959
+ 'update_hunk: "*** Update File: " filename LF change_move? change?',
4960
+ "",
4961
+ "filename: /(.+)/",
4962
+ 'add_line: "+" /(.*)/ LF -> line',
4963
+ "",
4964
+ 'change_move: "*** Move to: " filename LF',
4965
+ "change: (change_context | change_line)+ eof_line?",
4966
+ 'change_context: ("@@" | "@@ " /(.+)/) LF',
4967
+ 'change_line: ("+" | "-" | " ") /(.*)/ LF',
4968
+ 'eof_line: "*** End of File" LF',
4969
+ "",
4970
+ "%import common.LF"
4971
+ ].join("\n");
4972
+ var CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION = [
4973
+ "Use the `apply_patch` tool to edit files.",
4974
+ "Your patch language is a stripped\u2011down, file\u2011oriented diff format designed to be easy to parse and safe to apply. You can think of it as a high\u2011level envelope:",
4975
+ "",
4976
+ "*** Begin Patch",
4977
+ "[ one or more file sections ]",
4978
+ "*** End Patch",
4979
+ "",
4980
+ "Within that envelope, you get a sequence of file operations.",
4981
+ "You MUST include a header to specify the action you are taking.",
4982
+ "Each operation starts with one of three headers:",
4983
+ "",
4984
+ "*** Add File: <path> - create a new file. Every following line is a + line (the initial contents).",
4985
+ "*** Delete File: <path> - remove an existing file. Nothing follows.",
4986
+ "*** Update File: <path> - patch an existing file in place (optionally with a rename).",
4987
+ "",
4988
+ "May be immediately followed by *** Move to: <new path> if you want to rename the file.",
4989
+ "Then one or more \u201Chunks\u201D, each introduced by @@ (optionally followed by a hunk header).",
4990
+ "Within a hunk each line starts with:",
4991
+ "",
4992
+ "For instructions on [context_before] and [context_after]:",
4993
+ "- By default, show 3 lines of code immediately above and 3 lines immediately below each change. If a change is within 3 lines of a previous change, do NOT duplicate the first change\u2019s [context_after] lines in the second change\u2019s [context_before] lines.",
4994
+ "- If 3 lines of context is insufficient to uniquely identify the snippet of code within the file, use the @@ operator to indicate the class or function to which the snippet belongs. For instance, we might have:",
4995
+ "@@ class BaseClass",
4996
+ "[3 lines of pre-context]",
4997
+ "- [old_code]",
4998
+ "+ [new_code]",
4999
+ "[3 lines of post-context]",
5000
+ "",
5001
+ "- If a code block is repeated so many times in a class or function such that even a single `@@` statement and 3 lines of context cannot uniquely identify the snippet of code, you can use multiple `@@` statements to jump to the right context. For instance:",
5002
+ "",
5003
+ "@@ class BaseClass",
5004
+ "@@ def method():",
5005
+ "[3 lines of pre-context]",
5006
+ "- [old_code]",
5007
+ "+ [new_code]",
5008
+ "[3 lines of post-context]",
5009
+ "",
5010
+ "The full grammar definition is below:",
5011
+ "Patch := Begin { FileOp } End",
5012
+ 'Begin := "*** Begin Patch" NEWLINE',
5013
+ 'End := "*** End Patch" NEWLINE',
5014
+ "FileOp := AddFile | DeleteFile | UpdateFile",
5015
+ 'AddFile := "*** Add File: " path NEWLINE { "+" line NEWLINE }',
5016
+ 'DeleteFile := "*** Delete File: " path NEWLINE',
5017
+ 'UpdateFile := "*** Update File: " path NEWLINE [ MoveTo ] { Hunk }',
5018
+ 'MoveTo := "*** Move to: " newPath NEWLINE',
5019
+ 'Hunk := "@@" [ header ] NEWLINE { HunkLine } [ "*** End of File" NEWLINE ]',
5020
+ 'HunkLine := (" " | "-" | "+") text NEWLINE',
5021
+ "",
5022
+ "A full patch can combine several operations:",
5023
+ "",
5024
+ "*** Begin Patch",
5025
+ "*** Add File: hello.txt",
5026
+ "+Hello world",
5027
+ "*** Update File: src/app.py",
5028
+ "*** Move to: src/main.py",
5029
+ "@@ def greet():",
5030
+ '-print("Hi")',
5031
+ '+print("Hello, world!")',
5032
+ "*** Delete File: obsolete.txt",
5033
+ "*** End Patch",
5034
+ "",
5035
+ "It is important to remember:",
5036
+ "",
5037
+ "- You must include a header with your intended action (Add/Delete/Update)",
5038
+ "- You must prefix new lines with `+` even when creating a new file",
5039
+ "- File references can only be relative, NEVER ABSOLUTE."
5040
+ ].join("\n");
5041
+ var applyPatchToolInputSchema = import_zod4.z.object({
5042
+ input: import_zod4.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
5043
+ });
5044
+ function createApplyPatchTool(options = {}) {
5045
+ return tool({
5046
+ description: options.description ?? CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
5047
+ inputSchema: applyPatchToolInputSchema,
5048
+ execute: async ({ input }) => applyPatch({
5049
+ patch: input,
5050
+ cwd: options.cwd,
5051
+ fs: options.fs,
5052
+ allowOutsideCwd: options.allowOutsideCwd,
5053
+ checkAccess: options.checkAccess,
5054
+ maxPatchBytes: options.maxPatchBytes
5055
+ })
5056
+ });
5057
+ }
5058
+ async function applyPatch(request) {
5059
+ const cwd = import_node_path4.default.resolve(request.cwd ?? process.cwd());
5060
+ const adapter = request.fs ?? createNodeAgentFilesystem();
5061
+ const allowOutsideCwd = request.allowOutsideCwd === true;
5062
+ const patchBytes = Buffer.byteLength(request.patch, "utf8");
5063
+ const maxPatchBytes = request.maxPatchBytes ?? DEFAULT_MAX_PATCH_BYTES;
5064
+ if (patchBytes > maxPatchBytes) {
5065
+ throw new Error(
5066
+ `apply_patch failed: patch too large (${patchBytes} bytes > ${maxPatchBytes} bytes)`
5067
+ );
5068
+ }
5069
+ const parsed = parsePatchDocument(normalizePatchText(request.patch));
5070
+ const added = [];
5071
+ const modified = [];
5072
+ const deleted = [];
5073
+ for (const operation of parsed.operations) {
5074
+ if (operation.type === "add") {
5075
+ const absolutePath2 = resolvePatchPath(operation.path, cwd, allowOutsideCwd);
5076
+ await runAccessHook(request.checkAccess, {
5077
+ cwd,
5078
+ kind: "add",
5079
+ path: absolutePath2
5080
+ });
5081
+ await adapter.ensureDir(import_node_path4.default.dirname(absolutePath2));
5082
+ await adapter.writeTextFile(absolutePath2, operation.content);
5083
+ added.push(toDisplayPath(absolutePath2, cwd));
5084
+ continue;
5085
+ }
5086
+ if (operation.type === "delete") {
5087
+ const absolutePath2 = resolvePatchPath(operation.path, cwd, allowOutsideCwd);
5088
+ await runAccessHook(request.checkAccess, {
5089
+ cwd,
5090
+ kind: "delete",
5091
+ path: absolutePath2
5092
+ });
5093
+ await adapter.readTextFile(absolutePath2);
5094
+ await adapter.deleteFile(absolutePath2);
5095
+ deleted.push(toDisplayPath(absolutePath2, cwd));
5096
+ continue;
5097
+ }
5098
+ const absolutePath = resolvePatchPath(operation.path, cwd, allowOutsideCwd);
5099
+ await runAccessHook(request.checkAccess, {
5100
+ cwd,
5101
+ kind: "update",
5102
+ path: absolutePath
5103
+ });
5104
+ const current = await adapter.readTextFile(absolutePath);
5105
+ const next = deriveUpdatedContent(current, operation.chunks, toDisplayPath(absolutePath, cwd));
5106
+ if (operation.movePath) {
5107
+ const destinationPath = resolvePatchPath(operation.movePath, cwd, allowOutsideCwd);
5108
+ await runAccessHook(request.checkAccess, {
5109
+ cwd,
5110
+ kind: "move",
5111
+ path: destinationPath,
5112
+ fromPath: absolutePath,
5113
+ toPath: destinationPath
5114
+ });
5115
+ await adapter.ensureDir(import_node_path4.default.dirname(destinationPath));
5116
+ await adapter.writeTextFile(destinationPath, next);
5117
+ await adapter.deleteFile(absolutePath);
5118
+ modified.push(toDisplayPath(destinationPath, cwd));
5119
+ continue;
5120
+ }
5121
+ await adapter.writeTextFile(absolutePath, next);
5122
+ modified.push(toDisplayPath(absolutePath, cwd));
5123
+ }
5124
+ return {
5125
+ success: true,
5126
+ summary: formatSummary(added, modified, deleted),
5127
+ added,
5128
+ modified,
5129
+ deleted
5130
+ };
5131
+ }
5132
+ async function runAccessHook(hook, context) {
5133
+ if (!hook) {
5134
+ return;
5135
+ }
5136
+ await hook(context);
5137
+ }
5138
+ function normalizePatchText(raw) {
5139
+ return raw.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
5140
+ }
5141
+ function resolvePatchPath(rawPath, cwd, allowOutsideCwd) {
5142
+ const trimmed = rawPath.trim();
5143
+ if (trimmed.length === 0) {
5144
+ throw new Error("apply_patch failed: empty file path");
5145
+ }
5146
+ const absolutePath = import_node_path4.default.isAbsolute(trimmed) ? import_node_path4.default.resolve(trimmed) : import_node_path4.default.resolve(cwd, trimmed);
5147
+ if (!allowOutsideCwd && !isPathInsideCwd(absolutePath, cwd)) {
5148
+ throw new Error(`apply_patch failed: path "${trimmed}" resolves outside cwd "${cwd}"`);
5149
+ }
5150
+ return absolutePath;
5151
+ }
5152
+ function isPathInsideCwd(candidatePath, cwd) {
5153
+ const relative = import_node_path4.default.relative(cwd, candidatePath);
5154
+ return relative === "" || !relative.startsWith("..") && !import_node_path4.default.isAbsolute(relative);
5155
+ }
5156
+ function toDisplayPath(absolutePath, cwd) {
5157
+ const relative = import_node_path4.default.relative(cwd, absolutePath);
5158
+ if (relative === "") {
5159
+ return ".";
5160
+ }
5161
+ if (!relative.startsWith("..") && !import_node_path4.default.isAbsolute(relative)) {
5162
+ return relative;
5163
+ }
5164
+ return absolutePath;
5165
+ }
5166
+ function parsePatchDocument(patch) {
5167
+ const lines = patch.split("\n");
5168
+ if (lines.at(-1) === "") {
5169
+ lines.pop();
5170
+ }
5171
+ if (lines.length < 2) {
5172
+ throw new Error("apply_patch failed: patch must contain Begin/End markers");
5173
+ }
5174
+ if (lines[0] !== BEGIN_PATCH_LINE) {
5175
+ throw new Error(`apply_patch failed: missing "${BEGIN_PATCH_LINE}" header`);
5176
+ }
5177
+ if (lines[lines.length - 1] !== END_PATCH_LINE) {
5178
+ throw new Error(`apply_patch failed: missing "${END_PATCH_LINE}" footer`);
5179
+ }
5180
+ const body = lines.slice(1, -1);
5181
+ if (body.length === 0) {
5182
+ throw new Error("apply_patch failed: patch body is empty");
5183
+ }
5184
+ const operations = [];
5185
+ let index = 0;
5186
+ while (index < body.length) {
5187
+ const line = body[index];
5188
+ if (!line) {
5189
+ throw new Error("apply_patch failed: unexpected empty line between file sections");
5190
+ }
5191
+ if (line.startsWith(ADD_FILE_PREFIX)) {
5192
+ const filePath = extractPatchPath(line, ADD_FILE_PREFIX);
5193
+ index += 1;
5194
+ const contentLines = [];
5195
+ while (index < body.length) {
5196
+ const contentLine = body[index];
5197
+ if (contentLine === void 0 || isPatchSectionHeader(contentLine)) {
5198
+ break;
5199
+ }
5200
+ if (!contentLine.startsWith("+")) {
5201
+ throw new Error(`apply_patch failed: invalid add-file line "${contentLine}"`);
5202
+ }
5203
+ contentLines.push(contentLine.slice(1));
5204
+ index += 1;
5205
+ }
5206
+ if (contentLines.length === 0) {
5207
+ throw new Error(`apply_patch failed: add-file section for "${filePath}" is empty`);
5208
+ }
5209
+ operations.push({
5210
+ type: "add",
5211
+ path: filePath,
5212
+ content: `${contentLines.join("\n")}
5213
+ `
5214
+ });
5215
+ continue;
5216
+ }
5217
+ if (line.startsWith(DELETE_FILE_PREFIX)) {
5218
+ operations.push({
5219
+ type: "delete",
5220
+ path: extractPatchPath(line, DELETE_FILE_PREFIX)
5221
+ });
5222
+ index += 1;
5223
+ continue;
5224
+ }
5225
+ if (line.startsWith(UPDATE_FILE_PREFIX)) {
5226
+ const filePath = extractPatchPath(line, UPDATE_FILE_PREFIX);
5227
+ index += 1;
5228
+ let movePath;
5229
+ const moveHeader = body[index];
5230
+ if (moveHeader?.startsWith(MOVE_TO_PREFIX)) {
5231
+ movePath = extractPatchPath(moveHeader, MOVE_TO_PREFIX);
5232
+ index += 1;
5233
+ }
5234
+ const chunks = [];
5235
+ while (index < body.length) {
5236
+ const hunkHeader = body[index];
5237
+ if (hunkHeader === void 0 || isPatchSectionHeader(hunkHeader)) {
5238
+ break;
5239
+ }
5240
+ if (!(hunkHeader === "@@" || hunkHeader.startsWith("@@ "))) {
5241
+ throw new Error(
5242
+ `apply_patch failed: expected hunk marker in "${filePath}", got "${hunkHeader}"`
5243
+ );
5244
+ }
5245
+ const contextSelector = hunkHeader.length > 2 ? hunkHeader.slice(3) : void 0;
5246
+ index += 1;
5247
+ const oldLines = [];
5248
+ const newLines = [];
5249
+ let sawBodyLine = false;
5250
+ let sawChangeLine = false;
5251
+ let isEndOfFile = false;
5252
+ while (index < body.length) {
5253
+ const chunkLine = body[index];
5254
+ if (chunkLine === void 0) {
5255
+ break;
5256
+ }
5257
+ if (chunkLine === "@@" || chunkLine.startsWith("@@ ") || isPatchSectionHeader(chunkLine)) {
5258
+ break;
5259
+ }
5260
+ if (chunkLine === END_OF_FILE_LINE) {
5261
+ isEndOfFile = true;
5262
+ index += 1;
5263
+ break;
5264
+ }
5265
+ if (chunkLine.length === 0) {
5266
+ throw new Error(`apply_patch failed: invalid empty hunk line in "${filePath}"`);
5267
+ }
5268
+ const prefix = chunkLine[0];
5269
+ const content = chunkLine.slice(1);
5270
+ if (prefix === " ") {
5271
+ oldLines.push(content);
5272
+ newLines.push(content);
5273
+ } else if (prefix === "-") {
5274
+ oldLines.push(content);
5275
+ sawChangeLine = true;
5276
+ } else if (prefix === "+") {
5277
+ newLines.push(content);
5278
+ sawChangeLine = true;
5279
+ } else {
5280
+ throw new Error(
5281
+ `apply_patch failed: unsupported hunk prefix "${prefix}" in "${chunkLine}"`
5282
+ );
5283
+ }
5284
+ sawBodyLine = true;
5285
+ index += 1;
5286
+ }
5287
+ if (!sawBodyLine) {
5288
+ throw new Error(`apply_patch failed: empty hunk body in "${filePath}"`);
5289
+ }
5290
+ if (!sawChangeLine) {
5291
+ throw new Error(
5292
+ `apply_patch failed: hunk in "${filePath}" must include '+' or '-' lines`
5293
+ );
5294
+ }
5295
+ chunks.push({
5296
+ contextSelector,
5297
+ oldLines,
5298
+ newLines,
5299
+ isEndOfFile
5300
+ });
5301
+ }
5302
+ if (chunks.length === 0) {
5303
+ throw new Error(`apply_patch failed: update section for "${filePath}" has no hunks`);
5304
+ }
5305
+ operations.push({
5306
+ type: "update",
5307
+ path: filePath,
5308
+ movePath,
5309
+ chunks
5310
+ });
5311
+ continue;
5312
+ }
5313
+ throw new Error(`apply_patch failed: unrecognized section header "${line}"`);
5314
+ }
5315
+ return { operations };
5316
+ }
5317
+ function extractPatchPath(line, prefix) {
5318
+ const value = line.slice(prefix.length).trim();
5319
+ if (value.length === 0) {
5320
+ throw new Error(`apply_patch failed: missing file path in "${line}"`);
5321
+ }
5322
+ return value;
5323
+ }
5324
+ function isPatchSectionHeader(line) {
5325
+ return line.startsWith(ADD_FILE_PREFIX) || line.startsWith(DELETE_FILE_PREFIX) || line.startsWith(UPDATE_FILE_PREFIX);
5326
+ }
5327
+ function deriveUpdatedContent(originalContent, chunks, displayPath) {
5328
+ const originalLines = splitFileContentIntoLines(originalContent);
5329
+ const replacements = [];
5330
+ let lineIndex = 0;
5331
+ for (const chunk of chunks) {
5332
+ if (chunk.contextSelector !== void 0) {
5333
+ const contextIndex = seekSequence(originalLines, [chunk.contextSelector], lineIndex, false);
5334
+ if (contextIndex === null) {
5335
+ throw new Error(
5336
+ `apply_patch failed: unable to locate context "${chunk.contextSelector}" in ${displayPath}`
5337
+ );
5338
+ }
5339
+ lineIndex = contextIndex + 1;
5340
+ }
5341
+ if (chunk.oldLines.length === 0) {
5342
+ replacements.push({
5343
+ startIndex: originalLines.length,
5344
+ oldLength: 0,
5345
+ newLines: [...chunk.newLines]
5346
+ });
5347
+ continue;
5348
+ }
5349
+ let oldLines = [...chunk.oldLines];
5350
+ let newLines = [...chunk.newLines];
5351
+ let startIndex = seekSequence(originalLines, oldLines, lineIndex, chunk.isEndOfFile);
5352
+ if (startIndex === null && oldLines.at(-1) === "") {
5353
+ oldLines = oldLines.slice(0, -1);
5354
+ if (newLines.at(-1) === "") {
5355
+ newLines = newLines.slice(0, -1);
5356
+ }
5357
+ startIndex = seekSequence(originalLines, oldLines, lineIndex, chunk.isEndOfFile);
5358
+ }
5359
+ if (startIndex === null) {
5360
+ throw new Error(
5361
+ `apply_patch failed: failed to match hunk in ${displayPath}:
5362
+ ${chunk.oldLines.join("\n")}`
5363
+ );
5364
+ }
5365
+ replacements.push({
5366
+ startIndex,
5367
+ oldLength: oldLines.length,
5368
+ newLines
5369
+ });
5370
+ lineIndex = startIndex + oldLines.length;
5371
+ }
5372
+ replacements.sort((left, right) => left.startIndex - right.startIndex);
5373
+ const nextLines = applyReplacements(originalLines, replacements);
5374
+ if (nextLines.length > 0 && nextLines[nextLines.length - 1] !== "") {
5375
+ nextLines.push("");
5376
+ }
5377
+ return nextLines.join("\n");
5378
+ }
5379
+ function splitFileContentIntoLines(content) {
5380
+ const lines = content.split("\n");
5381
+ if (lines.at(-1) === "") {
5382
+ lines.pop();
5383
+ }
5384
+ return lines;
5385
+ }
5386
+ function seekSequence(sourceLines, targetLines, startIndex, isEndOfFile) {
5387
+ if (targetLines.length === 0) {
5388
+ return Math.min(Math.max(startIndex, 0), sourceLines.length);
5389
+ }
5390
+ const from = Math.max(startIndex, 0);
5391
+ const maxStart = sourceLines.length - targetLines.length;
5392
+ if (maxStart < from) {
5393
+ return null;
5394
+ }
5395
+ const matchesAt = (candidateIndex) => {
5396
+ for (let offset = 0; offset < targetLines.length; offset += 1) {
5397
+ if (sourceLines[candidateIndex + offset] !== targetLines[offset]) {
5398
+ return false;
5399
+ }
5400
+ }
5401
+ return true;
5402
+ };
5403
+ if (isEndOfFile) {
5404
+ return matchesAt(maxStart) ? maxStart : null;
5405
+ }
5406
+ for (let candidate = from; candidate <= maxStart; candidate += 1) {
5407
+ if (matchesAt(candidate)) {
5408
+ return candidate;
5409
+ }
5410
+ }
5411
+ return null;
5412
+ }
5413
+ function applyReplacements(lines, replacements) {
5414
+ const result = [...lines];
5415
+ for (let index = replacements.length - 1; index >= 0; index -= 1) {
5416
+ const replacement = replacements[index];
5417
+ if (replacement === void 0) {
5418
+ continue;
5419
+ }
5420
+ result.splice(replacement.startIndex, replacement.oldLength, ...replacement.newLines);
5421
+ }
5422
+ return result;
5423
+ }
5424
+ function formatSummary(added, modified, deleted) {
5425
+ const lines = ["Success. Updated the following files:"];
5426
+ for (const filePath of added) {
5427
+ lines.push(`A ${filePath}`);
5428
+ }
5429
+ for (const filePath of modified) {
5430
+ lines.push(`M ${filePath}`);
5431
+ }
5432
+ for (const filePath of deleted) {
5433
+ lines.push(`D ${filePath}`);
5434
+ }
5435
+ return `${lines.join("\n")}
5436
+ `;
5437
+ }
5438
+
5439
+ // src/tools/filesystemTools.ts
5440
+ var DEFAULT_READ_FILE_LINE_LIMIT = 2e3;
5441
+ var DEFAULT_READ_FILES_LINE_LIMIT = 200;
5442
+ var DEFAULT_READ_FILES_CHAR_LIMIT = 4e3;
5443
+ var DEFAULT_LIST_DIR_LIMIT = 25;
5444
+ var DEFAULT_LIST_DIR_DEPTH = 2;
5445
+ var DEFAULT_GREP_LIMIT = 100;
5446
+ var MAX_GREP_LIMIT = 2e3;
5447
+ var DEFAULT_MAX_LINE_LENGTH = 500;
5448
+ var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
5449
+ var DEFAULT_TAB_WIDTH = 4;
5450
+ var codexReadFileInputSchema = import_zod5.z.object({
5451
+ file_path: import_zod5.z.string().min(1).describe("Absolute path to the file"),
5452
+ offset: import_zod5.z.number().int().min(1).optional().describe("The line number to start reading from. Must be 1 or greater."),
5453
+ limit: import_zod5.z.number().int().min(1).optional().describe("The maximum number of lines to return."),
5454
+ mode: import_zod5.z.enum(["slice", "indentation"]).optional().describe('Optional mode selector: "slice" (default) or "indentation".'),
5455
+ indentation: import_zod5.z.object({
5456
+ anchor_line: import_zod5.z.number().int().min(1).optional(),
5457
+ max_levels: import_zod5.z.number().int().min(0).optional(),
5458
+ include_siblings: import_zod5.z.boolean().optional(),
5459
+ include_header: import_zod5.z.boolean().optional(),
5460
+ max_lines: import_zod5.z.number().int().min(1).optional()
5461
+ }).optional()
5462
+ });
5463
+ var codexListDirInputSchema = import_zod5.z.object({
5464
+ dir_path: import_zod5.z.string().min(1).describe("Absolute path to the directory to list."),
5465
+ offset: import_zod5.z.number().int().min(1).optional().describe("The entry number to start listing from. Must be 1 or greater."),
5466
+ limit: import_zod5.z.number().int().min(1).optional().describe("The maximum number of entries to return."),
5467
+ depth: import_zod5.z.number().int().min(1).optional().describe("The maximum directory depth to traverse. Must be 1 or greater.")
5468
+ });
5469
+ var codexGrepFilesInputSchema = import_zod5.z.object({
5470
+ pattern: import_zod5.z.string().min(1).describe("Regular expression pattern to search for."),
5471
+ include: import_zod5.z.string().optional().describe('Optional glob limiting searched files (for example "*.rs").'),
5472
+ path: import_zod5.z.string().optional().describe("Directory or file path to search. Defaults to cwd."),
5473
+ limit: import_zod5.z.number().int().min(1).optional().describe("Maximum number of file paths to return (defaults to 100).")
5474
+ });
5475
+ var applyPatchInputSchema = import_zod5.z.object({
5476
+ input: import_zod5.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
5477
+ });
5478
+ var geminiReadFileInputSchema = import_zod5.z.object({
5479
+ file_path: import_zod5.z.string().min(1),
5480
+ offset: import_zod5.z.number().int().min(0).nullish(),
5481
+ limit: import_zod5.z.number().int().min(1).nullish()
5482
+ });
5483
+ var geminiReadFilesInputSchema = import_zod5.z.object({
5484
+ paths: import_zod5.z.array(import_zod5.z.string().min(1)).min(1),
5485
+ line_offset: import_zod5.z.number().int().min(0).nullish(),
5486
+ line_limit: import_zod5.z.number().int().min(1).nullish(),
5487
+ char_offset: import_zod5.z.number().int().min(0).nullish(),
5488
+ char_limit: import_zod5.z.number().int().min(1).nullish(),
5489
+ include_line_numbers: import_zod5.z.boolean().nullish()
5490
+ }).superRefine((value, context) => {
5491
+ const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
5492
+ const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
5493
+ if (hasLineWindow && hasCharWindow) {
5494
+ context.addIssue({
5495
+ code: import_zod5.z.ZodIssueCode.custom,
5496
+ message: "Use either line_* or char_* window arguments, not both."
5497
+ });
5498
+ }
5499
+ });
5500
+ var geminiWriteFileInputSchema = import_zod5.z.object({
5501
+ file_path: import_zod5.z.string().min(1),
5502
+ content: import_zod5.z.string()
5503
+ });
5504
+ var geminiReplaceInputSchema = import_zod5.z.object({
5505
+ file_path: import_zod5.z.string().min(1),
5506
+ instruction: import_zod5.z.string().min(1),
5507
+ old_string: import_zod5.z.string(),
5508
+ new_string: import_zod5.z.string(),
5509
+ expected_replacements: import_zod5.z.number().int().min(1).nullish()
5510
+ });
5511
+ var geminiListDirectoryInputSchema = import_zod5.z.object({
5512
+ dir_path: import_zod5.z.string().min(1),
5513
+ ignore: import_zod5.z.array(import_zod5.z.string()).nullish(),
5514
+ file_filtering_options: import_zod5.z.object({
5515
+ respect_git_ignore: import_zod5.z.boolean().nullish(),
5516
+ respect_gemini_ignore: import_zod5.z.boolean().nullish()
5517
+ }).nullish()
5518
+ });
5519
+ var geminiRgSearchInputSchema = import_zod5.z.object({
5520
+ pattern: import_zod5.z.string().min(1),
5521
+ path: import_zod5.z.string().nullish(),
5522
+ glob: import_zod5.z.string().nullish(),
5523
+ case_sensitive: import_zod5.z.boolean().nullish(),
5524
+ exclude_pattern: import_zod5.z.string().nullish(),
5525
+ names_only: import_zod5.z.boolean().nullish(),
5526
+ max_matches_per_file: import_zod5.z.number().int().min(1).nullish(),
5527
+ max_results: import_zod5.z.number().int().min(1).nullish()
5528
+ });
5529
+ var geminiGrepSearchInputSchema = import_zod5.z.object({
5530
+ pattern: import_zod5.z.string().min(1),
5531
+ dir_path: import_zod5.z.string().nullish(),
5532
+ include: import_zod5.z.string().nullish(),
5533
+ exclude_pattern: import_zod5.z.string().nullish(),
5534
+ names_only: import_zod5.z.boolean().nullish(),
5535
+ max_matches_per_file: import_zod5.z.number().int().min(1).nullish(),
5536
+ total_max_matches: import_zod5.z.number().int().min(1).nullish()
5537
+ });
5538
+ var geminiGlobInputSchema = import_zod5.z.object({
5539
+ pattern: import_zod5.z.string().min(1),
5540
+ dir_path: import_zod5.z.string().nullish(),
5541
+ case_sensitive: import_zod5.z.boolean().nullish(),
5542
+ respect_git_ignore: import_zod5.z.boolean().nullish(),
5543
+ respect_gemini_ignore: import_zod5.z.boolean().nullish()
5544
+ });
5545
+ function resolveFilesystemToolProfile(model, profile = "auto") {
5546
+ if (profile !== "auto") {
5547
+ return profile;
5548
+ }
5549
+ if (isCodexModel(model)) {
5550
+ return "codex";
5551
+ }
5552
+ if (isGeminiModel(model)) {
5553
+ return "gemini";
5554
+ }
5555
+ return "model-agnostic";
5556
+ }
5557
+ function createFilesystemToolSetForModel(model, profileOrOptions = "auto", maybeOptions) {
5558
+ if (typeof profileOrOptions === "string") {
5559
+ const resolvedProfile2 = resolveFilesystemToolProfile(model, profileOrOptions);
5560
+ if (resolvedProfile2 === "codex") {
5561
+ return createCodexFilesystemToolSet(maybeOptions);
5562
+ }
5563
+ if (resolvedProfile2 === "gemini") {
5564
+ return createGeminiFilesystemToolSet(maybeOptions);
5565
+ }
5566
+ return createModelAgnosticFilesystemToolSet(maybeOptions);
5567
+ }
5568
+ const resolvedProfile = resolveFilesystemToolProfile(model, "auto");
5569
+ if (resolvedProfile === "codex") {
5570
+ return createCodexFilesystemToolSet(profileOrOptions);
5571
+ }
5572
+ if (resolvedProfile === "gemini") {
5573
+ return createGeminiFilesystemToolSet(profileOrOptions);
5574
+ }
5575
+ return createModelAgnosticFilesystemToolSet(profileOrOptions);
5576
+ }
5577
+ function createCodexFilesystemToolSet(options = {}) {
5578
+ return {
5579
+ apply_patch: createCodexApplyPatchTool(options),
5580
+ read_file: createCodexReadFileTool(options),
5581
+ list_dir: createListDirTool(options),
5582
+ grep_files: createGrepFilesTool(options)
5583
+ };
5584
+ }
5585
+ function createGeminiFilesystemToolSet(options = {}) {
5586
+ return {
5587
+ read_file: createGeminiReadFileTool(options),
5588
+ write_file: createWriteFileTool(options),
5589
+ replace: createReplaceTool(options),
5590
+ list_directory: createListDirectoryTool(options),
5591
+ grep_search: createGrepSearchTool(options),
5592
+ glob: createGlobTool(options)
5593
+ };
5594
+ }
5595
+ function createModelAgnosticFilesystemToolSet(options = {}) {
5596
+ return createGeminiFilesystemToolSet(options);
5597
+ }
5598
+ function createCodexApplyPatchTool(options = {}) {
5599
+ return customTool({
5600
+ description: CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
5601
+ format: {
5602
+ type: "grammar",
5603
+ syntax: "lark",
5604
+ definition: CODEX_APPLY_PATCH_LARK_GRAMMAR
5605
+ },
5606
+ execute: async (input) => {
5607
+ const runtime = resolveRuntime(options);
5608
+ const result = await applyPatch({
5609
+ patch: input,
5610
+ cwd: runtime.cwd,
5611
+ fs: runtime.filesystem,
5612
+ allowOutsideCwd: runtime.allowOutsideCwd,
5613
+ checkAccess: runtime.checkAccess ? async (context) => {
5614
+ await runtime.checkAccess?.({
5615
+ cwd: runtime.cwd,
5616
+ tool: "apply_patch",
5617
+ action: mapApplyPatchAction(context.kind),
5618
+ path: context.path,
5619
+ fromPath: context.fromPath,
5620
+ toPath: context.toPath
5621
+ });
5622
+ } : void 0,
5623
+ maxPatchBytes: options.applyPatch?.maxPatchBytes
5624
+ });
5625
+ return result.summary;
5626
+ }
5627
+ });
5628
+ }
5629
+ function createCodexReadFileTool(options = {}) {
5630
+ return tool({
5631
+ description: "Reads a local file with 1-indexed line numbers, supporting slice and indentation-aware block modes.",
5632
+ inputSchema: codexReadFileInputSchema,
5633
+ execute: async (input) => readFileCodex(input, options)
5634
+ });
5635
+ }
5636
+ function createListDirTool(options = {}) {
5637
+ return tool({
5638
+ description: "Lists entries in a local directory with 1-indexed entry numbers and simple type labels.",
5639
+ inputSchema: codexListDirInputSchema,
5640
+ execute: async (input) => listDirectoryCodex(input, options)
5641
+ });
5642
+ }
5643
+ function createGrepFilesTool(options = {}) {
5644
+ return tool({
5645
+ description: "Finds files whose contents match the pattern and lists them by modification time.",
5646
+ inputSchema: codexGrepFilesInputSchema,
5647
+ execute: async (input) => grepFilesCodex(input, options)
5648
+ });
5649
+ }
5650
+ function createGeminiReadFileTool(options = {}) {
5651
+ return tool({
5652
+ description: "Reads and returns the content of a specified file. Supports optional 0-based line offset and line limit.",
5653
+ inputSchema: geminiReadFileInputSchema,
5654
+ execute: async (input) => readFileGemini(input, options)
5655
+ });
5656
+ }
5657
+ function createReadFilesTool(options = {}) {
5658
+ return tool({
5659
+ description: "Reads one or more files with optional line-based or character-based slicing, similar to a controlled head/tail view.",
5660
+ inputSchema: geminiReadFilesInputSchema,
5661
+ execute: async (input) => readFilesGemini(input, options)
5662
+ });
5663
+ }
5664
+ function createWriteFileTool(options = {}) {
5665
+ return tool({
5666
+ description: "Writes content to a specified file in the local filesystem.",
5667
+ inputSchema: geminiWriteFileInputSchema,
5668
+ execute: async (input) => writeFileGemini(input, options)
5669
+ });
5670
+ }
5671
+ function createReplaceTool(options = {}) {
5672
+ return tool({
5673
+ description: "Replaces exact literal text within a file.",
5674
+ inputSchema: geminiReplaceInputSchema,
5675
+ execute: async (input) => replaceFileContentGemini(input, options)
5676
+ });
5677
+ }
5678
+ function createListDirectoryTool(options = {}) {
5679
+ return tool({
5680
+ description: "Lists files and subdirectories directly within a specified directory path.",
5681
+ inputSchema: geminiListDirectoryInputSchema,
5682
+ execute: async (input) => listDirectoryGemini(input, options)
5683
+ });
5684
+ }
5685
+ function createGrepSearchTool(options = {}) {
5686
+ return tool({
5687
+ description: "Searches for a regex pattern within file contents.",
5688
+ inputSchema: geminiGrepSearchInputSchema,
5689
+ execute: async (input) => grepSearchGemini(input, options)
5690
+ });
5691
+ }
5692
+ function createRgSearchTool(options = {}) {
5693
+ return tool({
5694
+ description: "Searches for a regex pattern within file contents.",
5695
+ inputSchema: geminiRgSearchInputSchema,
5696
+ execute: async (input) => rgSearchGemini(input, options)
5697
+ });
5698
+ }
5699
+ function createGlobTool(options = {}) {
5700
+ return tool({
5701
+ description: "Finds files matching glob patterns, sorted by modification time (newest first).",
5702
+ inputSchema: geminiGlobInputSchema,
5703
+ execute: async (input) => globFilesGemini(input, options)
5704
+ });
5705
+ }
5706
+ async function readFileCodex(input, options) {
5707
+ const runtime = resolveRuntime(options);
5708
+ if (!import_node_path5.default.isAbsolute(input.file_path)) {
5709
+ throw new Error("file_path must be an absolute path");
5710
+ }
5711
+ const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
5712
+ await runAccessHook2(runtime, {
5713
+ cwd: runtime.cwd,
5714
+ tool: "read_file",
5715
+ action: "read",
5716
+ path: filePath
5717
+ });
5718
+ const content = await runtime.filesystem.readTextFile(filePath);
5719
+ const lines = splitLines(content);
5720
+ const offset = input.offset ?? 1;
5721
+ const limit = input.limit ?? DEFAULT_READ_FILE_LINE_LIMIT;
5722
+ const mode = input.mode ?? "slice";
5723
+ if (offset > lines.length) {
5724
+ throw new Error("offset exceeds file length");
5725
+ }
5726
+ if (mode === "slice") {
5727
+ const output = [];
5728
+ const lastLine = Math.min(lines.length, offset + limit - 1);
5729
+ for (let lineNumber = offset; lineNumber <= lastLine; lineNumber += 1) {
5730
+ const line = lines[lineNumber - 1] ?? "";
5731
+ output.push(`L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`);
5732
+ }
5733
+ return output.join("\n");
5734
+ }
5735
+ const indentation = input.indentation ?? {};
5736
+ const anchorLine = indentation.anchor_line ?? offset;
5737
+ if (anchorLine < 1 || anchorLine > lines.length) {
5738
+ throw new Error("anchor_line exceeds file length");
5739
+ }
5740
+ const records = lines.map((line, index) => ({
5741
+ number: index + 1,
5742
+ raw: line,
5743
+ display: truncateAtCodePointBoundary(line, runtime.maxLineLength),
5744
+ indent: measureIndent(line, DEFAULT_TAB_WIDTH)
5745
+ }));
5746
+ const selected = readWithIndentationMode({
5747
+ records,
5748
+ anchorLine,
5749
+ limit,
5750
+ maxLevels: indentation.max_levels ?? 0,
5751
+ includeSiblings: indentation.include_siblings ?? false,
5752
+ includeHeader: indentation.include_header ?? true,
5753
+ maxLines: indentation.max_lines
5754
+ });
5755
+ return selected.map((record) => `L${record.number}: ${record.display}`).join("\n");
5756
+ }
5757
+ async function listDirectoryCodex(input, options) {
5758
+ const runtime = resolveRuntime(options);
5759
+ if (!import_node_path5.default.isAbsolute(input.dir_path)) {
5760
+ throw new Error("dir_path must be an absolute path");
5761
+ }
5762
+ const dirPath = resolvePathWithPolicy(input.dir_path, runtime.cwd, runtime.allowOutsideCwd);
5763
+ await runAccessHook2(runtime, {
5764
+ cwd: runtime.cwd,
5765
+ tool: "list_dir",
5766
+ action: "list",
5767
+ path: dirPath
5768
+ });
5769
+ const stats = await runtime.filesystem.stat(dirPath);
5770
+ if (stats.kind !== "directory") {
5771
+ throw new Error(`failed to read directory: "${dirPath}" is not a directory`);
5772
+ }
5773
+ const offset = input.offset ?? 1;
5774
+ const limit = input.limit ?? DEFAULT_LIST_DIR_LIMIT;
5775
+ const depth = input.depth ?? DEFAULT_LIST_DIR_DEPTH;
5776
+ const entries = await collectDirectoryEntries(
5777
+ runtime.filesystem,
5778
+ dirPath,
5779
+ depth,
5780
+ runtime.maxLineLength
5781
+ );
5782
+ if (offset > entries.length) {
5783
+ throw new Error("offset exceeds directory entry count");
5784
+ }
5785
+ const startIndex = offset - 1;
5786
+ const remaining = entries.length - startIndex;
5787
+ const cappedLimit = Math.min(limit, remaining);
5788
+ const selected = entries.slice(startIndex, startIndex + cappedLimit);
5789
+ const output = [`Absolute path: ${dirPath}`];
5790
+ for (const entry of selected) {
5791
+ output.push(formatListEntry(entry));
5792
+ }
5793
+ if (startIndex + cappedLimit < entries.length) {
5794
+ output.push(`More than ${cappedLimit} entries found`);
5795
+ }
5796
+ return output.join("\n");
5797
+ }
5798
+ async function grepFilesCodex(input, options) {
5799
+ const runtime = resolveRuntime(options);
5800
+ const pattern = input.pattern.trim();
5801
+ if (pattern.length === 0) {
5802
+ throw new Error("pattern must not be empty");
5803
+ }
5804
+ const regex = compileRegex(pattern);
5805
+ const searchPath = resolvePathWithPolicy(
5806
+ input.path ?? runtime.cwd,
5807
+ runtime.cwd,
5808
+ runtime.allowOutsideCwd
5809
+ );
5810
+ await runAccessHook2(runtime, {
5811
+ cwd: runtime.cwd,
5812
+ tool: "grep_files",
5813
+ action: "search",
5814
+ path: searchPath,
5815
+ pattern,
5816
+ include: input.include?.trim()
5817
+ });
5818
+ const searchPathInfo = await runtime.filesystem.stat(searchPath);
5819
+ const filesToScan = await collectSearchFiles({
5820
+ filesystem: runtime.filesystem,
5821
+ searchPath,
5822
+ rootKind: searchPathInfo.kind,
5823
+ maxScannedFiles: runtime.grepMaxScannedFiles
5824
+ });
5825
+ const includeMatcher = input.include ? createGlobMatcher(input.include) : null;
5826
+ const matches = [];
5827
+ for (const filePath of filesToScan) {
5828
+ const relativePath = toDisplayPath2(filePath, runtime.cwd);
5829
+ if (includeMatcher && !includeMatcher(relativePath)) {
5830
+ continue;
5831
+ }
5832
+ const fileContent = await runtime.filesystem.readTextFile(filePath);
5833
+ if (!regex.test(fileContent)) {
5834
+ continue;
5835
+ }
5836
+ const stats = await runtime.filesystem.stat(filePath);
5837
+ matches.push({ filePath: normalizeSlashes(relativePath), mtimeMs: stats.mtimeMs });
5838
+ }
5839
+ if (matches.length === 0) {
5840
+ return "No matches found.";
5841
+ }
5842
+ matches.sort((left, right) => right.mtimeMs - left.mtimeMs);
5843
+ const limit = Math.min(input.limit ?? DEFAULT_GREP_LIMIT, MAX_GREP_LIMIT);
5844
+ return matches.slice(0, limit).map((match) => match.filePath).join("\n");
5845
+ }
5846
+ async function readFileGemini(input, options) {
5847
+ const runtime = resolveRuntime(options);
5848
+ const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
5849
+ await runAccessHook2(runtime, {
5850
+ cwd: runtime.cwd,
5851
+ tool: "read_file",
5852
+ action: "read",
5853
+ path: filePath
5854
+ });
5855
+ const content = await runtime.filesystem.readTextFile(filePath);
5856
+ const lines = splitLines(content);
5857
+ const offset = Math.max(0, input.offset ?? 0);
5858
+ const limit = input.limit ?? DEFAULT_READ_FILE_LINE_LIMIT;
5859
+ if (offset >= lines.length) {
5860
+ return "";
5861
+ }
5862
+ const end = Math.min(lines.length, offset + limit);
5863
+ return lines.slice(offset, end).map(
5864
+ (line, index) => `L${offset + index + 1}: ${truncateAtCodePointBoundary(line ?? "", runtime.maxLineLength)}`
5865
+ ).join("\n");
5866
+ }
5867
+ async function readFilesGemini(input, options) {
5868
+ const runtime = resolveRuntime(options);
5869
+ const useCharWindow = input.char_offset !== void 0 || input.char_limit !== void 0;
5870
+ const lineOffset = Math.max(0, input.line_offset ?? 0);
5871
+ const lineLimit = input.line_limit ?? DEFAULT_READ_FILES_LINE_LIMIT;
5872
+ const charOffset = Math.max(0, input.char_offset ?? 0);
5873
+ const charLimit = input.char_limit ?? DEFAULT_READ_FILES_CHAR_LIMIT;
5874
+ const includeLineNumbers = input.include_line_numbers !== false;
5875
+ const sections = [];
5876
+ for (const rawPath of input.paths) {
5877
+ const filePath = resolvePathWithPolicy(rawPath, runtime.cwd, runtime.allowOutsideCwd);
5878
+ await runAccessHook2(runtime, {
5879
+ cwd: runtime.cwd,
5880
+ tool: "read_files",
5881
+ action: "read",
5882
+ path: filePath
5883
+ });
5884
+ const content = await runtime.filesystem.readTextFile(filePath);
5885
+ const displayPath = normalizeSlashes(toDisplayPath2(filePath, runtime.cwd));
5886
+ sections.push(`==> ${displayPath} <==`);
5887
+ if (useCharWindow) {
5888
+ if (charOffset >= content.length) {
5889
+ sections.push("");
5890
+ continue;
5891
+ }
5892
+ const end2 = Math.min(content.length, charOffset + charLimit);
5893
+ sections.push(content.slice(charOffset, end2));
5894
+ continue;
5895
+ }
5896
+ const lines = splitLines(content);
5897
+ if (lineOffset >= lines.length) {
5898
+ sections.push("");
5899
+ continue;
5900
+ }
5901
+ const end = Math.min(lines.length, lineOffset + lineLimit);
5902
+ const selected = lines.slice(lineOffset, end);
5903
+ if (includeLineNumbers) {
5904
+ for (let index = 0; index < selected.length; index += 1) {
5905
+ const lineNumber = lineOffset + index + 1;
5906
+ const line = selected[index] ?? "";
5907
+ sections.push(
5908
+ `L${lineNumber}: ${truncateAtCodePointBoundary(line, runtime.maxLineLength)}`
5909
+ );
5910
+ }
5911
+ continue;
5912
+ }
5913
+ sections.push(selected.join("\n"));
5914
+ }
5915
+ return sections.join("\n");
5916
+ }
5917
+ async function writeFileGemini(input, options) {
5918
+ const runtime = resolveRuntime(options);
5919
+ const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
5920
+ await runAccessHook2(runtime, {
5921
+ cwd: runtime.cwd,
5922
+ tool: "write_file",
5923
+ action: "write",
5924
+ path: filePath
5925
+ });
5926
+ await runtime.filesystem.ensureDir(import_node_path5.default.dirname(filePath));
5927
+ await runtime.filesystem.writeTextFile(filePath, input.content);
5928
+ return `Successfully wrote file: ${toDisplayPath2(filePath, runtime.cwd)}`;
5929
+ }
5930
+ async function replaceFileContentGemini(input, options) {
5931
+ const runtime = resolveRuntime(options);
5932
+ const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
5933
+ await runAccessHook2(runtime, {
5934
+ cwd: runtime.cwd,
5935
+ tool: "replace",
5936
+ action: "write",
5937
+ path: filePath
5938
+ });
5939
+ const expectedReplacements = input.expected_replacements ?? 1;
5940
+ const oldValue = input.old_string;
5941
+ const newValue = input.new_string;
5942
+ let originalContent = "";
5943
+ try {
5944
+ originalContent = await runtime.filesystem.readTextFile(filePath);
5945
+ } catch (error) {
5946
+ if (isNoEntError(error) && oldValue.length === 0) {
5947
+ await runtime.filesystem.ensureDir(import_node_path5.default.dirname(filePath));
5948
+ await runtime.filesystem.writeTextFile(filePath, newValue);
5949
+ return `Successfully wrote new file: ${toDisplayPath2(filePath, runtime.cwd)}`;
5950
+ }
5951
+ throw error;
5952
+ }
5953
+ if (oldValue === newValue) {
5954
+ throw new Error("No changes to apply. old_string and new_string are identical.");
5955
+ }
5956
+ const occurrences = countOccurrences(originalContent, oldValue);
5957
+ if (occurrences === 0) {
5958
+ throw new Error("Failed to edit, could not find old_string in file.");
5959
+ }
5960
+ if (occurrences !== expectedReplacements) {
5961
+ throw new Error(
5962
+ `Failed to edit, expected ${expectedReplacements} occurrence(s) but found ${occurrences}.`
5963
+ );
5964
+ }
5965
+ const updatedContent = safeReplaceAll(originalContent, oldValue, newValue);
5966
+ await runtime.filesystem.writeTextFile(filePath, updatedContent);
5967
+ return `Successfully replaced ${occurrences} occurrence(s) in ${toDisplayPath2(filePath, runtime.cwd)}.`;
5968
+ }
5969
+ async function listDirectoryGemini(input, options) {
5970
+ const runtime = resolveRuntime(options);
5971
+ const dirPath = resolvePathWithPolicy(input.dir_path, runtime.cwd, runtime.allowOutsideCwd);
5972
+ await runAccessHook2(runtime, {
5973
+ cwd: runtime.cwd,
5974
+ tool: "list_directory",
5975
+ action: "list",
5976
+ path: dirPath
5977
+ });
5978
+ const stats = await runtime.filesystem.stat(dirPath);
5979
+ if (stats.kind !== "directory") {
5980
+ throw new Error(`Path is not a directory: ${dirPath}`);
5981
+ }
5982
+ const entries = await runtime.filesystem.readDir(dirPath);
5983
+ const ignoreMatchers = (input.ignore ?? []).map((pattern) => createGlobMatcher(pattern));
5984
+ const filtered = entries.filter((entry) => {
5985
+ if (ignoreMatchers.length === 0) {
5986
+ return true;
5987
+ }
5988
+ return !ignoreMatchers.some((matches) => matches(entry.name));
5989
+ }).sort((left, right) => left.name.localeCompare(right.name));
5990
+ if (filtered.length === 0) {
5991
+ return `Directory ${toDisplayPath2(dirPath, runtime.cwd)} is empty.`;
5992
+ }
5993
+ return filtered.map((entry) => {
5994
+ const label = entry.kind === "directory" ? `${entry.name}/` : entry.name;
5995
+ return label;
5996
+ }).join("\n");
5997
+ }
5998
+ async function rgSearchGemini(input, options, toolName = "rg_search") {
5999
+ const runtime = resolveRuntime(options);
6000
+ const pattern = input.pattern.trim();
6001
+ if (pattern.length === 0) {
6002
+ throw new Error("pattern must not be empty");
6003
+ }
6004
+ const glob = input.glob?.trim();
6005
+ const searchPath = resolvePathWithPolicy(
6006
+ input.path ?? runtime.cwd,
6007
+ runtime.cwd,
6008
+ runtime.allowOutsideCwd
6009
+ );
6010
+ await runAccessHook2(runtime, {
6011
+ cwd: runtime.cwd,
6012
+ tool: toolName,
6013
+ action: "search",
6014
+ path: searchPath,
6015
+ pattern,
6016
+ include: glob
6017
+ });
6018
+ const searchPathInfo = await runtime.filesystem.stat(searchPath);
6019
+ const filesToScan = await collectSearchFiles({
6020
+ filesystem: runtime.filesystem,
6021
+ searchPath,
6022
+ rootKind: searchPathInfo.kind,
6023
+ maxScannedFiles: runtime.grepMaxScannedFiles
6024
+ });
6025
+ const matcher = glob ? createGlobMatcher(glob) : null;
6026
+ const patternRegex = compileRegex(pattern, input.case_sensitive === true ? "m" : "im");
6027
+ const excludeRegex = input.exclude_pattern ? compileRegex(input.exclude_pattern) : null;
6028
+ const totalMaxMatches = input.max_results ?? DEFAULT_GREP_LIMIT;
6029
+ const perFileMaxMatches = input.max_matches_per_file ?? Number.POSITIVE_INFINITY;
6030
+ const matches = [];
6031
+ const fileMatches = /* @__PURE__ */ new Set();
6032
+ for (const filePath of filesToScan) {
6033
+ const relativePath = normalizeSlashes(toDisplayPath2(filePath, runtime.cwd));
6034
+ if (matcher && !matcher(relativePath)) {
6035
+ continue;
6036
+ }
6037
+ const content = await runtime.filesystem.readTextFile(filePath);
6038
+ const lines = splitLines(content);
6039
+ let fileMatchCount = 0;
6040
+ for (let index = 0; index < lines.length; index += 1) {
6041
+ const line = lines[index] ?? "";
6042
+ if (!patternRegex.test(line)) {
6043
+ continue;
6044
+ }
6045
+ if (excludeRegex?.test(line)) {
6046
+ continue;
6047
+ }
6048
+ if (fileMatches.has(relativePath) === false) {
6049
+ fileMatches.add(relativePath);
6050
+ }
6051
+ if (input.names_only) {
6052
+ continue;
6053
+ }
6054
+ matches.push({
6055
+ filePath: relativePath,
6056
+ mtimeMs: 0,
6057
+ lineNumber: index + 1,
6058
+ line
6059
+ });
6060
+ fileMatchCount += 1;
6061
+ if (fileMatchCount >= perFileMaxMatches || matches.length >= totalMaxMatches) {
6062
+ break;
6063
+ }
6064
+ }
6065
+ if (input.names_only && fileMatches.size >= totalMaxMatches) {
6066
+ break;
6067
+ }
6068
+ if (!input.names_only && matches.length >= totalMaxMatches) {
6069
+ break;
6070
+ }
6071
+ }
6072
+ if (input.names_only) {
6073
+ if (fileMatches.size === 0) {
6074
+ return "No matches found.";
6075
+ }
6076
+ return [...fileMatches].slice(0, totalMaxMatches).join("\n");
6077
+ }
6078
+ if (matches.length === 0) {
6079
+ return "No matches found.";
6080
+ }
6081
+ return matches.slice(0, totalMaxMatches).map((match) => `${match.filePath}:${match.lineNumber}:${match.line ?? ""}`).join("\n");
6082
+ }
6083
+ async function grepSearchGemini(input, options) {
6084
+ return rgSearchGemini(
6085
+ {
6086
+ pattern: input.pattern,
6087
+ path: input.dir_path,
6088
+ glob: input.include,
6089
+ exclude_pattern: input.exclude_pattern,
6090
+ names_only: input.names_only,
6091
+ max_matches_per_file: input.max_matches_per_file,
6092
+ max_results: input.total_max_matches
6093
+ },
6094
+ options,
6095
+ "grep_search"
6096
+ );
6097
+ }
6098
+ async function globFilesGemini(input, options) {
6099
+ const runtime = resolveRuntime(options);
6100
+ const dirPath = resolvePathWithPolicy(
6101
+ input.dir_path ?? runtime.cwd,
6102
+ runtime.cwd,
6103
+ runtime.allowOutsideCwd
6104
+ );
6105
+ await runAccessHook2(runtime, {
6106
+ cwd: runtime.cwd,
6107
+ tool: "glob",
6108
+ action: "search",
6109
+ path: dirPath,
6110
+ pattern: input.pattern
6111
+ });
6112
+ const dirStats = await runtime.filesystem.stat(dirPath);
6113
+ if (dirStats.kind !== "directory") {
6114
+ throw new Error(`Path is not a directory: ${dirPath}`);
6115
+ }
6116
+ const matcher = createGlobMatcher(input.pattern, input.case_sensitive === true);
6117
+ const files = await collectSearchFiles({
6118
+ filesystem: runtime.filesystem,
6119
+ searchPath: dirPath,
6120
+ rootKind: "directory",
6121
+ maxScannedFiles: runtime.grepMaxScannedFiles
6122
+ });
6123
+ const matched = [];
6124
+ for (const filePath of files) {
6125
+ const relativePath = normalizeSlashes(import_node_path5.default.relative(dirPath, filePath));
6126
+ if (!matcher(relativePath)) {
6127
+ continue;
6128
+ }
6129
+ const fileStats = await runtime.filesystem.stat(filePath);
6130
+ matched.push({
6131
+ filePath,
6132
+ mtimeMs: fileStats.mtimeMs
6133
+ });
6134
+ }
6135
+ if (matched.length === 0) {
6136
+ return "No files found.";
6137
+ }
6138
+ matched.sort((left, right) => right.mtimeMs - left.mtimeMs);
6139
+ return matched.map((entry) => normalizeSlashes(toDisplayPath2(entry.filePath, runtime.cwd))).join("\n");
6140
+ }
6141
+ function resolveRuntime(options) {
6142
+ return {
6143
+ cwd: import_node_path5.default.resolve(options.cwd ?? process.cwd()),
6144
+ filesystem: options.fs ?? createNodeAgentFilesystem(),
6145
+ allowOutsideCwd: options.allowOutsideCwd === true,
6146
+ checkAccess: options.checkAccess,
6147
+ maxLineLength: options.maxLineLength ?? DEFAULT_MAX_LINE_LENGTH,
6148
+ grepMaxScannedFiles: options.grepMaxScannedFiles ?? DEFAULT_GREP_MAX_SCANNED_FILES
6149
+ };
6150
+ }
6151
+ async function runAccessHook2(runtime, context) {
6152
+ if (!runtime.checkAccess) {
6153
+ return;
6154
+ }
6155
+ await runtime.checkAccess(context);
6156
+ }
6157
+ function isCodexModel(model) {
6158
+ const normalized = model.startsWith("chatgpt-") ? model.slice("chatgpt-".length) : model;
6159
+ return normalized.includes("codex");
6160
+ }
6161
+ function isGeminiModel(model) {
6162
+ return model.startsWith("gemini-");
6163
+ }
6164
+ function mapApplyPatchAction(action) {
6165
+ if (action === "add" || action === "update") {
6166
+ return "write";
6167
+ }
6168
+ if (action === "delete") {
6169
+ return "delete";
6170
+ }
6171
+ return "move";
6172
+ }
6173
+ function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
6174
+ const absolutePath = import_node_path5.default.isAbsolute(inputPath) ? import_node_path5.default.resolve(inputPath) : import_node_path5.default.resolve(cwd, inputPath);
6175
+ if (!allowOutsideCwd && !isPathInsideCwd2(absolutePath, cwd)) {
6176
+ throw new Error(`path "${inputPath}" resolves outside cwd "${cwd}"`);
6177
+ }
6178
+ return absolutePath;
6179
+ }
6180
+ function isPathInsideCwd2(candidatePath, cwd) {
6181
+ const relative = import_node_path5.default.relative(cwd, candidatePath);
6182
+ return relative === "" || !relative.startsWith("..") && !import_node_path5.default.isAbsolute(relative);
6183
+ }
6184
+ function toDisplayPath2(absolutePath, cwd) {
6185
+ const relative = import_node_path5.default.relative(cwd, absolutePath);
6186
+ if (relative === "") {
6187
+ return ".";
6188
+ }
6189
+ if (!relative.startsWith("..") && !import_node_path5.default.isAbsolute(relative)) {
6190
+ return relative;
6191
+ }
6192
+ return absolutePath;
6193
+ }
6194
+ function splitLines(content) {
6195
+ const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
6196
+ const lines = normalized.split("\n");
6197
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
6198
+ lines.pop();
6199
+ }
6200
+ return lines;
6201
+ }
6202
+ function truncateAtCodePointBoundary(value, maxLength) {
6203
+ if (value.length <= maxLength) {
6204
+ return value;
6205
+ }
6206
+ return Array.from(value).slice(0, maxLength).join("");
6207
+ }
6208
+ function measureIndent(line, tabWidth) {
6209
+ let count = 0;
6210
+ for (const char of line) {
6211
+ if (char === " ") {
6212
+ count += 1;
6213
+ continue;
6214
+ }
6215
+ if (char === " ") {
6216
+ count += tabWidth;
6217
+ continue;
6218
+ }
6219
+ break;
6220
+ }
6221
+ return count;
6222
+ }
6223
+ function computeEffectiveIndents(records) {
6224
+ const effective = [];
6225
+ let previous = 0;
6226
+ for (const record of records) {
6227
+ if (record.raw.trim().length === 0) {
6228
+ effective.push(previous);
6229
+ } else {
6230
+ previous = record.indent;
6231
+ effective.push(previous);
6232
+ }
6233
+ }
6234
+ return effective;
6235
+ }
6236
+ function trimBoundaryBlankLines(records) {
6237
+ while (records.length > 0 && records[0]?.raw.trim().length === 0) {
6238
+ records.shift();
6239
+ }
6240
+ while (records.length > 0 && records[records.length - 1]?.raw.trim().length === 0) {
6241
+ records.pop();
6242
+ }
6243
+ }
6244
+ function isCommentLine(line) {
6245
+ const trimmed = line.trim();
6246
+ return trimmed.startsWith("#") || trimmed.startsWith("//") || trimmed.startsWith("--");
6247
+ }
6248
+ function readWithIndentationMode(params) {
6249
+ const { records, anchorLine, limit, maxLevels, includeSiblings, includeHeader, maxLines } = params;
6250
+ const anchorIndex = anchorLine - 1;
6251
+ const effectiveIndents = computeEffectiveIndents(records);
6252
+ const anchorIndent = effectiveIndents[anchorIndex] ?? 0;
6253
+ const minIndent = maxLevels === 0 ? 0 : Math.max(anchorIndent - maxLevels * DEFAULT_TAB_WIDTH, 0);
6254
+ const guardLimit = maxLines ?? limit;
6255
+ const finalLimit = Math.min(limit, guardLimit, records.length);
6256
+ if (finalLimit <= 1) {
6257
+ return [records[anchorIndex]].filter((entry) => Boolean(entry));
6258
+ }
6259
+ let upper = anchorIndex - 1;
6260
+ let lower = anchorIndex + 1;
6261
+ let upperMinIndentHits = 0;
6262
+ let lowerMinIndentHits = 0;
6263
+ const output = [records[anchorIndex]].filter(
6264
+ (entry) => Boolean(entry)
6265
+ );
6266
+ while (output.length < finalLimit) {
6267
+ let progressed = 0;
6268
+ if (upper >= 0) {
6269
+ const candidate = records[upper];
6270
+ const candidateIndent = effectiveIndents[upper] ?? 0;
6271
+ if (candidate && candidateIndent >= minIndent) {
6272
+ output.unshift(candidate);
6273
+ progressed += 1;
6274
+ upper -= 1;
6275
+ if (candidateIndent === minIndent && !includeSiblings) {
6276
+ const allowHeaderComment = includeHeader && isCommentLine(candidate.raw);
6277
+ const canTakeLine = allowHeaderComment || upperMinIndentHits === 0;
6278
+ if (canTakeLine) {
6279
+ upperMinIndentHits += 1;
6280
+ } else {
6281
+ output.shift();
6282
+ progressed -= 1;
6283
+ upper = -1;
6284
+ }
6285
+ }
6286
+ if (output.length >= finalLimit) {
6287
+ break;
6288
+ }
6289
+ } else {
6290
+ upper = -1;
6291
+ }
6292
+ }
6293
+ if (lower < records.length) {
6294
+ const candidate = records[lower];
6295
+ const candidateIndent = effectiveIndents[lower] ?? 0;
6296
+ if (candidate && candidateIndent >= minIndent) {
6297
+ output.push(candidate);
6298
+ progressed += 1;
6299
+ lower += 1;
6300
+ if (candidateIndent === minIndent && !includeSiblings) {
6301
+ if (lowerMinIndentHits > 0) {
6302
+ output.pop();
6303
+ progressed -= 1;
6304
+ lower = records.length;
6305
+ }
6306
+ lowerMinIndentHits += 1;
6307
+ }
6308
+ } else {
6309
+ lower = records.length;
6310
+ }
6311
+ }
6312
+ if (progressed === 0) {
6313
+ break;
6314
+ }
6315
+ }
6316
+ trimBoundaryBlankLines(output);
6317
+ return output;
6318
+ }
6319
+ async function collectDirectoryEntries(filesystem, rootPath, depth, maxLineLength) {
6320
+ const queue = [
6321
+ { path: rootPath, relativePrefix: "", remainingDepth: depth }
6322
+ ];
6323
+ const records = [];
6324
+ while (queue.length > 0) {
6325
+ const next = queue.shift();
6326
+ if (!next) {
6327
+ break;
6328
+ }
6329
+ const entries = await filesystem.readDir(next.path);
6330
+ const nextEntries = [...entries].map((entry) => {
6331
+ const relativePath = next.relativePrefix ? `${next.relativePrefix}/${entry.name}` : entry.name;
6332
+ return {
6333
+ entry,
6334
+ relativePath,
6335
+ depth: next.relativePrefix.length === 0 ? 0 : next.relativePrefix.split("/").length,
6336
+ sortName: normalizeSlashes(relativePath)
6337
+ };
6338
+ }).sort((left, right) => left.sortName.localeCompare(right.sortName));
6339
+ for (const item of nextEntries) {
6340
+ if (item.entry.kind === "directory" && next.remainingDepth > 1) {
6341
+ queue.push({
6342
+ path: item.entry.path,
6343
+ relativePrefix: item.relativePath,
6344
+ remainingDepth: next.remainingDepth - 1
6345
+ });
6346
+ }
6347
+ records.push({
6348
+ name: item.sortName,
6349
+ displayName: truncateAtCodePointBoundary(item.entry.name, maxLineLength),
6350
+ depth: item.depth,
6351
+ kind: item.entry.kind
6352
+ });
6353
+ }
6354
+ }
6355
+ records.sort((left, right) => left.name.localeCompare(right.name));
6356
+ return records;
6357
+ }
6358
+ function formatListEntry(entry) {
6359
+ const indent = " ".repeat(entry.depth * 2);
6360
+ let name = entry.displayName;
6361
+ if (entry.kind === "directory") {
6362
+ name += "/";
6363
+ } else if (entry.kind === "symlink") {
6364
+ name += "@";
6365
+ } else if (entry.kind === "other") {
6366
+ name += "?";
6367
+ }
6368
+ return `${indent}${name}`;
6369
+ }
6370
+ async function collectSearchFiles(params) {
6371
+ const { filesystem, searchPath, rootKind, maxScannedFiles } = params;
6372
+ if (rootKind === "file") {
6373
+ return [searchPath];
6374
+ }
6375
+ const queue = [searchPath];
6376
+ const files = [];
6377
+ while (queue.length > 0) {
6378
+ const current = queue.shift();
6379
+ if (!current) {
6380
+ break;
6381
+ }
6382
+ const entries = await filesystem.readDir(current);
6383
+ for (const entry of entries) {
6384
+ if (entry.kind === "directory") {
6385
+ queue.push(entry.path);
6386
+ continue;
6387
+ }
6388
+ if (entry.kind !== "file") {
6389
+ continue;
6390
+ }
6391
+ files.push(entry.path);
6392
+ if (files.length >= maxScannedFiles) {
6393
+ return files;
6394
+ }
6395
+ }
6396
+ }
6397
+ return files;
6398
+ }
6399
+ function compileRegex(pattern, flags = "m") {
6400
+ try {
6401
+ return new RegExp(pattern, flags);
6402
+ } catch (error) {
6403
+ const message = error instanceof Error ? error.message : String(error);
6404
+ throw new Error(`invalid regex pattern: ${message}`);
6405
+ }
6406
+ }
6407
+ function createGlobMatcher(pattern, caseSensitive = false) {
6408
+ const expanded = expandBracePatterns(normalizeSlashes(pattern.trim()));
6409
+ const flags = caseSensitive ? "" : "i";
6410
+ const compiled = expanded.map((entry) => ({
6411
+ regex: globToRegex(entry, flags),
6412
+ applyToBasename: !entry.includes("/")
6413
+ }));
6414
+ return (candidatePath) => {
6415
+ const normalizedPath = normalizeSlashes(candidatePath);
6416
+ const basename = import_node_path5.default.posix.basename(normalizedPath);
6417
+ return compiled.some(
6418
+ (entry) => entry.regex.test(entry.applyToBasename ? basename : normalizedPath)
6419
+ );
6420
+ };
6421
+ }
6422
+ function globToRegex(globPattern, flags) {
6423
+ let source = "^";
6424
+ for (let index = 0; index < globPattern.length; index += 1) {
6425
+ const char = globPattern[index];
6426
+ const nextChar = globPattern[index + 1];
6427
+ if (char === void 0) {
6428
+ continue;
6429
+ }
6430
+ if (char === "*" && nextChar === "*") {
6431
+ source += ".*";
6432
+ index += 1;
6433
+ continue;
6434
+ }
6435
+ if (char === "*") {
6436
+ source += "[^/]*";
6437
+ continue;
6438
+ }
6439
+ if (char === "?") {
6440
+ source += "[^/]";
6441
+ continue;
6442
+ }
6443
+ source += escapeRegexCharacter(char);
6444
+ }
6445
+ source += "$";
6446
+ return new RegExp(source, flags);
6447
+ }
6448
+ function expandBracePatterns(pattern) {
6449
+ const start = pattern.indexOf("{");
6450
+ if (start === -1) {
6451
+ return [pattern];
6452
+ }
6453
+ let depth = 0;
6454
+ let end = -1;
6455
+ for (let index = start; index < pattern.length; index += 1) {
6456
+ const char = pattern[index];
6457
+ if (char === "{") {
6458
+ depth += 1;
6459
+ continue;
6460
+ }
6461
+ if (char === "}") {
6462
+ depth -= 1;
6463
+ if (depth === 0) {
6464
+ end = index;
6465
+ break;
6466
+ }
6467
+ }
6468
+ }
6469
+ if (end === -1) {
6470
+ return [pattern];
6471
+ }
6472
+ const prefix = pattern.slice(0, start);
6473
+ const suffix = pattern.slice(end + 1);
6474
+ const body = pattern.slice(start + 1, end);
6475
+ const variants = splitTopLevel(body, ",");
6476
+ const expanded = [];
6477
+ for (const variant of variants) {
6478
+ expanded.push(...expandBracePatterns(`${prefix}${variant}${suffix}`));
6479
+ }
6480
+ return expanded;
6481
+ }
6482
+ function splitTopLevel(value, separator) {
6483
+ const parts = [];
6484
+ let depth = 0;
6485
+ let current = "";
6486
+ for (const char of value) {
6487
+ if (char === "{") {
6488
+ depth += 1;
6489
+ current += char;
6490
+ continue;
6491
+ }
6492
+ if (char === "}") {
6493
+ depth = Math.max(0, depth - 1);
6494
+ current += char;
6495
+ continue;
6496
+ }
6497
+ if (char === separator && depth === 0) {
6498
+ parts.push(current);
6499
+ current = "";
6500
+ continue;
6501
+ }
6502
+ current += char;
6503
+ }
6504
+ parts.push(current);
6505
+ return parts;
6506
+ }
6507
+ function escapeRegexCharacter(char) {
6508
+ return /[.*+?^${}()|[\]\\]/u.test(char) ? `\\${char}` : char;
6509
+ }
6510
+ function normalizeSlashes(value) {
6511
+ return value.replaceAll("\\", "/");
6512
+ }
6513
+ function countOccurrences(text, search) {
6514
+ if (search.length === 0) {
6515
+ return 0;
6516
+ }
6517
+ return text.split(search).length - 1;
6518
+ }
6519
+ function safeReplaceAll(text, search, replacement) {
6520
+ if (search.length === 0) {
6521
+ return text;
6522
+ }
6523
+ return text.split(search).join(replacement);
6524
+ }
6525
+ function isNoEntError(error) {
6526
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
6527
+ }
6528
+
6529
+ // src/agent.ts
6530
+ async function runAgentLoop(request) {
6531
+ const { tools: customTools, filesystemTool, filesystem_tool, ...toolLoopRequest } = request;
6532
+ const filesystemSelection = filesystemTool ?? filesystem_tool;
6533
+ const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
6534
+ const mergedTools = mergeToolSets(filesystemTools, customTools ?? {});
6535
+ if (Object.keys(mergedTools).length === 0) {
6536
+ throw new Error(
6537
+ "runAgentLoop requires at least one tool. Provide `tools` or enable `filesystemTool`."
6538
+ );
6539
+ }
6540
+ return runToolLoop({
6541
+ ...toolLoopRequest,
6542
+ tools: mergedTools
6543
+ });
6544
+ }
6545
+ function resolveFilesystemTools(model, selection) {
6546
+ if (selection === void 0 || selection === false) {
6547
+ return {};
6548
+ }
6549
+ if (selection === true) {
6550
+ return createFilesystemToolSetForModel(model, "auto");
6551
+ }
6552
+ if (typeof selection === "string") {
6553
+ return createFilesystemToolSetForModel(model, selection);
6554
+ }
6555
+ if (selection.enabled === false) {
6556
+ return {};
6557
+ }
6558
+ if (selection.options && selection.profile !== void 0) {
6559
+ return createFilesystemToolSetForModel(model, selection.profile, selection.options);
6560
+ }
6561
+ if (selection.options) {
6562
+ return createFilesystemToolSetForModel(model, selection.options);
6563
+ }
6564
+ return createFilesystemToolSetForModel(model, selection.profile ?? "auto");
6565
+ }
6566
+ function mergeToolSets(base, extra) {
6567
+ const merged = { ...base };
6568
+ for (const [toolName, toolSpec] of Object.entries(extra)) {
6569
+ if (Object.hasOwn(merged, toolName)) {
6570
+ throw new Error(
6571
+ `Duplicate tool name "${toolName}" in runAgentLoop. Rename the custom tool or disable that filesystem tool.`
6572
+ );
6573
+ }
6574
+ merged[toolName] = toolSpec;
6575
+ }
6576
+ return merged;
6577
+ }
3919
6578
  // Annotate the CommonJS export names for ESM import in node:
3920
6579
  0 && (module.exports = {
6580
+ CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
6581
+ CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
6582
+ CODEX_APPLY_PATCH_LARK_GRAMMAR,
6583
+ FIREWORKS_DEFAULT_GLM_MODEL,
6584
+ FIREWORKS_DEFAULT_KIMI_MODEL,
6585
+ FIREWORKS_DEFAULT_MINIMAX_MODEL,
6586
+ FIREWORKS_MODEL_IDS,
6587
+ InMemoryAgentFilesystem,
3921
6588
  LlmJsonCallError,
3922
6589
  appendMarkdownSourcesSection,
6590
+ applyPatch,
3923
6591
  configureGemini,
3924
6592
  convertGooglePartsToLlmParts,
6593
+ createApplyPatchTool,
6594
+ createCodexApplyPatchTool,
6595
+ createCodexFilesystemToolSet,
6596
+ createCodexReadFileTool,
6597
+ createFilesystemToolSetForModel,
6598
+ createGeminiFilesystemToolSet,
6599
+ createGeminiReadFileTool,
6600
+ createGlobTool,
6601
+ createGrepFilesTool,
6602
+ createGrepSearchTool,
6603
+ createInMemoryAgentFilesystem,
6604
+ createListDirTool,
6605
+ createListDirectoryTool,
6606
+ createModelAgnosticFilesystemToolSet,
6607
+ createNodeAgentFilesystem,
6608
+ createReadFilesTool,
6609
+ createReplaceTool,
6610
+ createRgSearchTool,
6611
+ createWriteFileTool,
6612
+ customTool,
3925
6613
  encodeChatGptAuthJson,
3926
6614
  encodeChatGptAuthJsonB64,
3927
6615
  estimateCallCostUsd,
@@ -3932,11 +6620,15 @@ ${lines}`;
3932
6620
  generateText,
3933
6621
  getChatGptAuthProfile,
3934
6622
  getCurrentToolCallContext,
6623
+ isFireworksModelId,
3935
6624
  isGeminiModelId,
3936
6625
  loadEnvFromFile,
3937
6626
  loadLocalEnv,
3938
6627
  parseJsonFromLlmText,
3939
6628
  refreshChatGptOauthToken,
6629
+ resolveFilesystemToolProfile,
6630
+ resolveFireworksModelId,
6631
+ runAgentLoop,
3940
6632
  runToolLoop,
3941
6633
  sanitisePartForLogging,
3942
6634
  streamJson,