@ljoukov/llm 3.0.3 → 3.0.4

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
@@ -1689,6 +1689,66 @@ function parseEventBlock(raw) {
1689
1689
  }
1690
1690
  }
1691
1691
 
1692
+ // src/utils/modelConcurrency.ts
1693
+ var MIN_MODEL_CONCURRENCY_CAP = 1;
1694
+ var MAX_MODEL_CONCURRENCY_CAP = 64;
1695
+ var DEFAULT_MODEL_CONCURRENCY_CAP = 3;
1696
+ function parsePositiveInteger(raw) {
1697
+ if (raw === void 0) {
1698
+ return void 0;
1699
+ }
1700
+ const normalized = raw.trim();
1701
+ if (!normalized) {
1702
+ return void 0;
1703
+ }
1704
+ if (!/^-?\d+$/u.test(normalized)) {
1705
+ return void 0;
1706
+ }
1707
+ const parsed = Number.parseInt(normalized, 10);
1708
+ if (!Number.isFinite(parsed)) {
1709
+ return void 0;
1710
+ }
1711
+ return parsed;
1712
+ }
1713
+ function clampModelConcurrencyCap(value) {
1714
+ if (!Number.isFinite(value)) {
1715
+ return DEFAULT_MODEL_CONCURRENCY_CAP;
1716
+ }
1717
+ const rounded = Math.floor(value);
1718
+ if (rounded < MIN_MODEL_CONCURRENCY_CAP) {
1719
+ return MIN_MODEL_CONCURRENCY_CAP;
1720
+ }
1721
+ if (rounded > MAX_MODEL_CONCURRENCY_CAP) {
1722
+ return MAX_MODEL_CONCURRENCY_CAP;
1723
+ }
1724
+ return rounded;
1725
+ }
1726
+ function normalizeModelIdForEnv(modelId) {
1727
+ return modelId.trim().replace(/[^A-Za-z0-9]+/gu, "_").replace(/^_+|_+$/gu, "").toUpperCase();
1728
+ }
1729
+ function resolveModelConcurrencyCap(options) {
1730
+ const env = options.env ?? process.env;
1731
+ const providerPrefix = options.providerEnvPrefix;
1732
+ const defaultCap = clampModelConcurrencyCap(options.defaultCap ?? DEFAULT_MODEL_CONCURRENCY_CAP);
1733
+ const normalizedModelId = options.modelId ? normalizeModelIdForEnv(options.modelId) : "";
1734
+ const candidateKeys = [
1735
+ ...normalizedModelId ? [
1736
+ `${providerPrefix}_MAX_PARALLEL_REQUESTS_MODEL_${normalizedModelId}`,
1737
+ `LLM_MAX_PARALLEL_REQUESTS_MODEL_${normalizedModelId}`
1738
+ ] : [],
1739
+ `${providerPrefix}_MAX_PARALLEL_REQUESTS_PER_MODEL`,
1740
+ "LLM_MAX_PARALLEL_REQUESTS_PER_MODEL"
1741
+ ];
1742
+ for (const key of candidateKeys) {
1743
+ const parsed = parsePositiveInteger(env[key]);
1744
+ if (parsed === void 0) {
1745
+ continue;
1746
+ }
1747
+ return clampModelConcurrencyCap(parsed);
1748
+ }
1749
+ return defaultCap;
1750
+ }
1751
+
1692
1752
  // src/utils/scheduler.ts
1693
1753
  function sleep(ms) {
1694
1754
  return new Promise((resolve) => {
@@ -1704,13 +1764,72 @@ function toError(value) {
1704
1764
  }
1705
1765
  return new Error("Unknown error");
1706
1766
  }
1767
+ function getStatusCode(error) {
1768
+ if (!error || typeof error !== "object") {
1769
+ return void 0;
1770
+ }
1771
+ const maybe = error;
1772
+ const candidates = [maybe.status, maybe.statusCode];
1773
+ for (const candidate of candidates) {
1774
+ if (typeof candidate === "number") {
1775
+ return candidate;
1776
+ }
1777
+ if (typeof candidate === "string") {
1778
+ const parsed = Number.parseInt(candidate, 10);
1779
+ if (Number.isFinite(parsed)) {
1780
+ return parsed;
1781
+ }
1782
+ }
1783
+ }
1784
+ if (typeof maybe.code === "number") {
1785
+ return maybe.code;
1786
+ }
1787
+ return void 0;
1788
+ }
1789
+ function getErrorText(error) {
1790
+ if (error instanceof Error) {
1791
+ return error.message.toLowerCase();
1792
+ }
1793
+ if (typeof error === "string") {
1794
+ return error.toLowerCase();
1795
+ }
1796
+ if (error && typeof error === "object") {
1797
+ const maybe = error;
1798
+ const code = typeof maybe.code === "string" ? maybe.code : "";
1799
+ const message = typeof maybe.message === "string" ? maybe.message : "";
1800
+ return `${code} ${message}`.trim().toLowerCase();
1801
+ }
1802
+ return "";
1803
+ }
1804
+ function defaultIsOverloadError(error) {
1805
+ const status = getStatusCode(error);
1806
+ if (status === 429 || status === 503 || status === 529) {
1807
+ return true;
1808
+ }
1809
+ const text = getErrorText(error);
1810
+ if (!text) {
1811
+ return false;
1812
+ }
1813
+ return text.includes("rate limit") || text.includes("too many requests") || text.includes("resource exhausted") || text.includes("resource_exhausted") || text.includes("overload");
1814
+ }
1707
1815
  function createCallScheduler(options = {}) {
1708
1816
  const maxParallelRequests = Math.max(1, Math.floor(options.maxParallelRequests ?? 3));
1817
+ const initialParallelRequests = Math.min(
1818
+ maxParallelRequests,
1819
+ Math.max(1, Math.floor(options.initialParallelRequests ?? Math.min(3, maxParallelRequests)))
1820
+ );
1821
+ const increaseAfterConsecutiveSuccesses = Math.max(
1822
+ 1,
1823
+ Math.floor(options.increaseAfterConsecutiveSuccesses ?? 8)
1824
+ );
1709
1825
  const minIntervalBetweenStartMs = Math.max(0, Math.floor(options.minIntervalBetweenStartMs ?? 0));
1710
1826
  const startJitterMs = Math.max(0, Math.floor(options.startJitterMs ?? 0));
1711
1827
  const retryPolicy = options.retry;
1828
+ const isOverloadError2 = options.isOverloadError ?? defaultIsOverloadError;
1712
1829
  let activeCount = 0;
1713
1830
  let lastStartTime = 0;
1831
+ let currentParallelLimit = initialParallelRequests;
1832
+ let consecutiveSuccesses = 0;
1714
1833
  let startSpacingChain = Promise.resolve();
1715
1834
  const queue = [];
1716
1835
  async function applyStartSpacing() {
@@ -1741,6 +1860,10 @@ function createCallScheduler(options = {}) {
1741
1860
  await applyStartSpacing();
1742
1861
  return await fn();
1743
1862
  } catch (error) {
1863
+ if (isOverloadError2(error)) {
1864
+ consecutiveSuccesses = 0;
1865
+ currentParallelLimit = Math.max(1, Math.ceil(currentParallelLimit / 2));
1866
+ }
1744
1867
  const err = toError(error);
1745
1868
  if (!retryPolicy || attempt >= retryPolicy.maxAttempts) {
1746
1869
  throw err;
@@ -1760,7 +1883,7 @@ function createCallScheduler(options = {}) {
1760
1883
  }
1761
1884
  }
1762
1885
  function drainQueue() {
1763
- while (activeCount < maxParallelRequests && queue.length > 0) {
1886
+ while (activeCount < currentParallelLimit && queue.length > 0) {
1764
1887
  const task = queue.shift();
1765
1888
  if (!task) {
1766
1889
  continue;
@@ -1774,6 +1897,11 @@ function createCallScheduler(options = {}) {
1774
1897
  const job = async () => {
1775
1898
  try {
1776
1899
  const result = await attemptWithRetries(fn, 1);
1900
+ consecutiveSuccesses += 1;
1901
+ if (currentParallelLimit < maxParallelRequests && consecutiveSuccesses >= increaseAfterConsecutiveSuccesses) {
1902
+ currentParallelLimit += 1;
1903
+ consecutiveSuccesses = 0;
1904
+ }
1777
1905
  resolve(result);
1778
1906
  } catch (error) {
1779
1907
  reject(toError(error));
@@ -1863,13 +1991,28 @@ function getFireworksClient() {
1863
1991
  }
1864
1992
 
1865
1993
  // src/fireworks/calls.ts
1866
- var scheduler = createCallScheduler({
1867
- maxParallelRequests: 3,
1868
- minIntervalBetweenStartMs: 200,
1869
- startJitterMs: 200
1870
- });
1871
- async function runFireworksCall(fn) {
1872
- return scheduler.run(async () => fn(getFireworksClient()));
1994
+ var DEFAULT_SCHEDULER_KEY = "__default__";
1995
+ var schedulerByModel = /* @__PURE__ */ new Map();
1996
+ function getSchedulerForModel(modelId) {
1997
+ const normalizedModelId = modelId?.trim();
1998
+ const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY;
1999
+ const existing = schedulerByModel.get(schedulerKey);
2000
+ if (existing) {
2001
+ return existing;
2002
+ }
2003
+ const created = createCallScheduler({
2004
+ maxParallelRequests: resolveModelConcurrencyCap({
2005
+ providerEnvPrefix: "FIREWORKS",
2006
+ modelId: normalizedModelId
2007
+ }),
2008
+ minIntervalBetweenStartMs: 200,
2009
+ startJitterMs: 200
2010
+ });
2011
+ schedulerByModel.set(schedulerKey, created);
2012
+ return created;
2013
+ }
2014
+ async function runFireworksCall(fn, modelId) {
2015
+ return getSchedulerForModel(modelId).run(async () => fn(getFireworksClient()));
1873
2016
  }
1874
2017
 
1875
2018
  // src/fireworks/models.ts
@@ -2205,6 +2348,18 @@ function shouldRetry(error) {
2205
2348
  }
2206
2349
  return false;
2207
2350
  }
2351
+ function isOverloadError(error) {
2352
+ const status = getStatus(error);
2353
+ if (status === 429 || status === 503 || status === 529) {
2354
+ return true;
2355
+ }
2356
+ const reason = getErrorReason(error);
2357
+ if (reason && RATE_LIMIT_REASONS.has(reason)) {
2358
+ return true;
2359
+ }
2360
+ const message = getErrorMessage(error).toLowerCase();
2361
+ return message.includes("rate limit") || message.includes("too many requests") || message.includes("resource exhausted") || message.includes("resource_exhausted");
2362
+ }
2208
2363
  function retryDelayMs(attempt) {
2209
2364
  const baseRetryDelayMs = 500;
2210
2365
  const maxRetryDelayMs = 4e3;
@@ -2212,23 +2367,39 @@ function retryDelayMs(attempt) {
2212
2367
  const jitter = Math.floor(Math.random() * 200);
2213
2368
  return base + jitter;
2214
2369
  }
2215
- var scheduler2 = createCallScheduler({
2216
- maxParallelRequests: 3,
2217
- minIntervalBetweenStartMs: 200,
2218
- startJitterMs: 200,
2219
- retry: {
2220
- maxAttempts: 3,
2221
- getDelayMs: (attempt, error) => {
2222
- if (!shouldRetry(error)) {
2223
- return null;
2370
+ var DEFAULT_SCHEDULER_KEY2 = "__default__";
2371
+ var schedulerByModel2 = /* @__PURE__ */ new Map();
2372
+ function getSchedulerForModel2(modelId) {
2373
+ const normalizedModelId = modelId?.trim();
2374
+ const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY2;
2375
+ const existing = schedulerByModel2.get(schedulerKey);
2376
+ if (existing) {
2377
+ return existing;
2378
+ }
2379
+ const created = createCallScheduler({
2380
+ maxParallelRequests: resolveModelConcurrencyCap({
2381
+ providerEnvPrefix: "GOOGLE",
2382
+ modelId: normalizedModelId
2383
+ }),
2384
+ minIntervalBetweenStartMs: 200,
2385
+ startJitterMs: 200,
2386
+ isOverloadError,
2387
+ retry: {
2388
+ maxAttempts: 3,
2389
+ getDelayMs: (attempt, error) => {
2390
+ if (!shouldRetry(error)) {
2391
+ return null;
2392
+ }
2393
+ const hintedDelay = getRetryAfterMs(error);
2394
+ return hintedDelay ?? retryDelayMs(attempt);
2224
2395
  }
2225
- const hintedDelay = getRetryAfterMs(error);
2226
- return hintedDelay ?? retryDelayMs(attempt);
2227
2396
  }
2228
- }
2229
- });
2230
- async function runGeminiCall(fn) {
2231
- return scheduler2.run(async () => fn(await getGeminiClient()));
2397
+ });
2398
+ schedulerByModel2.set(schedulerKey, created);
2399
+ return created;
2400
+ }
2401
+ async function runGeminiCall(fn, modelId) {
2402
+ return getSchedulerForModel2(modelId).run(async () => fn(await getGeminiClient()));
2232
2403
  }
2233
2404
 
2234
2405
  // src/openai/client.ts
@@ -2389,13 +2560,28 @@ function getOpenAiClient() {
2389
2560
 
2390
2561
  // src/openai/calls.ts
2391
2562
  var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
2392
- var scheduler3 = createCallScheduler({
2393
- maxParallelRequests: 3,
2394
- minIntervalBetweenStartMs: 200,
2395
- startJitterMs: 200
2396
- });
2397
- async function runOpenAiCall(fn) {
2398
- return scheduler3.run(async () => fn(getOpenAiClient()));
2563
+ var DEFAULT_SCHEDULER_KEY3 = "__default__";
2564
+ var schedulerByModel3 = /* @__PURE__ */ new Map();
2565
+ function getSchedulerForModel3(modelId) {
2566
+ const normalizedModelId = modelId?.trim();
2567
+ const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY3;
2568
+ const existing = schedulerByModel3.get(schedulerKey);
2569
+ if (existing) {
2570
+ return existing;
2571
+ }
2572
+ const created = createCallScheduler({
2573
+ maxParallelRequests: resolveModelConcurrencyCap({
2574
+ providerEnvPrefix: "OPENAI",
2575
+ modelId: normalizedModelId
2576
+ }),
2577
+ minIntervalBetweenStartMs: 200,
2578
+ startJitterMs: 200
2579
+ });
2580
+ schedulerByModel3.set(schedulerKey, created);
2581
+ return created;
2582
+ }
2583
+ async function runOpenAiCall(fn, modelId) {
2584
+ return getSchedulerForModel3(modelId).run(async () => fn(getOpenAiClient()));
2399
2585
  }
2400
2586
 
2401
2587
  // src/openai/models.ts
@@ -4157,7 +4343,7 @@ async function runTextCall(params) {
4157
4343
  }
4158
4344
  }
4159
4345
  }
4160
- });
4346
+ }, modelForProvider);
4161
4347
  } else if (provider === "chatgpt") {
4162
4348
  const chatGptInput = toChatGptInput(contents);
4163
4349
  const reasoningEffort = resolveOpenAiReasoningEffort(
@@ -4252,7 +4438,7 @@ async function runTextCall(params) {
4252
4438
  pushDelta("response", textOutput);
4253
4439
  }
4254
4440
  latestUsage = extractFireworksUsageTokens(response.usage);
4255
- });
4441
+ }, modelForProvider);
4256
4442
  } else {
4257
4443
  const geminiContents = contents.map(convertLlmContentToGeminiContent);
4258
4444
  const config = {
@@ -4320,7 +4506,7 @@ async function runTextCall(params) {
4320
4506
  }
4321
4507
  }
4322
4508
  grounding = latestGrounding;
4323
- });
4509
+ }, modelForProvider);
4324
4510
  }
4325
4511
  const mergedParts = mergeConsecutiveTextParts(responseParts);
4326
4512
  const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
@@ -4747,7 +4933,7 @@ async function runToolLoop(request) {
4747
4933
  }
4748
4934
  }
4749
4935
  return await stream.finalResponse();
4750
- });
4936
+ }, providerInfo.model);
4751
4937
  modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
4752
4938
  emitEvent({ type: "model", modelVersion });
4753
4939
  if (finalResponse.error) {
@@ -5023,7 +5209,7 @@ async function runToolLoop(request) {
5023
5209
  },
5024
5210
  { signal: request.signal }
5025
5211
  );
5026
- });
5212
+ }, providerInfo.model);
5027
5213
  const modelVersion = typeof response.model === "string" ? response.model : request.model;
5028
5214
  request.onEvent?.({ type: "model", modelVersion });
5029
5215
  const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
@@ -5220,7 +5406,7 @@ async function runToolLoop(request) {
5220
5406
  usageMetadata: latestUsageMetadata,
5221
5407
  modelVersion: resolvedModelVersion ?? request.model
5222
5408
  };
5223
- });
5409
+ }, request.model);
5224
5410
  const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
5225
5411
  const modelVersion = response.modelVersion ?? request.model;
5226
5412
  const stepCostUsd = estimateCallCostUsd({
@@ -5558,13 +5744,630 @@ function appendMarkdownSourcesSection(value, sources) {
5558
5744
  ${lines}`;
5559
5745
  }
5560
5746
 
5747
+ // src/agent/subagents.ts
5748
+ var import_node_crypto2 = require("crypto");
5749
+ var import_zod4 = require("zod");
5750
+ var DEFAULT_SUBAGENT_MAX_AGENTS = 4;
5751
+ var DEFAULT_SUBAGENT_MAX_DEPTH = 2;
5752
+ var DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS = 1500;
5753
+ var DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS = 9e4;
5754
+ var MAX_SUBAGENT_MAX_AGENTS = 64;
5755
+ var MAX_SUBAGENT_MAX_DEPTH = 12;
5756
+ var MAX_SUBAGENT_MAX_STEPS = 64;
5757
+ var MAX_SUBAGENT_WAIT_TIMEOUT_MS = 6e5;
5758
+ var SUBAGENT_CONTROL_TOOL_NAMES = ["send_input", "resume_agent", "wait", "close_agent"];
5759
+ var subagentInputItemSchema = import_zod4.z.object({
5760
+ text: import_zod4.z.string().optional(),
5761
+ image_url: import_zod4.z.string().optional(),
5762
+ name: import_zod4.z.string().optional(),
5763
+ path: import_zod4.z.string().optional(),
5764
+ type: import_zod4.z.string().optional()
5765
+ }).passthrough();
5766
+ var spawnAgentInputSchema = import_zod4.z.object({
5767
+ prompt: import_zod4.z.string().optional().describe("Initial prompt for the subagent."),
5768
+ message: import_zod4.z.string().optional().describe("Codex-style alias for prompt."),
5769
+ items: import_zod4.z.array(subagentInputItemSchema).optional().describe("Optional Codex-style input items."),
5770
+ agent_type: import_zod4.z.string().optional().describe("Codex-style agent type hint."),
5771
+ instructions: import_zod4.z.string().optional().describe("Optional extra instructions for this subagent instance."),
5772
+ model: import_zod4.z.string().optional().describe("Optional model override. Must be one of this package's supported text model ids."),
5773
+ max_steps: import_zod4.z.number().int().min(1).max(MAX_SUBAGENT_MAX_STEPS).optional().describe("Optional max step budget for each subagent run.")
5774
+ }).refine((value) => Boolean(resolvePromptValue(value.prompt, value.message, value.items)), {
5775
+ message: "Either prompt, message, or items must contain non-empty input."
5776
+ });
5777
+ var sendInputSchema = import_zod4.z.object({
5778
+ agent_id: import_zod4.z.string().optional().describe("Target subagent id."),
5779
+ id: import_zod4.z.string().optional().describe("Codex-style alias for agent_id."),
5780
+ input: import_zod4.z.string().optional().describe("New user input queued for the subagent."),
5781
+ message: import_zod4.z.string().optional().describe("Codex-style alias for input."),
5782
+ items: import_zod4.z.array(subagentInputItemSchema).optional().describe("Optional Codex-style input items."),
5783
+ interrupt: import_zod4.z.boolean().optional().describe("If true and currently running, aborts active run before queuing input.")
5784
+ }).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
5785
+ message: "agent_id (or id) is required."
5786
+ }).refine((value) => Boolean(resolvePromptValue(value.input, value.message, value.items)), {
5787
+ message: "input (or message/items) is required."
5788
+ });
5789
+ var resumeAgentSchema = import_zod4.z.object({
5790
+ agent_id: import_zod4.z.string().optional().describe("Target subagent id."),
5791
+ id: import_zod4.z.string().optional().describe("Codex-style alias for agent_id.")
5792
+ }).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
5793
+ message: "agent_id (or id) is required."
5794
+ });
5795
+ var waitSchema = import_zod4.z.object({
5796
+ agent_id: import_zod4.z.string().optional().describe("Target subagent id."),
5797
+ id: import_zod4.z.string().optional().describe("Codex-style alias for agent_id."),
5798
+ ids: import_zod4.z.array(import_zod4.z.string().min(1)).optional().describe("Codex-style list of agent ids."),
5799
+ timeout_ms: import_zod4.z.number().int().min(1).optional().describe("Optional wait timeout in milliseconds.")
5800
+ }).refine(
5801
+ (value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)) || Array.isArray(value.ids) && value.ids.length > 0,
5802
+ {
5803
+ message: "agent_id/id or ids is required."
5804
+ }
5805
+ );
5806
+ var closeSchema = import_zod4.z.object({
5807
+ agent_id: import_zod4.z.string().optional().describe("Target subagent id."),
5808
+ id: import_zod4.z.string().optional().describe("Codex-style alias for agent_id.")
5809
+ }).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
5810
+ message: "agent_id (or id) is required."
5811
+ });
5812
+ function resolveSubagentToolConfig(selection, currentDepth) {
5813
+ const defaults = {
5814
+ maxAgents: DEFAULT_SUBAGENT_MAX_AGENTS,
5815
+ maxDepth: DEFAULT_SUBAGENT_MAX_DEPTH,
5816
+ defaultWaitTimeoutMs: DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS,
5817
+ maxWaitTimeoutMs: DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS,
5818
+ promptPattern: "codex",
5819
+ inheritTools: true,
5820
+ inheritFilesystemTool: true
5821
+ };
5822
+ if (selection === void 0 || selection === false) {
5823
+ return {
5824
+ enabled: false,
5825
+ ...defaults
5826
+ };
5827
+ }
5828
+ const config = selection === true ? {} : selection;
5829
+ const maxAgents = normalizeInteger(
5830
+ config.maxAgents,
5831
+ defaults.maxAgents,
5832
+ 1,
5833
+ MAX_SUBAGENT_MAX_AGENTS
5834
+ );
5835
+ const maxDepth = normalizeInteger(config.maxDepth, defaults.maxDepth, 1, MAX_SUBAGENT_MAX_DEPTH);
5836
+ const defaultWaitTimeoutMs = normalizeInteger(
5837
+ config.defaultWaitTimeoutMs,
5838
+ defaults.defaultWaitTimeoutMs,
5839
+ 1,
5840
+ MAX_SUBAGENT_WAIT_TIMEOUT_MS
5841
+ );
5842
+ const maxWaitTimeoutMs = normalizeInteger(
5843
+ config.maxWaitTimeoutMs,
5844
+ defaults.maxWaitTimeoutMs,
5845
+ defaultWaitTimeoutMs,
5846
+ MAX_SUBAGENT_WAIT_TIMEOUT_MS
5847
+ );
5848
+ const promptPattern = config.promptPattern ?? defaults.promptPattern;
5849
+ const instructions = trimToUndefined(config.instructions);
5850
+ const maxSteps = normalizeOptionalInteger(config.maxSteps, 1, MAX_SUBAGENT_MAX_STEPS);
5851
+ const enabled = config.enabled !== false && currentDepth < maxDepth;
5852
+ return {
5853
+ enabled,
5854
+ maxAgents,
5855
+ maxDepth,
5856
+ defaultWaitTimeoutMs,
5857
+ maxWaitTimeoutMs,
5858
+ promptPattern,
5859
+ ...instructions ? { instructions } : {},
5860
+ ...config.model ? { model: config.model } : {},
5861
+ ...maxSteps ? { maxSteps } : {},
5862
+ inheritTools: config.inheritTools !== false,
5863
+ inheritFilesystemTool: config.inheritFilesystemTool !== false
5864
+ };
5865
+ }
5866
+ function buildCodexSubagentOrchestratorInstructions(params) {
5867
+ return [
5868
+ "Subagent orchestration tools are available: spawn_agent, send_input, resume_agent, wait, close_agent.",
5869
+ "Use this control pattern:",
5870
+ "1. spawn_agent with a focused prompt.",
5871
+ "2. wait on that agent_id until it is no longer running.",
5872
+ "3. For follow-up turns, send_input then resume_agent.",
5873
+ "4. close_agent when delegation is complete.",
5874
+ `Limits: max active subagents ${params.maxAgents}, max depth ${params.maxDepth}, current depth ${params.currentDepth}.`
5875
+ ].join("\n");
5876
+ }
5877
+ function buildCodexSubagentWorkerInstructions(params) {
5878
+ return [
5879
+ `You are a delegated subagent at depth ${params.depth}/${params.maxDepth}.`,
5880
+ "Focus on the delegated task, use available tools when needed, and return concise actionable output.",
5881
+ "If blocked, report the blocker explicitly."
5882
+ ].join("\n");
5883
+ }
5884
+ function createSubagentToolController(options) {
5885
+ if (!options.config.enabled) {
5886
+ return {
5887
+ tools: {},
5888
+ closeAll: async () => {
5889
+ }
5890
+ };
5891
+ }
5892
+ const agents = /* @__PURE__ */ new Map();
5893
+ const tools = {
5894
+ spawn_agent: tool({
5895
+ description: "Spawns a subagent asynchronously. Returns immediately with agent status and id.",
5896
+ inputSchema: spawnAgentInputSchema,
5897
+ execute: async (input) => {
5898
+ if (countActiveAgents(agents) >= options.config.maxAgents) {
5899
+ throw new Error(
5900
+ `Subagent limit reached (${options.config.maxAgents}). Close existing agents before spawning new ones.`
5901
+ );
5902
+ }
5903
+ const childDepth = options.parentDepth + 1;
5904
+ if (childDepth > options.config.maxDepth) {
5905
+ throw new Error(
5906
+ `Subagent depth limit reached (${options.config.maxDepth}). Cannot spawn at depth ${childDepth}.`
5907
+ );
5908
+ }
5909
+ let model = options.config.model ?? options.parentModel;
5910
+ if (input.model) {
5911
+ if (!isLlmTextModelId(input.model)) {
5912
+ throw new Error(`Unsupported subagent model id: ${input.model}`);
5913
+ }
5914
+ model = input.model;
5915
+ }
5916
+ const id = `agent_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}`;
5917
+ const now = Date.now();
5918
+ const initialPrompt = resolvePromptValue(input.prompt, input.message, input.items);
5919
+ if (!initialPrompt) {
5920
+ throw new Error("spawn_agent requires prompt/message/items with non-empty text.");
5921
+ }
5922
+ const agent = {
5923
+ id,
5924
+ depth: childDepth,
5925
+ model,
5926
+ status: "idle",
5927
+ createdAtMs: now,
5928
+ updatedAtMs: now,
5929
+ pendingInputs: [initialPrompt],
5930
+ history: [],
5931
+ ...options.buildChildInstructions ? {
5932
+ instructions: trimToUndefined(
5933
+ options.buildChildInstructions(input.instructions, childDepth)
5934
+ )
5935
+ } : input.instructions ? { instructions: trimToUndefined(input.instructions) } : {},
5936
+ ...input.max_steps ? { maxSteps: input.max_steps } : options.config.maxSteps ? { maxSteps: options.config.maxSteps } : {},
5937
+ turns: 0,
5938
+ notification: "spawned",
5939
+ notificationMessage: `Spawned subagent ${id}.`,
5940
+ version: 1,
5941
+ waiters: /* @__PURE__ */ new Set()
5942
+ };
5943
+ agents.set(id, agent);
5944
+ startRun(agent, options);
5945
+ return buildToolResponse(agent, {
5946
+ notification: "spawned",
5947
+ message: `Spawned subagent ${id}.`
5948
+ });
5949
+ }
5950
+ }),
5951
+ send_input: tool({
5952
+ description: "Queues new input for an existing subagent.",
5953
+ inputSchema: sendInputSchema,
5954
+ execute: async (input) => {
5955
+ const agentId = resolveAgentIdValue(input.agent_id, input.id);
5956
+ if (!agentId) {
5957
+ throw new Error("send_input requires agent_id or id.");
5958
+ }
5959
+ const agent = requireAgent(agents, agentId);
5960
+ const nextInput = resolvePromptValue(input.input, input.message, input.items);
5961
+ if (!nextInput) {
5962
+ throw new Error("send_input requires input/message/items with non-empty text.");
5963
+ }
5964
+ if (agent.status === "closed") {
5965
+ throw new Error(`Subagent ${agent.id} is closed.`);
5966
+ }
5967
+ if (input.interrupt && agent.abortController) {
5968
+ agent.abortController.abort("send_input_interrupt");
5969
+ agent.pendingInputs.unshift(nextInput);
5970
+ setNotification(agent, "input_queued", `Interrupted ${agent.id} and queued new input.`);
5971
+ return buildToolResponse(agent);
5972
+ }
5973
+ agent.pendingInputs.push(nextInput);
5974
+ setNotification(agent, "input_queued", `Queued input for ${agent.id}.`);
5975
+ return buildToolResponse(agent);
5976
+ }
5977
+ }),
5978
+ resume_agent: tool({
5979
+ description: "Resumes a subagent run when queued input is available.",
5980
+ inputSchema: resumeAgentSchema,
5981
+ execute: async (input) => {
5982
+ const agentId = resolveAgentIdValue(input.agent_id, input.id);
5983
+ if (!agentId) {
5984
+ throw new Error("resume_agent requires agent_id or id.");
5985
+ }
5986
+ const agent = requireAgent(agents, agentId);
5987
+ if (agent.status === "closed") {
5988
+ setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
5989
+ return buildToolResponse(agent, {
5990
+ notification: "already_closed",
5991
+ message: `Subagent ${agent.id} is already closed.`
5992
+ });
5993
+ }
5994
+ const outcome = startRun(agent, options);
5995
+ if (outcome === "started") {
5996
+ return buildToolResponse(agent, {
5997
+ notification: "run_started",
5998
+ message: `Started subagent ${agent.id}.`
5999
+ });
6000
+ }
6001
+ if (outcome === "already_running") {
6002
+ setNotification(agent, "already_running", `Subagent ${agent.id} is already running.`);
6003
+ return buildToolResponse(agent);
6004
+ }
6005
+ setNotification(agent, "no_pending_input", `Subagent ${agent.id} has no queued input.`);
6006
+ return buildToolResponse(agent);
6007
+ }
6008
+ }),
6009
+ wait: tool({
6010
+ description: "Waits for a running subagent to change state or until timeout. Returns current status.",
6011
+ inputSchema: waitSchema,
6012
+ execute: async (input) => {
6013
+ const usesIdsArray = Array.isArray(input.ids) && input.ids.length > 0;
6014
+ const ids = resolveAgentIdList(input.agent_id, input.id, input.ids);
6015
+ if (ids.length === 0) {
6016
+ throw new Error("wait requires agent_id/id or ids.");
6017
+ }
6018
+ const timeoutMs = normalizeInteger(
6019
+ input.timeout_ms,
6020
+ options.config.defaultWaitTimeoutMs,
6021
+ 1,
6022
+ options.config.maxWaitTimeoutMs
6023
+ );
6024
+ if (usesIdsArray) {
6025
+ const status = await waitForAnyAgentStatus(agents, ids, timeoutMs);
6026
+ return { status, timed_out: Object.keys(status).length === 0, timeout_ms: timeoutMs };
6027
+ }
6028
+ const agent = requireAgent(agents, ids[0]);
6029
+ if (agent.status === "running") {
6030
+ const completed = await waitUntilNotRunning(agent, timeoutMs);
6031
+ if (!completed) {
6032
+ setNotification(
6033
+ agent,
6034
+ "timeout",
6035
+ `Timed out after ${timeoutMs}ms while waiting for ${agent.id}.`
6036
+ );
6037
+ return buildToolResponse(agent, void 0, { timed_out: true, timeout_ms: timeoutMs });
6038
+ }
6039
+ }
6040
+ return buildToolResponse(agent, void 0, { timed_out: false, timeout_ms: timeoutMs });
6041
+ }
6042
+ }),
6043
+ close_agent: tool({
6044
+ description: "Closes a subagent and aborts its current run if it is still running.",
6045
+ inputSchema: closeSchema,
6046
+ execute: async (input) => {
6047
+ const agentId = resolveAgentIdValue(input.agent_id, input.id);
6048
+ if (!agentId) {
6049
+ throw new Error("close_agent requires agent_id or id.");
6050
+ }
6051
+ const agent = requireAgent(agents, agentId);
6052
+ if (agent.status === "closed") {
6053
+ setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
6054
+ return buildToolResponse(agent, void 0, { cancelled: false });
6055
+ }
6056
+ const cancelled = closeSubagent(agent, `Closed ${agent.id}.`);
6057
+ return buildToolResponse(
6058
+ agent,
6059
+ { notification: "closed", message: `Closed ${agent.id}.` },
6060
+ { cancelled }
6061
+ );
6062
+ }
6063
+ })
6064
+ };
6065
+ return {
6066
+ tools,
6067
+ closeAll: async () => {
6068
+ const running = [];
6069
+ for (const agent of agents.values()) {
6070
+ if (agent.status !== "closed") {
6071
+ closeSubagent(agent, `Parent agent loop closed ${agent.id}.`);
6072
+ }
6073
+ if (agent.runningPromise) {
6074
+ running.push(agent.runningPromise);
6075
+ }
6076
+ }
6077
+ if (running.length > 0) {
6078
+ await Promise.race([Promise.allSettled(running), sleep2(2e3)]);
6079
+ }
6080
+ }
6081
+ };
6082
+ }
6083
+ function requireAgent(agents, id) {
6084
+ const agent = agents.get(id);
6085
+ if (!agent) {
6086
+ throw new Error(`Unknown subagent id: ${id}`);
6087
+ }
6088
+ return agent;
6089
+ }
6090
+ function resolveAgentIdValue(agentId, idAlias) {
6091
+ const preferred = agentId?.trim();
6092
+ if (preferred) {
6093
+ return preferred;
6094
+ }
6095
+ const alias = idAlias?.trim();
6096
+ return alias ?? "";
6097
+ }
6098
+ function resolveAgentIdList(agentId, idAlias, ids) {
6099
+ if (Array.isArray(ids) && ids.length > 0) {
6100
+ return [...new Set(ids.map((value) => value.trim()).filter(Boolean))];
6101
+ }
6102
+ const single = resolveAgentIdValue(agentId, idAlias);
6103
+ return single ? [single] : [];
6104
+ }
6105
+ function resolvePromptValue(prompt, message, items) {
6106
+ const promptValue = prompt?.trim();
6107
+ if (promptValue) {
6108
+ return promptValue;
6109
+ }
6110
+ const messageValue = message?.trim();
6111
+ if (messageValue) {
6112
+ return messageValue;
6113
+ }
6114
+ const itemText = resolveInputItemsText(items);
6115
+ return itemText ?? "";
6116
+ }
6117
+ function resolveInputItemsText(items) {
6118
+ if (!items || items.length === 0) {
6119
+ return void 0;
6120
+ }
6121
+ const lines = [];
6122
+ for (const item of items) {
6123
+ if (typeof item.text === "string" && item.text.trim().length > 0) {
6124
+ lines.push(item.text.trim());
6125
+ continue;
6126
+ }
6127
+ const itemType = typeof item.type === "string" ? item.type.trim() : "";
6128
+ const name = typeof item.name === "string" ? item.name.trim() : "";
6129
+ const path6 = typeof item.path === "string" ? item.path.trim() : "";
6130
+ const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
6131
+ const compact = [itemType, name, path6 || imageUrl].filter(Boolean).join(" ");
6132
+ if (compact) {
6133
+ lines.push(compact);
6134
+ }
6135
+ }
6136
+ if (lines.length === 0) {
6137
+ return void 0;
6138
+ }
6139
+ return lines.join("\n");
6140
+ }
6141
+ function countActiveAgents(agents) {
6142
+ let count = 0;
6143
+ for (const agent of agents.values()) {
6144
+ if (agent.status !== "closed") {
6145
+ count += 1;
6146
+ }
6147
+ }
6148
+ return count;
6149
+ }
6150
+ async function waitForAnyAgentStatus(agents, ids, timeoutMs) {
6151
+ const requested = ids.map((id) => requireAgent(agents, id));
6152
+ const deadline = Date.now() + timeoutMs;
6153
+ while (true) {
6154
+ const status = {};
6155
+ for (const agent of requested) {
6156
+ if (agent.status !== "running") {
6157
+ status[agent.id] = buildSnapshot(agent);
6158
+ }
6159
+ }
6160
+ if (Object.keys(status).length > 0) {
6161
+ return status;
6162
+ }
6163
+ const remaining = deadline - Date.now();
6164
+ if (remaining <= 0) {
6165
+ return {};
6166
+ }
6167
+ await Promise.race(
6168
+ requested.map(async (agent) => {
6169
+ const changed = await waitForVersionChange(agent, agent.version, remaining);
6170
+ if (!changed) {
6171
+ return;
6172
+ }
6173
+ })
6174
+ );
6175
+ }
6176
+ }
6177
+ function setNotification(agent, notification, message) {
6178
+ agent.notification = notification;
6179
+ agent.notificationMessage = message;
6180
+ agent.updatedAtMs = Date.now();
6181
+ agent.version += 1;
6182
+ notifyWaiters(agent);
6183
+ }
6184
+ function setLifecycle(agent, status, notification, message) {
6185
+ agent.status = status;
6186
+ setNotification(agent, notification, message);
6187
+ }
6188
+ function notifyWaiters(agent) {
6189
+ if (agent.waiters.size === 0) {
6190
+ return;
6191
+ }
6192
+ const waiters = [...agent.waiters];
6193
+ agent.waiters.clear();
6194
+ for (const notify of waiters) {
6195
+ notify();
6196
+ }
6197
+ }
6198
+ function startRun(agent, options) {
6199
+ if (agent.runningPromise) {
6200
+ return "already_running";
6201
+ }
6202
+ const nextInput = agent.pendingInputs.shift();
6203
+ if (!nextInput) {
6204
+ return "no_pending_input";
6205
+ }
6206
+ const input = [...agent.history, { role: "user", content: nextInput }];
6207
+ const abortController = new AbortController();
6208
+ agent.abortController = abortController;
6209
+ agent.lastError = void 0;
6210
+ setLifecycle(
6211
+ agent,
6212
+ "running",
6213
+ "run_started",
6214
+ `Subagent ${agent.id} started run ${agent.turns + 1}.`
6215
+ );
6216
+ const runPromise = (async () => {
6217
+ try {
6218
+ const result = await options.runSubagent({
6219
+ agentId: agent.id,
6220
+ depth: agent.depth,
6221
+ model: agent.model,
6222
+ input,
6223
+ instructions: agent.instructions,
6224
+ maxSteps: agent.maxSteps,
6225
+ signal: abortController.signal
6226
+ });
6227
+ if (agent.status === "closed") {
6228
+ return;
6229
+ }
6230
+ agent.lastResult = result;
6231
+ agent.lastError = void 0;
6232
+ agent.turns += 1;
6233
+ agent.history = [...input, { role: "assistant", content: result.text }];
6234
+ setLifecycle(
6235
+ agent,
6236
+ "idle",
6237
+ "run_completed",
6238
+ `Subagent ${agent.id} completed run ${agent.turns}.`
6239
+ );
6240
+ } catch (error) {
6241
+ if (agent.status === "closed") {
6242
+ return;
6243
+ }
6244
+ if (abortController.signal.aborted) {
6245
+ setLifecycle(agent, "idle", "input_queued", `Subagent ${agent.id} run interrupted.`);
6246
+ return;
6247
+ }
6248
+ const message = toErrorMessage(error);
6249
+ agent.lastError = message;
6250
+ setLifecycle(agent, "failed", "run_failed", `Subagent ${agent.id} failed: ${message}`);
6251
+ } finally {
6252
+ agent.runningPromise = void 0;
6253
+ agent.abortController = void 0;
6254
+ }
6255
+ })();
6256
+ agent.runningPromise = runPromise;
6257
+ return "started";
6258
+ }
6259
+ function closeSubagent(agent, message) {
6260
+ const cancelled = Boolean(agent.runningPromise);
6261
+ agent.pendingInputs = [];
6262
+ if (agent.abortController) {
6263
+ agent.abortController.abort("close_agent");
6264
+ }
6265
+ setLifecycle(agent, "closed", "closed", message);
6266
+ return cancelled;
6267
+ }
6268
+ async function waitUntilNotRunning(agent, timeoutMs) {
6269
+ const deadline = Date.now() + timeoutMs;
6270
+ while (agent.status === "running") {
6271
+ const remaining = deadline - Date.now();
6272
+ if (remaining <= 0) {
6273
+ return false;
6274
+ }
6275
+ const currentVersion = agent.version;
6276
+ const changed = await waitForVersionChange(agent, currentVersion, remaining);
6277
+ if (!changed) {
6278
+ return false;
6279
+ }
6280
+ }
6281
+ return true;
6282
+ }
6283
+ async function waitForVersionChange(agent, version, timeoutMs) {
6284
+ if (agent.version !== version) {
6285
+ return true;
6286
+ }
6287
+ return await new Promise((resolve) => {
6288
+ const waiter = () => {
6289
+ cleanup();
6290
+ resolve(true);
6291
+ };
6292
+ const timeout = setTimeout(() => {
6293
+ cleanup();
6294
+ resolve(false);
6295
+ }, timeoutMs);
6296
+ const cleanup = () => {
6297
+ clearTimeout(timeout);
6298
+ agent.waiters.delete(waiter);
6299
+ };
6300
+ agent.waiters.add(waiter);
6301
+ });
6302
+ }
6303
+ function buildToolResponse(agent, override, extra = {}) {
6304
+ const notification = override?.notification ?? agent.notification;
6305
+ const message = override?.message ?? agent.notificationMessage;
6306
+ const snapshot = buildSnapshot(agent);
6307
+ return {
6308
+ agent_id: snapshot.agent_id,
6309
+ notification,
6310
+ message,
6311
+ status: snapshot.status,
6312
+ agent: snapshot,
6313
+ tool_availability: snapshot.status === "closed" ? [] : [...SUBAGENT_CONTROL_TOOL_NAMES],
6314
+ ...extra
6315
+ };
6316
+ }
6317
+ function buildSnapshot(agent) {
6318
+ return {
6319
+ agent_id: agent.id,
6320
+ status: agent.status,
6321
+ depth: agent.depth,
6322
+ model: agent.model,
6323
+ pending_inputs: agent.pendingInputs.length,
6324
+ turns: agent.turns,
6325
+ created_at: new Date(agent.createdAtMs).toISOString(),
6326
+ updated_at: new Date(agent.updatedAtMs).toISOString(),
6327
+ ...agent.lastError ? { last_error: agent.lastError } : {},
6328
+ ...agent.lastResult ? {
6329
+ last_result: {
6330
+ text: agent.lastResult.text,
6331
+ thoughts: agent.lastResult.thoughts,
6332
+ step_count: agent.lastResult.steps.length,
6333
+ total_cost_usd: agent.lastResult.totalCostUsd
6334
+ }
6335
+ } : {}
6336
+ };
6337
+ }
6338
+ function normalizeInteger(value, fallback, min, max) {
6339
+ const parsed = Number.isFinite(value) ? Math.floor(value) : fallback;
6340
+ return Math.max(min, Math.min(max, parsed));
6341
+ }
6342
+ function normalizeOptionalInteger(value, min, max) {
6343
+ if (!Number.isFinite(value)) {
6344
+ return void 0;
6345
+ }
6346
+ return Math.max(min, Math.min(max, Math.floor(value)));
6347
+ }
6348
+ function trimToUndefined(value) {
6349
+ const trimmed = value?.trim();
6350
+ return trimmed && trimmed.length > 0 ? trimmed : void 0;
6351
+ }
6352
+ function toErrorMessage(error) {
6353
+ if (error instanceof Error) {
6354
+ return error.message;
6355
+ }
6356
+ return String(error);
6357
+ }
6358
+ function sleep2(ms) {
6359
+ return new Promise((resolve) => {
6360
+ setTimeout(resolve, ms);
6361
+ });
6362
+ }
6363
+
5561
6364
  // src/tools/filesystemTools.ts
5562
6365
  var import_node_path5 = __toESM(require("path"), 1);
5563
- var import_zod5 = require("zod");
6366
+ var import_zod6 = require("zod");
5564
6367
 
5565
6368
  // src/tools/applyPatch.ts
5566
6369
  var import_node_path4 = __toESM(require("path"), 1);
5567
- var import_zod4 = require("zod");
6370
+ var import_zod5 = require("zod");
5568
6371
 
5569
6372
  // src/tools/filesystem.ts
5570
6373
  var import_node_fs3 = require("fs");
@@ -5860,8 +6663,8 @@ var CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION = [
5860
6663
  "- You must prefix new lines with `+` even when creating a new file",
5861
6664
  "- File references can only be relative, NEVER ABSOLUTE."
5862
6665
  ].join("\n");
5863
- var applyPatchToolInputSchema = import_zod4.z.object({
5864
- input: import_zod4.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
6666
+ var applyPatchToolInputSchema = import_zod5.z.object({
6667
+ input: import_zod5.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
5865
6668
  });
5866
6669
  function createApplyPatchTool(options = {}) {
5867
6670
  return tool({
@@ -6269,100 +7072,100 @@ var MAX_GREP_LIMIT = 2e3;
6269
7072
  var DEFAULT_MAX_LINE_LENGTH = 500;
6270
7073
  var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
6271
7074
  var DEFAULT_TAB_WIDTH = 4;
6272
- var codexReadFileInputSchema = import_zod5.z.object({
6273
- file_path: import_zod5.z.string().min(1).describe("Absolute path to the file"),
6274
- offset: import_zod5.z.number().int().min(1).optional().describe("The line number to start reading from. Must be 1 or greater."),
6275
- limit: import_zod5.z.number().int().min(1).optional().describe("The maximum number of lines to return."),
6276
- mode: import_zod5.z.enum(["slice", "indentation"]).optional().describe('Optional mode selector: "slice" (default) or "indentation".'),
6277
- indentation: import_zod5.z.object({
6278
- anchor_line: import_zod5.z.number().int().min(1).optional(),
6279
- max_levels: import_zod5.z.number().int().min(0).optional(),
6280
- include_siblings: import_zod5.z.boolean().optional(),
6281
- include_header: import_zod5.z.boolean().optional(),
6282
- max_lines: import_zod5.z.number().int().min(1).optional()
7075
+ var codexReadFileInputSchema = import_zod6.z.object({
7076
+ file_path: import_zod6.z.string().min(1).describe("Absolute path to the file"),
7077
+ offset: import_zod6.z.number().int().min(1).optional().describe("The line number to start reading from. Must be 1 or greater."),
7078
+ limit: import_zod6.z.number().int().min(1).optional().describe("The maximum number of lines to return."),
7079
+ mode: import_zod6.z.enum(["slice", "indentation"]).optional().describe('Optional mode selector: "slice" (default) or "indentation".'),
7080
+ indentation: import_zod6.z.object({
7081
+ anchor_line: import_zod6.z.number().int().min(1).optional(),
7082
+ max_levels: import_zod6.z.number().int().min(0).optional(),
7083
+ include_siblings: import_zod6.z.boolean().optional(),
7084
+ include_header: import_zod6.z.boolean().optional(),
7085
+ max_lines: import_zod6.z.number().int().min(1).optional()
6283
7086
  }).optional()
6284
7087
  });
6285
- var codexListDirInputSchema = import_zod5.z.object({
6286
- dir_path: import_zod5.z.string().min(1).describe("Absolute path to the directory to list."),
6287
- offset: import_zod5.z.number().int().min(1).optional().describe("The entry number to start listing from. Must be 1 or greater."),
6288
- limit: import_zod5.z.number().int().min(1).optional().describe("The maximum number of entries to return."),
6289
- depth: import_zod5.z.number().int().min(1).optional().describe("The maximum directory depth to traverse. Must be 1 or greater.")
7088
+ var codexListDirInputSchema = import_zod6.z.object({
7089
+ dir_path: import_zod6.z.string().min(1).describe("Absolute path to the directory to list."),
7090
+ offset: import_zod6.z.number().int().min(1).optional().describe("The entry number to start listing from. Must be 1 or greater."),
7091
+ limit: import_zod6.z.number().int().min(1).optional().describe("The maximum number of entries to return."),
7092
+ depth: import_zod6.z.number().int().min(1).optional().describe("The maximum directory depth to traverse. Must be 1 or greater.")
6290
7093
  });
6291
- var codexGrepFilesInputSchema = import_zod5.z.object({
6292
- pattern: import_zod5.z.string().min(1).describe("Regular expression pattern to search for."),
6293
- include: import_zod5.z.string().optional().describe('Optional glob limiting searched files (for example "*.rs").'),
6294
- path: import_zod5.z.string().optional().describe("Directory or file path to search. Defaults to cwd."),
6295
- limit: import_zod5.z.number().int().min(1).optional().describe("Maximum number of file paths to return (defaults to 100).")
7094
+ var codexGrepFilesInputSchema = import_zod6.z.object({
7095
+ pattern: import_zod6.z.string().min(1).describe("Regular expression pattern to search for."),
7096
+ include: import_zod6.z.string().optional().describe('Optional glob limiting searched files (for example "*.rs").'),
7097
+ path: import_zod6.z.string().optional().describe("Directory or file path to search. Defaults to cwd."),
7098
+ limit: import_zod6.z.number().int().min(1).optional().describe("Maximum number of file paths to return (defaults to 100).")
6296
7099
  });
6297
- var applyPatchInputSchema = import_zod5.z.object({
6298
- input: import_zod5.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
7100
+ var applyPatchInputSchema = import_zod6.z.object({
7101
+ input: import_zod6.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
6299
7102
  });
6300
- var geminiReadFileInputSchema = import_zod5.z.object({
6301
- file_path: import_zod5.z.string().min(1),
6302
- offset: import_zod5.z.number().int().min(0).nullish(),
6303
- limit: import_zod5.z.number().int().min(1).nullish()
7103
+ var geminiReadFileInputSchema = import_zod6.z.object({
7104
+ file_path: import_zod6.z.string().min(1),
7105
+ offset: import_zod6.z.number().int().min(0).nullish(),
7106
+ limit: import_zod6.z.number().int().min(1).nullish()
6304
7107
  });
6305
- var geminiReadFilesInputSchema = import_zod5.z.object({
6306
- paths: import_zod5.z.array(import_zod5.z.string().min(1)).min(1),
6307
- line_offset: import_zod5.z.number().int().min(0).nullish(),
6308
- line_limit: import_zod5.z.number().int().min(1).nullish(),
6309
- char_offset: import_zod5.z.number().int().min(0).nullish(),
6310
- char_limit: import_zod5.z.number().int().min(1).nullish(),
6311
- include_line_numbers: import_zod5.z.boolean().nullish()
7108
+ var geminiReadFilesInputSchema = import_zod6.z.object({
7109
+ paths: import_zod6.z.array(import_zod6.z.string().min(1)).min(1),
7110
+ line_offset: import_zod6.z.number().int().min(0).nullish(),
7111
+ line_limit: import_zod6.z.number().int().min(1).nullish(),
7112
+ char_offset: import_zod6.z.number().int().min(0).nullish(),
7113
+ char_limit: import_zod6.z.number().int().min(1).nullish(),
7114
+ include_line_numbers: import_zod6.z.boolean().nullish()
6312
7115
  }).superRefine((value, context) => {
6313
7116
  const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
6314
7117
  const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
6315
7118
  if (hasLineWindow && hasCharWindow) {
6316
7119
  context.addIssue({
6317
- code: import_zod5.z.ZodIssueCode.custom,
7120
+ code: import_zod6.z.ZodIssueCode.custom,
6318
7121
  message: "Use either line_* or char_* window arguments, not both."
6319
7122
  });
6320
7123
  }
6321
7124
  });
6322
- var geminiWriteFileInputSchema = import_zod5.z.object({
6323
- file_path: import_zod5.z.string().min(1),
6324
- content: import_zod5.z.string()
7125
+ var geminiWriteFileInputSchema = import_zod6.z.object({
7126
+ file_path: import_zod6.z.string().min(1),
7127
+ content: import_zod6.z.string()
6325
7128
  });
6326
- var geminiReplaceInputSchema = import_zod5.z.object({
6327
- file_path: import_zod5.z.string().min(1),
6328
- instruction: import_zod5.z.string().min(1),
6329
- old_string: import_zod5.z.string(),
6330
- new_string: import_zod5.z.string(),
6331
- expected_replacements: import_zod5.z.number().int().min(1).nullish()
7129
+ var geminiReplaceInputSchema = import_zod6.z.object({
7130
+ file_path: import_zod6.z.string().min(1),
7131
+ instruction: import_zod6.z.string().min(1),
7132
+ old_string: import_zod6.z.string(),
7133
+ new_string: import_zod6.z.string(),
7134
+ expected_replacements: import_zod6.z.number().int().min(1).nullish()
6332
7135
  });
6333
- var geminiListDirectoryInputSchema = import_zod5.z.object({
6334
- dir_path: import_zod5.z.string().min(1),
6335
- ignore: import_zod5.z.array(import_zod5.z.string()).nullish(),
6336
- file_filtering_options: import_zod5.z.object({
6337
- respect_git_ignore: import_zod5.z.boolean().nullish(),
6338
- respect_gemini_ignore: import_zod5.z.boolean().nullish()
7136
+ var geminiListDirectoryInputSchema = import_zod6.z.object({
7137
+ dir_path: import_zod6.z.string().min(1),
7138
+ ignore: import_zod6.z.array(import_zod6.z.string()).nullish(),
7139
+ file_filtering_options: import_zod6.z.object({
7140
+ respect_git_ignore: import_zod6.z.boolean().nullish(),
7141
+ respect_gemini_ignore: import_zod6.z.boolean().nullish()
6339
7142
  }).nullish()
6340
7143
  });
6341
- var geminiRgSearchInputSchema = import_zod5.z.object({
6342
- pattern: import_zod5.z.string().min(1),
6343
- path: import_zod5.z.string().nullish(),
6344
- glob: import_zod5.z.string().nullish(),
6345
- case_sensitive: import_zod5.z.boolean().nullish(),
6346
- exclude_pattern: import_zod5.z.string().nullish(),
6347
- names_only: import_zod5.z.boolean().nullish(),
6348
- max_matches_per_file: import_zod5.z.number().int().min(1).nullish(),
6349
- max_results: import_zod5.z.number().int().min(1).nullish()
7144
+ var geminiRgSearchInputSchema = import_zod6.z.object({
7145
+ pattern: import_zod6.z.string().min(1),
7146
+ path: import_zod6.z.string().nullish(),
7147
+ glob: import_zod6.z.string().nullish(),
7148
+ case_sensitive: import_zod6.z.boolean().nullish(),
7149
+ exclude_pattern: import_zod6.z.string().nullish(),
7150
+ names_only: import_zod6.z.boolean().nullish(),
7151
+ max_matches_per_file: import_zod6.z.number().int().min(1).nullish(),
7152
+ max_results: import_zod6.z.number().int().min(1).nullish()
6350
7153
  });
6351
- var geminiGrepSearchInputSchema = import_zod5.z.object({
6352
- pattern: import_zod5.z.string().min(1),
6353
- dir_path: import_zod5.z.string().nullish(),
6354
- include: import_zod5.z.string().nullish(),
6355
- exclude_pattern: import_zod5.z.string().nullish(),
6356
- names_only: import_zod5.z.boolean().nullish(),
6357
- max_matches_per_file: import_zod5.z.number().int().min(1).nullish(),
6358
- total_max_matches: import_zod5.z.number().int().min(1).nullish()
7154
+ var geminiGrepSearchInputSchema = import_zod6.z.object({
7155
+ pattern: import_zod6.z.string().min(1),
7156
+ dir_path: import_zod6.z.string().nullish(),
7157
+ include: import_zod6.z.string().nullish(),
7158
+ exclude_pattern: import_zod6.z.string().nullish(),
7159
+ names_only: import_zod6.z.boolean().nullish(),
7160
+ max_matches_per_file: import_zod6.z.number().int().min(1).nullish(),
7161
+ total_max_matches: import_zod6.z.number().int().min(1).nullish()
6359
7162
  });
6360
- var geminiGlobInputSchema = import_zod5.z.object({
6361
- pattern: import_zod5.z.string().min(1),
6362
- dir_path: import_zod5.z.string().nullish(),
6363
- case_sensitive: import_zod5.z.boolean().nullish(),
6364
- respect_git_ignore: import_zod5.z.boolean().nullish(),
6365
- respect_gemini_ignore: import_zod5.z.boolean().nullish()
7163
+ var geminiGlobInputSchema = import_zod6.z.object({
7164
+ pattern: import_zod6.z.string().min(1),
7165
+ dir_path: import_zod6.z.string().nullish(),
7166
+ case_sensitive: import_zod6.z.boolean().nullish(),
7167
+ respect_git_ignore: import_zod6.z.boolean().nullish(),
7168
+ respect_gemini_ignore: import_zod6.z.boolean().nullish()
6366
7169
  });
6367
7170
  function resolveFilesystemToolProfile(model, profile = "auto") {
6368
7171
  if (profile !== "auto") {
@@ -7350,19 +8153,54 @@ function isNoEntError(error) {
7350
8153
 
7351
8154
  // src/agent.ts
7352
8155
  async function runAgentLoop(request) {
7353
- const { tools: customTools, filesystemTool, filesystem_tool, ...toolLoopRequest } = request;
8156
+ return await runAgentLoopInternal(request, { depth: 0 });
8157
+ }
8158
+ async function runAgentLoopInternal(request, context) {
8159
+ const {
8160
+ tools: customTools,
8161
+ filesystemTool,
8162
+ filesystem_tool,
8163
+ subagentTool,
8164
+ subagent_tool,
8165
+ subagents,
8166
+ ...toolLoopRequest
8167
+ } = request;
7354
8168
  const filesystemSelection = filesystemTool ?? filesystem_tool;
8169
+ const subagentSelection = subagentTool ?? subagent_tool ?? subagents;
7355
8170
  const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
7356
- const mergedTools = mergeToolSets(filesystemTools, customTools ?? {});
8171
+ const resolvedSubagentConfig = resolveSubagentToolConfig(subagentSelection, context.depth);
8172
+ const subagentController = createSubagentController({
8173
+ model: request.model,
8174
+ depth: context.depth,
8175
+ customTools: customTools ?? {},
8176
+ filesystemSelection,
8177
+ subagentSelection,
8178
+ toolLoopRequest,
8179
+ resolvedSubagentConfig
8180
+ });
8181
+ const mergedTools = mergeToolSets(
8182
+ mergeToolSets(filesystemTools, subagentController?.tools ?? {}),
8183
+ customTools ?? {}
8184
+ );
7357
8185
  if (Object.keys(mergedTools).length === 0) {
7358
8186
  throw new Error(
7359
- "runAgentLoop requires at least one tool. Provide `tools` or enable `filesystemTool`."
8187
+ "runAgentLoop requires at least one tool. Provide `tools`, enable `filesystemTool`, or enable `subagentTool`."
7360
8188
  );
7361
8189
  }
7362
- return runToolLoop({
7363
- ...toolLoopRequest,
7364
- tools: mergedTools
7365
- });
8190
+ const instructions = buildLoopInstructions(
8191
+ toolLoopRequest.instructions,
8192
+ resolvedSubagentConfig,
8193
+ context.depth
8194
+ );
8195
+ try {
8196
+ return await runToolLoop({
8197
+ ...toolLoopRequest,
8198
+ ...instructions ? { instructions } : {},
8199
+ tools: mergedTools
8200
+ });
8201
+ } finally {
8202
+ await subagentController?.closeAll();
8203
+ }
7366
8204
  }
7367
8205
  function resolveFilesystemTools(model, selection) {
7368
8206
  if (selection === void 0 || selection === false) {
@@ -7390,13 +8228,89 @@ function mergeToolSets(base, extra) {
7390
8228
  for (const [toolName, toolSpec] of Object.entries(extra)) {
7391
8229
  if (Object.hasOwn(merged, toolName)) {
7392
8230
  throw new Error(
7393
- `Duplicate tool name "${toolName}" in runAgentLoop. Rename the custom tool or disable that filesystem tool.`
8231
+ `Duplicate tool name "${toolName}" in runAgentLoop. Rename one of the conflicting tools or disable an overlapping built-in tool.`
7394
8232
  );
7395
8233
  }
7396
8234
  merged[toolName] = toolSpec;
7397
8235
  }
7398
8236
  return merged;
7399
8237
  }
8238
+ function createSubagentController(params) {
8239
+ if (!params.resolvedSubagentConfig.enabled) {
8240
+ return null;
8241
+ }
8242
+ return createSubagentToolController({
8243
+ config: params.resolvedSubagentConfig,
8244
+ parentDepth: params.depth,
8245
+ parentModel: params.resolvedSubagentConfig.model ?? params.model,
8246
+ buildChildInstructions: (spawnInstructions, childDepth) => buildChildInstructions(spawnInstructions, params.resolvedSubagentConfig, childDepth),
8247
+ runSubagent: async (subagentRequest) => {
8248
+ const childCustomTools = params.resolvedSubagentConfig.inheritTools ? params.customTools : {};
8249
+ const childFilesystemSelection = params.resolvedSubagentConfig.inheritFilesystemTool ? params.filesystemSelection : false;
8250
+ return await runAgentLoopInternal(
8251
+ {
8252
+ model: subagentRequest.model,
8253
+ input: subagentRequest.input,
8254
+ instructions: subagentRequest.instructions,
8255
+ tools: childCustomTools,
8256
+ filesystemTool: childFilesystemSelection,
8257
+ subagentTool: params.subagentSelection,
8258
+ modelTools: params.toolLoopRequest.modelTools,
8259
+ maxSteps: subagentRequest.maxSteps,
8260
+ openAiReasoningEffort: params.toolLoopRequest.openAiReasoningEffort,
8261
+ signal: subagentRequest.signal
8262
+ },
8263
+ { depth: params.depth + 1 }
8264
+ );
8265
+ }
8266
+ });
8267
+ }
8268
+ function buildLoopInstructions(baseInstructions, config, depth) {
8269
+ if (!config.enabled) {
8270
+ return trimToUndefined2(baseInstructions);
8271
+ }
8272
+ const blocks = [];
8273
+ const base = trimToUndefined2(baseInstructions);
8274
+ if (base) {
8275
+ blocks.push(base);
8276
+ }
8277
+ if (config.promptPattern === "codex") {
8278
+ blocks.push(
8279
+ buildCodexSubagentOrchestratorInstructions({
8280
+ currentDepth: depth,
8281
+ maxDepth: config.maxDepth,
8282
+ maxAgents: config.maxAgents
8283
+ })
8284
+ );
8285
+ }
8286
+ if (config.instructions) {
8287
+ blocks.push(config.instructions);
8288
+ }
8289
+ return blocks.length > 0 ? blocks.join("\n\n") : void 0;
8290
+ }
8291
+ function buildChildInstructions(spawnInstructions, config, childDepth) {
8292
+ const blocks = [];
8293
+ if (config.promptPattern === "codex") {
8294
+ blocks.push(
8295
+ buildCodexSubagentWorkerInstructions({
8296
+ depth: childDepth,
8297
+ maxDepth: config.maxDepth
8298
+ })
8299
+ );
8300
+ }
8301
+ if (config.instructions) {
8302
+ blocks.push(config.instructions);
8303
+ }
8304
+ const perSpawn = trimToUndefined2(spawnInstructions);
8305
+ if (perSpawn) {
8306
+ blocks.push(perSpawn);
8307
+ }
8308
+ return blocks.length > 0 ? blocks.join("\n\n") : void 0;
8309
+ }
8310
+ function trimToUndefined2(value) {
8311
+ const trimmed = value?.trim();
8312
+ return trimmed && trimmed.length > 0 ? trimmed : void 0;
8313
+ }
7400
8314
  // Annotate the CommonJS export names for ESM import in node:
7401
8315
  0 && (module.exports = {
7402
8316
  CHATGPT_MODEL_IDS,