@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/README.md +24 -0
- package/dist/index.cjs +1035 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +1035 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1582,6 +1582,66 @@ function parseEventBlock(raw) {
|
|
|
1582
1582
|
}
|
|
1583
1583
|
}
|
|
1584
1584
|
|
|
1585
|
+
// src/utils/modelConcurrency.ts
|
|
1586
|
+
var MIN_MODEL_CONCURRENCY_CAP = 1;
|
|
1587
|
+
var MAX_MODEL_CONCURRENCY_CAP = 64;
|
|
1588
|
+
var DEFAULT_MODEL_CONCURRENCY_CAP = 3;
|
|
1589
|
+
function parsePositiveInteger(raw) {
|
|
1590
|
+
if (raw === void 0) {
|
|
1591
|
+
return void 0;
|
|
1592
|
+
}
|
|
1593
|
+
const normalized = raw.trim();
|
|
1594
|
+
if (!normalized) {
|
|
1595
|
+
return void 0;
|
|
1596
|
+
}
|
|
1597
|
+
if (!/^-?\d+$/u.test(normalized)) {
|
|
1598
|
+
return void 0;
|
|
1599
|
+
}
|
|
1600
|
+
const parsed = Number.parseInt(normalized, 10);
|
|
1601
|
+
if (!Number.isFinite(parsed)) {
|
|
1602
|
+
return void 0;
|
|
1603
|
+
}
|
|
1604
|
+
return parsed;
|
|
1605
|
+
}
|
|
1606
|
+
function clampModelConcurrencyCap(value) {
|
|
1607
|
+
if (!Number.isFinite(value)) {
|
|
1608
|
+
return DEFAULT_MODEL_CONCURRENCY_CAP;
|
|
1609
|
+
}
|
|
1610
|
+
const rounded = Math.floor(value);
|
|
1611
|
+
if (rounded < MIN_MODEL_CONCURRENCY_CAP) {
|
|
1612
|
+
return MIN_MODEL_CONCURRENCY_CAP;
|
|
1613
|
+
}
|
|
1614
|
+
if (rounded > MAX_MODEL_CONCURRENCY_CAP) {
|
|
1615
|
+
return MAX_MODEL_CONCURRENCY_CAP;
|
|
1616
|
+
}
|
|
1617
|
+
return rounded;
|
|
1618
|
+
}
|
|
1619
|
+
function normalizeModelIdForEnv(modelId) {
|
|
1620
|
+
return modelId.trim().replace(/[^A-Za-z0-9]+/gu, "_").replace(/^_+|_+$/gu, "").toUpperCase();
|
|
1621
|
+
}
|
|
1622
|
+
function resolveModelConcurrencyCap(options) {
|
|
1623
|
+
const env = options.env ?? process.env;
|
|
1624
|
+
const providerPrefix = options.providerEnvPrefix;
|
|
1625
|
+
const defaultCap = clampModelConcurrencyCap(options.defaultCap ?? DEFAULT_MODEL_CONCURRENCY_CAP);
|
|
1626
|
+
const normalizedModelId = options.modelId ? normalizeModelIdForEnv(options.modelId) : "";
|
|
1627
|
+
const candidateKeys = [
|
|
1628
|
+
...normalizedModelId ? [
|
|
1629
|
+
`${providerPrefix}_MAX_PARALLEL_REQUESTS_MODEL_${normalizedModelId}`,
|
|
1630
|
+
`LLM_MAX_PARALLEL_REQUESTS_MODEL_${normalizedModelId}`
|
|
1631
|
+
] : [],
|
|
1632
|
+
`${providerPrefix}_MAX_PARALLEL_REQUESTS_PER_MODEL`,
|
|
1633
|
+
"LLM_MAX_PARALLEL_REQUESTS_PER_MODEL"
|
|
1634
|
+
];
|
|
1635
|
+
for (const key of candidateKeys) {
|
|
1636
|
+
const parsed = parsePositiveInteger(env[key]);
|
|
1637
|
+
if (parsed === void 0) {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
return clampModelConcurrencyCap(parsed);
|
|
1641
|
+
}
|
|
1642
|
+
return defaultCap;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1585
1645
|
// src/utils/scheduler.ts
|
|
1586
1646
|
function sleep(ms) {
|
|
1587
1647
|
return new Promise((resolve) => {
|
|
@@ -1597,13 +1657,72 @@ function toError(value) {
|
|
|
1597
1657
|
}
|
|
1598
1658
|
return new Error("Unknown error");
|
|
1599
1659
|
}
|
|
1660
|
+
function getStatusCode(error) {
|
|
1661
|
+
if (!error || typeof error !== "object") {
|
|
1662
|
+
return void 0;
|
|
1663
|
+
}
|
|
1664
|
+
const maybe = error;
|
|
1665
|
+
const candidates = [maybe.status, maybe.statusCode];
|
|
1666
|
+
for (const candidate of candidates) {
|
|
1667
|
+
if (typeof candidate === "number") {
|
|
1668
|
+
return candidate;
|
|
1669
|
+
}
|
|
1670
|
+
if (typeof candidate === "string") {
|
|
1671
|
+
const parsed = Number.parseInt(candidate, 10);
|
|
1672
|
+
if (Number.isFinite(parsed)) {
|
|
1673
|
+
return parsed;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
if (typeof maybe.code === "number") {
|
|
1678
|
+
return maybe.code;
|
|
1679
|
+
}
|
|
1680
|
+
return void 0;
|
|
1681
|
+
}
|
|
1682
|
+
function getErrorText(error) {
|
|
1683
|
+
if (error instanceof Error) {
|
|
1684
|
+
return error.message.toLowerCase();
|
|
1685
|
+
}
|
|
1686
|
+
if (typeof error === "string") {
|
|
1687
|
+
return error.toLowerCase();
|
|
1688
|
+
}
|
|
1689
|
+
if (error && typeof error === "object") {
|
|
1690
|
+
const maybe = error;
|
|
1691
|
+
const code = typeof maybe.code === "string" ? maybe.code : "";
|
|
1692
|
+
const message = typeof maybe.message === "string" ? maybe.message : "";
|
|
1693
|
+
return `${code} ${message}`.trim().toLowerCase();
|
|
1694
|
+
}
|
|
1695
|
+
return "";
|
|
1696
|
+
}
|
|
1697
|
+
function defaultIsOverloadError(error) {
|
|
1698
|
+
const status = getStatusCode(error);
|
|
1699
|
+
if (status === 429 || status === 503 || status === 529) {
|
|
1700
|
+
return true;
|
|
1701
|
+
}
|
|
1702
|
+
const text = getErrorText(error);
|
|
1703
|
+
if (!text) {
|
|
1704
|
+
return false;
|
|
1705
|
+
}
|
|
1706
|
+
return text.includes("rate limit") || text.includes("too many requests") || text.includes("resource exhausted") || text.includes("resource_exhausted") || text.includes("overload");
|
|
1707
|
+
}
|
|
1600
1708
|
function createCallScheduler(options = {}) {
|
|
1601
1709
|
const maxParallelRequests = Math.max(1, Math.floor(options.maxParallelRequests ?? 3));
|
|
1710
|
+
const initialParallelRequests = Math.min(
|
|
1711
|
+
maxParallelRequests,
|
|
1712
|
+
Math.max(1, Math.floor(options.initialParallelRequests ?? Math.min(3, maxParallelRequests)))
|
|
1713
|
+
);
|
|
1714
|
+
const increaseAfterConsecutiveSuccesses = Math.max(
|
|
1715
|
+
1,
|
|
1716
|
+
Math.floor(options.increaseAfterConsecutiveSuccesses ?? 8)
|
|
1717
|
+
);
|
|
1602
1718
|
const minIntervalBetweenStartMs = Math.max(0, Math.floor(options.minIntervalBetweenStartMs ?? 0));
|
|
1603
1719
|
const startJitterMs = Math.max(0, Math.floor(options.startJitterMs ?? 0));
|
|
1604
1720
|
const retryPolicy = options.retry;
|
|
1721
|
+
const isOverloadError2 = options.isOverloadError ?? defaultIsOverloadError;
|
|
1605
1722
|
let activeCount = 0;
|
|
1606
1723
|
let lastStartTime = 0;
|
|
1724
|
+
let currentParallelLimit = initialParallelRequests;
|
|
1725
|
+
let consecutiveSuccesses = 0;
|
|
1607
1726
|
let startSpacingChain = Promise.resolve();
|
|
1608
1727
|
const queue = [];
|
|
1609
1728
|
async function applyStartSpacing() {
|
|
@@ -1634,6 +1753,10 @@ function createCallScheduler(options = {}) {
|
|
|
1634
1753
|
await applyStartSpacing();
|
|
1635
1754
|
return await fn();
|
|
1636
1755
|
} catch (error) {
|
|
1756
|
+
if (isOverloadError2(error)) {
|
|
1757
|
+
consecutiveSuccesses = 0;
|
|
1758
|
+
currentParallelLimit = Math.max(1, Math.ceil(currentParallelLimit / 2));
|
|
1759
|
+
}
|
|
1637
1760
|
const err = toError(error);
|
|
1638
1761
|
if (!retryPolicy || attempt >= retryPolicy.maxAttempts) {
|
|
1639
1762
|
throw err;
|
|
@@ -1653,7 +1776,7 @@ function createCallScheduler(options = {}) {
|
|
|
1653
1776
|
}
|
|
1654
1777
|
}
|
|
1655
1778
|
function drainQueue() {
|
|
1656
|
-
while (activeCount <
|
|
1779
|
+
while (activeCount < currentParallelLimit && queue.length > 0) {
|
|
1657
1780
|
const task = queue.shift();
|
|
1658
1781
|
if (!task) {
|
|
1659
1782
|
continue;
|
|
@@ -1667,6 +1790,11 @@ function createCallScheduler(options = {}) {
|
|
|
1667
1790
|
const job = async () => {
|
|
1668
1791
|
try {
|
|
1669
1792
|
const result = await attemptWithRetries(fn, 1);
|
|
1793
|
+
consecutiveSuccesses += 1;
|
|
1794
|
+
if (currentParallelLimit < maxParallelRequests && consecutiveSuccesses >= increaseAfterConsecutiveSuccesses) {
|
|
1795
|
+
currentParallelLimit += 1;
|
|
1796
|
+
consecutiveSuccesses = 0;
|
|
1797
|
+
}
|
|
1670
1798
|
resolve(result);
|
|
1671
1799
|
} catch (error) {
|
|
1672
1800
|
reject(toError(error));
|
|
@@ -1756,13 +1884,28 @@ function getFireworksClient() {
|
|
|
1756
1884
|
}
|
|
1757
1885
|
|
|
1758
1886
|
// src/fireworks/calls.ts
|
|
1759
|
-
var
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1887
|
+
var DEFAULT_SCHEDULER_KEY = "__default__";
|
|
1888
|
+
var schedulerByModel = /* @__PURE__ */ new Map();
|
|
1889
|
+
function getSchedulerForModel(modelId) {
|
|
1890
|
+
const normalizedModelId = modelId?.trim();
|
|
1891
|
+
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY;
|
|
1892
|
+
const existing = schedulerByModel.get(schedulerKey);
|
|
1893
|
+
if (existing) {
|
|
1894
|
+
return existing;
|
|
1895
|
+
}
|
|
1896
|
+
const created = createCallScheduler({
|
|
1897
|
+
maxParallelRequests: resolveModelConcurrencyCap({
|
|
1898
|
+
providerEnvPrefix: "FIREWORKS",
|
|
1899
|
+
modelId: normalizedModelId
|
|
1900
|
+
}),
|
|
1901
|
+
minIntervalBetweenStartMs: 200,
|
|
1902
|
+
startJitterMs: 200
|
|
1903
|
+
});
|
|
1904
|
+
schedulerByModel.set(schedulerKey, created);
|
|
1905
|
+
return created;
|
|
1906
|
+
}
|
|
1907
|
+
async function runFireworksCall(fn, modelId) {
|
|
1908
|
+
return getSchedulerForModel(modelId).run(async () => fn(getFireworksClient()));
|
|
1766
1909
|
}
|
|
1767
1910
|
|
|
1768
1911
|
// src/fireworks/models.ts
|
|
@@ -2098,6 +2241,18 @@ function shouldRetry(error) {
|
|
|
2098
2241
|
}
|
|
2099
2242
|
return false;
|
|
2100
2243
|
}
|
|
2244
|
+
function isOverloadError(error) {
|
|
2245
|
+
const status = getStatus(error);
|
|
2246
|
+
if (status === 429 || status === 503 || status === 529) {
|
|
2247
|
+
return true;
|
|
2248
|
+
}
|
|
2249
|
+
const reason = getErrorReason(error);
|
|
2250
|
+
if (reason && RATE_LIMIT_REASONS.has(reason)) {
|
|
2251
|
+
return true;
|
|
2252
|
+
}
|
|
2253
|
+
const message = getErrorMessage(error).toLowerCase();
|
|
2254
|
+
return message.includes("rate limit") || message.includes("too many requests") || message.includes("resource exhausted") || message.includes("resource_exhausted");
|
|
2255
|
+
}
|
|
2101
2256
|
function retryDelayMs(attempt) {
|
|
2102
2257
|
const baseRetryDelayMs = 500;
|
|
2103
2258
|
const maxRetryDelayMs = 4e3;
|
|
@@ -2105,23 +2260,39 @@ function retryDelayMs(attempt) {
|
|
|
2105
2260
|
const jitter = Math.floor(Math.random() * 200);
|
|
2106
2261
|
return base + jitter;
|
|
2107
2262
|
}
|
|
2108
|
-
var
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2263
|
+
var DEFAULT_SCHEDULER_KEY2 = "__default__";
|
|
2264
|
+
var schedulerByModel2 = /* @__PURE__ */ new Map();
|
|
2265
|
+
function getSchedulerForModel2(modelId) {
|
|
2266
|
+
const normalizedModelId = modelId?.trim();
|
|
2267
|
+
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY2;
|
|
2268
|
+
const existing = schedulerByModel2.get(schedulerKey);
|
|
2269
|
+
if (existing) {
|
|
2270
|
+
return existing;
|
|
2271
|
+
}
|
|
2272
|
+
const created = createCallScheduler({
|
|
2273
|
+
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2274
|
+
providerEnvPrefix: "GOOGLE",
|
|
2275
|
+
modelId: normalizedModelId
|
|
2276
|
+
}),
|
|
2277
|
+
minIntervalBetweenStartMs: 200,
|
|
2278
|
+
startJitterMs: 200,
|
|
2279
|
+
isOverloadError,
|
|
2280
|
+
retry: {
|
|
2281
|
+
maxAttempts: 3,
|
|
2282
|
+
getDelayMs: (attempt, error) => {
|
|
2283
|
+
if (!shouldRetry(error)) {
|
|
2284
|
+
return null;
|
|
2285
|
+
}
|
|
2286
|
+
const hintedDelay = getRetryAfterMs(error);
|
|
2287
|
+
return hintedDelay ?? retryDelayMs(attempt);
|
|
2117
2288
|
}
|
|
2118
|
-
const hintedDelay = getRetryAfterMs(error);
|
|
2119
|
-
return hintedDelay ?? retryDelayMs(attempt);
|
|
2120
2289
|
}
|
|
2121
|
-
}
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2290
|
+
});
|
|
2291
|
+
schedulerByModel2.set(schedulerKey, created);
|
|
2292
|
+
return created;
|
|
2293
|
+
}
|
|
2294
|
+
async function runGeminiCall(fn, modelId) {
|
|
2295
|
+
return getSchedulerForModel2(modelId).run(async () => fn(await getGeminiClient()));
|
|
2125
2296
|
}
|
|
2126
2297
|
|
|
2127
2298
|
// src/openai/client.ts
|
|
@@ -2282,13 +2453,28 @@ function getOpenAiClient() {
|
|
|
2282
2453
|
|
|
2283
2454
|
// src/openai/calls.ts
|
|
2284
2455
|
var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
|
|
2285
|
-
var
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2456
|
+
var DEFAULT_SCHEDULER_KEY3 = "__default__";
|
|
2457
|
+
var schedulerByModel3 = /* @__PURE__ */ new Map();
|
|
2458
|
+
function getSchedulerForModel3(modelId) {
|
|
2459
|
+
const normalizedModelId = modelId?.trim();
|
|
2460
|
+
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY3;
|
|
2461
|
+
const existing = schedulerByModel3.get(schedulerKey);
|
|
2462
|
+
if (existing) {
|
|
2463
|
+
return existing;
|
|
2464
|
+
}
|
|
2465
|
+
const created = createCallScheduler({
|
|
2466
|
+
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2467
|
+
providerEnvPrefix: "OPENAI",
|
|
2468
|
+
modelId: normalizedModelId
|
|
2469
|
+
}),
|
|
2470
|
+
minIntervalBetweenStartMs: 200,
|
|
2471
|
+
startJitterMs: 200
|
|
2472
|
+
});
|
|
2473
|
+
schedulerByModel3.set(schedulerKey, created);
|
|
2474
|
+
return created;
|
|
2475
|
+
}
|
|
2476
|
+
async function runOpenAiCall(fn, modelId) {
|
|
2477
|
+
return getSchedulerForModel3(modelId).run(async () => fn(getOpenAiClient()));
|
|
2292
2478
|
}
|
|
2293
2479
|
|
|
2294
2480
|
// src/openai/models.ts
|
|
@@ -4050,7 +4236,7 @@ async function runTextCall(params) {
|
|
|
4050
4236
|
}
|
|
4051
4237
|
}
|
|
4052
4238
|
}
|
|
4053
|
-
});
|
|
4239
|
+
}, modelForProvider);
|
|
4054
4240
|
} else if (provider === "chatgpt") {
|
|
4055
4241
|
const chatGptInput = toChatGptInput(contents);
|
|
4056
4242
|
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
@@ -4145,7 +4331,7 @@ async function runTextCall(params) {
|
|
|
4145
4331
|
pushDelta("response", textOutput);
|
|
4146
4332
|
}
|
|
4147
4333
|
latestUsage = extractFireworksUsageTokens(response.usage);
|
|
4148
|
-
});
|
|
4334
|
+
}, modelForProvider);
|
|
4149
4335
|
} else {
|
|
4150
4336
|
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
4151
4337
|
const config = {
|
|
@@ -4213,7 +4399,7 @@ async function runTextCall(params) {
|
|
|
4213
4399
|
}
|
|
4214
4400
|
}
|
|
4215
4401
|
grounding = latestGrounding;
|
|
4216
|
-
});
|
|
4402
|
+
}, modelForProvider);
|
|
4217
4403
|
}
|
|
4218
4404
|
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
4219
4405
|
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
@@ -4640,7 +4826,7 @@ async function runToolLoop(request) {
|
|
|
4640
4826
|
}
|
|
4641
4827
|
}
|
|
4642
4828
|
return await stream.finalResponse();
|
|
4643
|
-
});
|
|
4829
|
+
}, providerInfo.model);
|
|
4644
4830
|
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
4645
4831
|
emitEvent({ type: "model", modelVersion });
|
|
4646
4832
|
if (finalResponse.error) {
|
|
@@ -4916,7 +5102,7 @@ async function runToolLoop(request) {
|
|
|
4916
5102
|
},
|
|
4917
5103
|
{ signal: request.signal }
|
|
4918
5104
|
);
|
|
4919
|
-
});
|
|
5105
|
+
}, providerInfo.model);
|
|
4920
5106
|
const modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
4921
5107
|
request.onEvent?.({ type: "model", modelVersion });
|
|
4922
5108
|
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
@@ -5113,7 +5299,7 @@ async function runToolLoop(request) {
|
|
|
5113
5299
|
usageMetadata: latestUsageMetadata,
|
|
5114
5300
|
modelVersion: resolvedModelVersion ?? request.model
|
|
5115
5301
|
};
|
|
5116
|
-
});
|
|
5302
|
+
}, request.model);
|
|
5117
5303
|
const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
5118
5304
|
const modelVersion = response.modelVersion ?? request.model;
|
|
5119
5305
|
const stepCostUsd = estimateCallCostUsd({
|
|
@@ -5451,13 +5637,630 @@ function appendMarkdownSourcesSection(value, sources) {
|
|
|
5451
5637
|
${lines}`;
|
|
5452
5638
|
}
|
|
5453
5639
|
|
|
5640
|
+
// src/agent/subagents.ts
|
|
5641
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
5642
|
+
import { z as z4 } from "zod";
|
|
5643
|
+
var DEFAULT_SUBAGENT_MAX_AGENTS = 4;
|
|
5644
|
+
var DEFAULT_SUBAGENT_MAX_DEPTH = 2;
|
|
5645
|
+
var DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS = 1500;
|
|
5646
|
+
var DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS = 9e4;
|
|
5647
|
+
var MAX_SUBAGENT_MAX_AGENTS = 64;
|
|
5648
|
+
var MAX_SUBAGENT_MAX_DEPTH = 12;
|
|
5649
|
+
var MAX_SUBAGENT_MAX_STEPS = 64;
|
|
5650
|
+
var MAX_SUBAGENT_WAIT_TIMEOUT_MS = 6e5;
|
|
5651
|
+
var SUBAGENT_CONTROL_TOOL_NAMES = ["send_input", "resume_agent", "wait", "close_agent"];
|
|
5652
|
+
var subagentInputItemSchema = z4.object({
|
|
5653
|
+
text: z4.string().optional(),
|
|
5654
|
+
image_url: z4.string().optional(),
|
|
5655
|
+
name: z4.string().optional(),
|
|
5656
|
+
path: z4.string().optional(),
|
|
5657
|
+
type: z4.string().optional()
|
|
5658
|
+
}).passthrough();
|
|
5659
|
+
var spawnAgentInputSchema = z4.object({
|
|
5660
|
+
prompt: z4.string().optional().describe("Initial prompt for the subagent."),
|
|
5661
|
+
message: z4.string().optional().describe("Codex-style alias for prompt."),
|
|
5662
|
+
items: z4.array(subagentInputItemSchema).optional().describe("Optional Codex-style input items."),
|
|
5663
|
+
agent_type: z4.string().optional().describe("Codex-style agent type hint."),
|
|
5664
|
+
instructions: z4.string().optional().describe("Optional extra instructions for this subagent instance."),
|
|
5665
|
+
model: z4.string().optional().describe("Optional model override. Must be one of this package's supported text model ids."),
|
|
5666
|
+
max_steps: z4.number().int().min(1).max(MAX_SUBAGENT_MAX_STEPS).optional().describe("Optional max step budget for each subagent run.")
|
|
5667
|
+
}).refine((value) => Boolean(resolvePromptValue(value.prompt, value.message, value.items)), {
|
|
5668
|
+
message: "Either prompt, message, or items must contain non-empty input."
|
|
5669
|
+
});
|
|
5670
|
+
var sendInputSchema = z4.object({
|
|
5671
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
5672
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id."),
|
|
5673
|
+
input: z4.string().optional().describe("New user input queued for the subagent."),
|
|
5674
|
+
message: z4.string().optional().describe("Codex-style alias for input."),
|
|
5675
|
+
items: z4.array(subagentInputItemSchema).optional().describe("Optional Codex-style input items."),
|
|
5676
|
+
interrupt: z4.boolean().optional().describe("If true and currently running, aborts active run before queuing input.")
|
|
5677
|
+
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
5678
|
+
message: "agent_id (or id) is required."
|
|
5679
|
+
}).refine((value) => Boolean(resolvePromptValue(value.input, value.message, value.items)), {
|
|
5680
|
+
message: "input (or message/items) is required."
|
|
5681
|
+
});
|
|
5682
|
+
var resumeAgentSchema = z4.object({
|
|
5683
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
5684
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id.")
|
|
5685
|
+
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
5686
|
+
message: "agent_id (or id) is required."
|
|
5687
|
+
});
|
|
5688
|
+
var waitSchema = z4.object({
|
|
5689
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
5690
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id."),
|
|
5691
|
+
ids: z4.array(z4.string().min(1)).optional().describe("Codex-style list of agent ids."),
|
|
5692
|
+
timeout_ms: z4.number().int().min(1).optional().describe("Optional wait timeout in milliseconds.")
|
|
5693
|
+
}).refine(
|
|
5694
|
+
(value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)) || Array.isArray(value.ids) && value.ids.length > 0,
|
|
5695
|
+
{
|
|
5696
|
+
message: "agent_id/id or ids is required."
|
|
5697
|
+
}
|
|
5698
|
+
);
|
|
5699
|
+
var closeSchema = z4.object({
|
|
5700
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
5701
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id.")
|
|
5702
|
+
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
5703
|
+
message: "agent_id (or id) is required."
|
|
5704
|
+
});
|
|
5705
|
+
function resolveSubagentToolConfig(selection, currentDepth) {
|
|
5706
|
+
const defaults = {
|
|
5707
|
+
maxAgents: DEFAULT_SUBAGENT_MAX_AGENTS,
|
|
5708
|
+
maxDepth: DEFAULT_SUBAGENT_MAX_DEPTH,
|
|
5709
|
+
defaultWaitTimeoutMs: DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS,
|
|
5710
|
+
maxWaitTimeoutMs: DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS,
|
|
5711
|
+
promptPattern: "codex",
|
|
5712
|
+
inheritTools: true,
|
|
5713
|
+
inheritFilesystemTool: true
|
|
5714
|
+
};
|
|
5715
|
+
if (selection === void 0 || selection === false) {
|
|
5716
|
+
return {
|
|
5717
|
+
enabled: false,
|
|
5718
|
+
...defaults
|
|
5719
|
+
};
|
|
5720
|
+
}
|
|
5721
|
+
const config = selection === true ? {} : selection;
|
|
5722
|
+
const maxAgents = normalizeInteger(
|
|
5723
|
+
config.maxAgents,
|
|
5724
|
+
defaults.maxAgents,
|
|
5725
|
+
1,
|
|
5726
|
+
MAX_SUBAGENT_MAX_AGENTS
|
|
5727
|
+
);
|
|
5728
|
+
const maxDepth = normalizeInteger(config.maxDepth, defaults.maxDepth, 1, MAX_SUBAGENT_MAX_DEPTH);
|
|
5729
|
+
const defaultWaitTimeoutMs = normalizeInteger(
|
|
5730
|
+
config.defaultWaitTimeoutMs,
|
|
5731
|
+
defaults.defaultWaitTimeoutMs,
|
|
5732
|
+
1,
|
|
5733
|
+
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
5734
|
+
);
|
|
5735
|
+
const maxWaitTimeoutMs = normalizeInteger(
|
|
5736
|
+
config.maxWaitTimeoutMs,
|
|
5737
|
+
defaults.maxWaitTimeoutMs,
|
|
5738
|
+
defaultWaitTimeoutMs,
|
|
5739
|
+
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
5740
|
+
);
|
|
5741
|
+
const promptPattern = config.promptPattern ?? defaults.promptPattern;
|
|
5742
|
+
const instructions = trimToUndefined(config.instructions);
|
|
5743
|
+
const maxSteps = normalizeOptionalInteger(config.maxSteps, 1, MAX_SUBAGENT_MAX_STEPS);
|
|
5744
|
+
const enabled = config.enabled !== false && currentDepth < maxDepth;
|
|
5745
|
+
return {
|
|
5746
|
+
enabled,
|
|
5747
|
+
maxAgents,
|
|
5748
|
+
maxDepth,
|
|
5749
|
+
defaultWaitTimeoutMs,
|
|
5750
|
+
maxWaitTimeoutMs,
|
|
5751
|
+
promptPattern,
|
|
5752
|
+
...instructions ? { instructions } : {},
|
|
5753
|
+
...config.model ? { model: config.model } : {},
|
|
5754
|
+
...maxSteps ? { maxSteps } : {},
|
|
5755
|
+
inheritTools: config.inheritTools !== false,
|
|
5756
|
+
inheritFilesystemTool: config.inheritFilesystemTool !== false
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
function buildCodexSubagentOrchestratorInstructions(params) {
|
|
5760
|
+
return [
|
|
5761
|
+
"Subagent orchestration tools are available: spawn_agent, send_input, resume_agent, wait, close_agent.",
|
|
5762
|
+
"Use this control pattern:",
|
|
5763
|
+
"1. spawn_agent with a focused prompt.",
|
|
5764
|
+
"2. wait on that agent_id until it is no longer running.",
|
|
5765
|
+
"3. For follow-up turns, send_input then resume_agent.",
|
|
5766
|
+
"4. close_agent when delegation is complete.",
|
|
5767
|
+
`Limits: max active subagents ${params.maxAgents}, max depth ${params.maxDepth}, current depth ${params.currentDepth}.`
|
|
5768
|
+
].join("\n");
|
|
5769
|
+
}
|
|
5770
|
+
function buildCodexSubagentWorkerInstructions(params) {
|
|
5771
|
+
return [
|
|
5772
|
+
`You are a delegated subagent at depth ${params.depth}/${params.maxDepth}.`,
|
|
5773
|
+
"Focus on the delegated task, use available tools when needed, and return concise actionable output.",
|
|
5774
|
+
"If blocked, report the blocker explicitly."
|
|
5775
|
+
].join("\n");
|
|
5776
|
+
}
|
|
5777
|
+
function createSubagentToolController(options) {
|
|
5778
|
+
if (!options.config.enabled) {
|
|
5779
|
+
return {
|
|
5780
|
+
tools: {},
|
|
5781
|
+
closeAll: async () => {
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
const agents = /* @__PURE__ */ new Map();
|
|
5786
|
+
const tools = {
|
|
5787
|
+
spawn_agent: tool({
|
|
5788
|
+
description: "Spawns a subagent asynchronously. Returns immediately with agent status and id.",
|
|
5789
|
+
inputSchema: spawnAgentInputSchema,
|
|
5790
|
+
execute: async (input) => {
|
|
5791
|
+
if (countActiveAgents(agents) >= options.config.maxAgents) {
|
|
5792
|
+
throw new Error(
|
|
5793
|
+
`Subagent limit reached (${options.config.maxAgents}). Close existing agents before spawning new ones.`
|
|
5794
|
+
);
|
|
5795
|
+
}
|
|
5796
|
+
const childDepth = options.parentDepth + 1;
|
|
5797
|
+
if (childDepth > options.config.maxDepth) {
|
|
5798
|
+
throw new Error(
|
|
5799
|
+
`Subagent depth limit reached (${options.config.maxDepth}). Cannot spawn at depth ${childDepth}.`
|
|
5800
|
+
);
|
|
5801
|
+
}
|
|
5802
|
+
let model = options.config.model ?? options.parentModel;
|
|
5803
|
+
if (input.model) {
|
|
5804
|
+
if (!isLlmTextModelId(input.model)) {
|
|
5805
|
+
throw new Error(`Unsupported subagent model id: ${input.model}`);
|
|
5806
|
+
}
|
|
5807
|
+
model = input.model;
|
|
5808
|
+
}
|
|
5809
|
+
const id = `agent_${randomBytes2(6).toString("hex")}`;
|
|
5810
|
+
const now = Date.now();
|
|
5811
|
+
const initialPrompt = resolvePromptValue(input.prompt, input.message, input.items);
|
|
5812
|
+
if (!initialPrompt) {
|
|
5813
|
+
throw new Error("spawn_agent requires prompt/message/items with non-empty text.");
|
|
5814
|
+
}
|
|
5815
|
+
const agent = {
|
|
5816
|
+
id,
|
|
5817
|
+
depth: childDepth,
|
|
5818
|
+
model,
|
|
5819
|
+
status: "idle",
|
|
5820
|
+
createdAtMs: now,
|
|
5821
|
+
updatedAtMs: now,
|
|
5822
|
+
pendingInputs: [initialPrompt],
|
|
5823
|
+
history: [],
|
|
5824
|
+
...options.buildChildInstructions ? {
|
|
5825
|
+
instructions: trimToUndefined(
|
|
5826
|
+
options.buildChildInstructions(input.instructions, childDepth)
|
|
5827
|
+
)
|
|
5828
|
+
} : input.instructions ? { instructions: trimToUndefined(input.instructions) } : {},
|
|
5829
|
+
...input.max_steps ? { maxSteps: input.max_steps } : options.config.maxSteps ? { maxSteps: options.config.maxSteps } : {},
|
|
5830
|
+
turns: 0,
|
|
5831
|
+
notification: "spawned",
|
|
5832
|
+
notificationMessage: `Spawned subagent ${id}.`,
|
|
5833
|
+
version: 1,
|
|
5834
|
+
waiters: /* @__PURE__ */ new Set()
|
|
5835
|
+
};
|
|
5836
|
+
agents.set(id, agent);
|
|
5837
|
+
startRun(agent, options);
|
|
5838
|
+
return buildToolResponse(agent, {
|
|
5839
|
+
notification: "spawned",
|
|
5840
|
+
message: `Spawned subagent ${id}.`
|
|
5841
|
+
});
|
|
5842
|
+
}
|
|
5843
|
+
}),
|
|
5844
|
+
send_input: tool({
|
|
5845
|
+
description: "Queues new input for an existing subagent.",
|
|
5846
|
+
inputSchema: sendInputSchema,
|
|
5847
|
+
execute: async (input) => {
|
|
5848
|
+
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
5849
|
+
if (!agentId) {
|
|
5850
|
+
throw new Error("send_input requires agent_id or id.");
|
|
5851
|
+
}
|
|
5852
|
+
const agent = requireAgent(agents, agentId);
|
|
5853
|
+
const nextInput = resolvePromptValue(input.input, input.message, input.items);
|
|
5854
|
+
if (!nextInput) {
|
|
5855
|
+
throw new Error("send_input requires input/message/items with non-empty text.");
|
|
5856
|
+
}
|
|
5857
|
+
if (agent.status === "closed") {
|
|
5858
|
+
throw new Error(`Subagent ${agent.id} is closed.`);
|
|
5859
|
+
}
|
|
5860
|
+
if (input.interrupt && agent.abortController) {
|
|
5861
|
+
agent.abortController.abort("send_input_interrupt");
|
|
5862
|
+
agent.pendingInputs.unshift(nextInput);
|
|
5863
|
+
setNotification(agent, "input_queued", `Interrupted ${agent.id} and queued new input.`);
|
|
5864
|
+
return buildToolResponse(agent);
|
|
5865
|
+
}
|
|
5866
|
+
agent.pendingInputs.push(nextInput);
|
|
5867
|
+
setNotification(agent, "input_queued", `Queued input for ${agent.id}.`);
|
|
5868
|
+
return buildToolResponse(agent);
|
|
5869
|
+
}
|
|
5870
|
+
}),
|
|
5871
|
+
resume_agent: tool({
|
|
5872
|
+
description: "Resumes a subagent run when queued input is available.",
|
|
5873
|
+
inputSchema: resumeAgentSchema,
|
|
5874
|
+
execute: async (input) => {
|
|
5875
|
+
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
5876
|
+
if (!agentId) {
|
|
5877
|
+
throw new Error("resume_agent requires agent_id or id.");
|
|
5878
|
+
}
|
|
5879
|
+
const agent = requireAgent(agents, agentId);
|
|
5880
|
+
if (agent.status === "closed") {
|
|
5881
|
+
setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
|
|
5882
|
+
return buildToolResponse(agent, {
|
|
5883
|
+
notification: "already_closed",
|
|
5884
|
+
message: `Subagent ${agent.id} is already closed.`
|
|
5885
|
+
});
|
|
5886
|
+
}
|
|
5887
|
+
const outcome = startRun(agent, options);
|
|
5888
|
+
if (outcome === "started") {
|
|
5889
|
+
return buildToolResponse(agent, {
|
|
5890
|
+
notification: "run_started",
|
|
5891
|
+
message: `Started subagent ${agent.id}.`
|
|
5892
|
+
});
|
|
5893
|
+
}
|
|
5894
|
+
if (outcome === "already_running") {
|
|
5895
|
+
setNotification(agent, "already_running", `Subagent ${agent.id} is already running.`);
|
|
5896
|
+
return buildToolResponse(agent);
|
|
5897
|
+
}
|
|
5898
|
+
setNotification(agent, "no_pending_input", `Subagent ${agent.id} has no queued input.`);
|
|
5899
|
+
return buildToolResponse(agent);
|
|
5900
|
+
}
|
|
5901
|
+
}),
|
|
5902
|
+
wait: tool({
|
|
5903
|
+
description: "Waits for a running subagent to change state or until timeout. Returns current status.",
|
|
5904
|
+
inputSchema: waitSchema,
|
|
5905
|
+
execute: async (input) => {
|
|
5906
|
+
const usesIdsArray = Array.isArray(input.ids) && input.ids.length > 0;
|
|
5907
|
+
const ids = resolveAgentIdList(input.agent_id, input.id, input.ids);
|
|
5908
|
+
if (ids.length === 0) {
|
|
5909
|
+
throw new Error("wait requires agent_id/id or ids.");
|
|
5910
|
+
}
|
|
5911
|
+
const timeoutMs = normalizeInteger(
|
|
5912
|
+
input.timeout_ms,
|
|
5913
|
+
options.config.defaultWaitTimeoutMs,
|
|
5914
|
+
1,
|
|
5915
|
+
options.config.maxWaitTimeoutMs
|
|
5916
|
+
);
|
|
5917
|
+
if (usesIdsArray) {
|
|
5918
|
+
const status = await waitForAnyAgentStatus(agents, ids, timeoutMs);
|
|
5919
|
+
return { status, timed_out: Object.keys(status).length === 0, timeout_ms: timeoutMs };
|
|
5920
|
+
}
|
|
5921
|
+
const agent = requireAgent(agents, ids[0]);
|
|
5922
|
+
if (agent.status === "running") {
|
|
5923
|
+
const completed = await waitUntilNotRunning(agent, timeoutMs);
|
|
5924
|
+
if (!completed) {
|
|
5925
|
+
setNotification(
|
|
5926
|
+
agent,
|
|
5927
|
+
"timeout",
|
|
5928
|
+
`Timed out after ${timeoutMs}ms while waiting for ${agent.id}.`
|
|
5929
|
+
);
|
|
5930
|
+
return buildToolResponse(agent, void 0, { timed_out: true, timeout_ms: timeoutMs });
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
return buildToolResponse(agent, void 0, { timed_out: false, timeout_ms: timeoutMs });
|
|
5934
|
+
}
|
|
5935
|
+
}),
|
|
5936
|
+
close_agent: tool({
|
|
5937
|
+
description: "Closes a subagent and aborts its current run if it is still running.",
|
|
5938
|
+
inputSchema: closeSchema,
|
|
5939
|
+
execute: async (input) => {
|
|
5940
|
+
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
5941
|
+
if (!agentId) {
|
|
5942
|
+
throw new Error("close_agent requires agent_id or id.");
|
|
5943
|
+
}
|
|
5944
|
+
const agent = requireAgent(agents, agentId);
|
|
5945
|
+
if (agent.status === "closed") {
|
|
5946
|
+
setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
|
|
5947
|
+
return buildToolResponse(agent, void 0, { cancelled: false });
|
|
5948
|
+
}
|
|
5949
|
+
const cancelled = closeSubagent(agent, `Closed ${agent.id}.`);
|
|
5950
|
+
return buildToolResponse(
|
|
5951
|
+
agent,
|
|
5952
|
+
{ notification: "closed", message: `Closed ${agent.id}.` },
|
|
5953
|
+
{ cancelled }
|
|
5954
|
+
);
|
|
5955
|
+
}
|
|
5956
|
+
})
|
|
5957
|
+
};
|
|
5958
|
+
return {
|
|
5959
|
+
tools,
|
|
5960
|
+
closeAll: async () => {
|
|
5961
|
+
const running = [];
|
|
5962
|
+
for (const agent of agents.values()) {
|
|
5963
|
+
if (agent.status !== "closed") {
|
|
5964
|
+
closeSubagent(agent, `Parent agent loop closed ${agent.id}.`);
|
|
5965
|
+
}
|
|
5966
|
+
if (agent.runningPromise) {
|
|
5967
|
+
running.push(agent.runningPromise);
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
if (running.length > 0) {
|
|
5971
|
+
await Promise.race([Promise.allSettled(running), sleep2(2e3)]);
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
};
|
|
5975
|
+
}
|
|
5976
|
+
function requireAgent(agents, id) {
|
|
5977
|
+
const agent = agents.get(id);
|
|
5978
|
+
if (!agent) {
|
|
5979
|
+
throw new Error(`Unknown subagent id: ${id}`);
|
|
5980
|
+
}
|
|
5981
|
+
return agent;
|
|
5982
|
+
}
|
|
5983
|
+
function resolveAgentIdValue(agentId, idAlias) {
|
|
5984
|
+
const preferred = agentId?.trim();
|
|
5985
|
+
if (preferred) {
|
|
5986
|
+
return preferred;
|
|
5987
|
+
}
|
|
5988
|
+
const alias = idAlias?.trim();
|
|
5989
|
+
return alias ?? "";
|
|
5990
|
+
}
|
|
5991
|
+
function resolveAgentIdList(agentId, idAlias, ids) {
|
|
5992
|
+
if (Array.isArray(ids) && ids.length > 0) {
|
|
5993
|
+
return [...new Set(ids.map((value) => value.trim()).filter(Boolean))];
|
|
5994
|
+
}
|
|
5995
|
+
const single = resolveAgentIdValue(agentId, idAlias);
|
|
5996
|
+
return single ? [single] : [];
|
|
5997
|
+
}
|
|
5998
|
+
function resolvePromptValue(prompt, message, items) {
|
|
5999
|
+
const promptValue = prompt?.trim();
|
|
6000
|
+
if (promptValue) {
|
|
6001
|
+
return promptValue;
|
|
6002
|
+
}
|
|
6003
|
+
const messageValue = message?.trim();
|
|
6004
|
+
if (messageValue) {
|
|
6005
|
+
return messageValue;
|
|
6006
|
+
}
|
|
6007
|
+
const itemText = resolveInputItemsText(items);
|
|
6008
|
+
return itemText ?? "";
|
|
6009
|
+
}
|
|
6010
|
+
function resolveInputItemsText(items) {
|
|
6011
|
+
if (!items || items.length === 0) {
|
|
6012
|
+
return void 0;
|
|
6013
|
+
}
|
|
6014
|
+
const lines = [];
|
|
6015
|
+
for (const item of items) {
|
|
6016
|
+
if (typeof item.text === "string" && item.text.trim().length > 0) {
|
|
6017
|
+
lines.push(item.text.trim());
|
|
6018
|
+
continue;
|
|
6019
|
+
}
|
|
6020
|
+
const itemType = typeof item.type === "string" ? item.type.trim() : "";
|
|
6021
|
+
const name = typeof item.name === "string" ? item.name.trim() : "";
|
|
6022
|
+
const path6 = typeof item.path === "string" ? item.path.trim() : "";
|
|
6023
|
+
const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
|
|
6024
|
+
const compact = [itemType, name, path6 || imageUrl].filter(Boolean).join(" ");
|
|
6025
|
+
if (compact) {
|
|
6026
|
+
lines.push(compact);
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
if (lines.length === 0) {
|
|
6030
|
+
return void 0;
|
|
6031
|
+
}
|
|
6032
|
+
return lines.join("\n");
|
|
6033
|
+
}
|
|
6034
|
+
function countActiveAgents(agents) {
|
|
6035
|
+
let count = 0;
|
|
6036
|
+
for (const agent of agents.values()) {
|
|
6037
|
+
if (agent.status !== "closed") {
|
|
6038
|
+
count += 1;
|
|
6039
|
+
}
|
|
6040
|
+
}
|
|
6041
|
+
return count;
|
|
6042
|
+
}
|
|
6043
|
+
async function waitForAnyAgentStatus(agents, ids, timeoutMs) {
|
|
6044
|
+
const requested = ids.map((id) => requireAgent(agents, id));
|
|
6045
|
+
const deadline = Date.now() + timeoutMs;
|
|
6046
|
+
while (true) {
|
|
6047
|
+
const status = {};
|
|
6048
|
+
for (const agent of requested) {
|
|
6049
|
+
if (agent.status !== "running") {
|
|
6050
|
+
status[agent.id] = buildSnapshot(agent);
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
if (Object.keys(status).length > 0) {
|
|
6054
|
+
return status;
|
|
6055
|
+
}
|
|
6056
|
+
const remaining = deadline - Date.now();
|
|
6057
|
+
if (remaining <= 0) {
|
|
6058
|
+
return {};
|
|
6059
|
+
}
|
|
6060
|
+
await Promise.race(
|
|
6061
|
+
requested.map(async (agent) => {
|
|
6062
|
+
const changed = await waitForVersionChange(agent, agent.version, remaining);
|
|
6063
|
+
if (!changed) {
|
|
6064
|
+
return;
|
|
6065
|
+
}
|
|
6066
|
+
})
|
|
6067
|
+
);
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
function setNotification(agent, notification, message) {
|
|
6071
|
+
agent.notification = notification;
|
|
6072
|
+
agent.notificationMessage = message;
|
|
6073
|
+
agent.updatedAtMs = Date.now();
|
|
6074
|
+
agent.version += 1;
|
|
6075
|
+
notifyWaiters(agent);
|
|
6076
|
+
}
|
|
6077
|
+
function setLifecycle(agent, status, notification, message) {
|
|
6078
|
+
agent.status = status;
|
|
6079
|
+
setNotification(agent, notification, message);
|
|
6080
|
+
}
|
|
6081
|
+
function notifyWaiters(agent) {
|
|
6082
|
+
if (agent.waiters.size === 0) {
|
|
6083
|
+
return;
|
|
6084
|
+
}
|
|
6085
|
+
const waiters = [...agent.waiters];
|
|
6086
|
+
agent.waiters.clear();
|
|
6087
|
+
for (const notify of waiters) {
|
|
6088
|
+
notify();
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
function startRun(agent, options) {
|
|
6092
|
+
if (agent.runningPromise) {
|
|
6093
|
+
return "already_running";
|
|
6094
|
+
}
|
|
6095
|
+
const nextInput = agent.pendingInputs.shift();
|
|
6096
|
+
if (!nextInput) {
|
|
6097
|
+
return "no_pending_input";
|
|
6098
|
+
}
|
|
6099
|
+
const input = [...agent.history, { role: "user", content: nextInput }];
|
|
6100
|
+
const abortController = new AbortController();
|
|
6101
|
+
agent.abortController = abortController;
|
|
6102
|
+
agent.lastError = void 0;
|
|
6103
|
+
setLifecycle(
|
|
6104
|
+
agent,
|
|
6105
|
+
"running",
|
|
6106
|
+
"run_started",
|
|
6107
|
+
`Subagent ${agent.id} started run ${agent.turns + 1}.`
|
|
6108
|
+
);
|
|
6109
|
+
const runPromise = (async () => {
|
|
6110
|
+
try {
|
|
6111
|
+
const result = await options.runSubagent({
|
|
6112
|
+
agentId: agent.id,
|
|
6113
|
+
depth: agent.depth,
|
|
6114
|
+
model: agent.model,
|
|
6115
|
+
input,
|
|
6116
|
+
instructions: agent.instructions,
|
|
6117
|
+
maxSteps: agent.maxSteps,
|
|
6118
|
+
signal: abortController.signal
|
|
6119
|
+
});
|
|
6120
|
+
if (agent.status === "closed") {
|
|
6121
|
+
return;
|
|
6122
|
+
}
|
|
6123
|
+
agent.lastResult = result;
|
|
6124
|
+
agent.lastError = void 0;
|
|
6125
|
+
agent.turns += 1;
|
|
6126
|
+
agent.history = [...input, { role: "assistant", content: result.text }];
|
|
6127
|
+
setLifecycle(
|
|
6128
|
+
agent,
|
|
6129
|
+
"idle",
|
|
6130
|
+
"run_completed",
|
|
6131
|
+
`Subagent ${agent.id} completed run ${agent.turns}.`
|
|
6132
|
+
);
|
|
6133
|
+
} catch (error) {
|
|
6134
|
+
if (agent.status === "closed") {
|
|
6135
|
+
return;
|
|
6136
|
+
}
|
|
6137
|
+
if (abortController.signal.aborted) {
|
|
6138
|
+
setLifecycle(agent, "idle", "input_queued", `Subagent ${agent.id} run interrupted.`);
|
|
6139
|
+
return;
|
|
6140
|
+
}
|
|
6141
|
+
const message = toErrorMessage(error);
|
|
6142
|
+
agent.lastError = message;
|
|
6143
|
+
setLifecycle(agent, "failed", "run_failed", `Subagent ${agent.id} failed: ${message}`);
|
|
6144
|
+
} finally {
|
|
6145
|
+
agent.runningPromise = void 0;
|
|
6146
|
+
agent.abortController = void 0;
|
|
6147
|
+
}
|
|
6148
|
+
})();
|
|
6149
|
+
agent.runningPromise = runPromise;
|
|
6150
|
+
return "started";
|
|
6151
|
+
}
|
|
6152
|
+
function closeSubagent(agent, message) {
|
|
6153
|
+
const cancelled = Boolean(agent.runningPromise);
|
|
6154
|
+
agent.pendingInputs = [];
|
|
6155
|
+
if (agent.abortController) {
|
|
6156
|
+
agent.abortController.abort("close_agent");
|
|
6157
|
+
}
|
|
6158
|
+
setLifecycle(agent, "closed", "closed", message);
|
|
6159
|
+
return cancelled;
|
|
6160
|
+
}
|
|
6161
|
+
async function waitUntilNotRunning(agent, timeoutMs) {
|
|
6162
|
+
const deadline = Date.now() + timeoutMs;
|
|
6163
|
+
while (agent.status === "running") {
|
|
6164
|
+
const remaining = deadline - Date.now();
|
|
6165
|
+
if (remaining <= 0) {
|
|
6166
|
+
return false;
|
|
6167
|
+
}
|
|
6168
|
+
const currentVersion = agent.version;
|
|
6169
|
+
const changed = await waitForVersionChange(agent, currentVersion, remaining);
|
|
6170
|
+
if (!changed) {
|
|
6171
|
+
return false;
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
return true;
|
|
6175
|
+
}
|
|
6176
|
+
async function waitForVersionChange(agent, version, timeoutMs) {
|
|
6177
|
+
if (agent.version !== version) {
|
|
6178
|
+
return true;
|
|
6179
|
+
}
|
|
6180
|
+
return await new Promise((resolve) => {
|
|
6181
|
+
const waiter = () => {
|
|
6182
|
+
cleanup();
|
|
6183
|
+
resolve(true);
|
|
6184
|
+
};
|
|
6185
|
+
const timeout = setTimeout(() => {
|
|
6186
|
+
cleanup();
|
|
6187
|
+
resolve(false);
|
|
6188
|
+
}, timeoutMs);
|
|
6189
|
+
const cleanup = () => {
|
|
6190
|
+
clearTimeout(timeout);
|
|
6191
|
+
agent.waiters.delete(waiter);
|
|
6192
|
+
};
|
|
6193
|
+
agent.waiters.add(waiter);
|
|
6194
|
+
});
|
|
6195
|
+
}
|
|
6196
|
+
function buildToolResponse(agent, override, extra = {}) {
|
|
6197
|
+
const notification = override?.notification ?? agent.notification;
|
|
6198
|
+
const message = override?.message ?? agent.notificationMessage;
|
|
6199
|
+
const snapshot = buildSnapshot(agent);
|
|
6200
|
+
return {
|
|
6201
|
+
agent_id: snapshot.agent_id,
|
|
6202
|
+
notification,
|
|
6203
|
+
message,
|
|
6204
|
+
status: snapshot.status,
|
|
6205
|
+
agent: snapshot,
|
|
6206
|
+
tool_availability: snapshot.status === "closed" ? [] : [...SUBAGENT_CONTROL_TOOL_NAMES],
|
|
6207
|
+
...extra
|
|
6208
|
+
};
|
|
6209
|
+
}
|
|
6210
|
+
function buildSnapshot(agent) {
|
|
6211
|
+
return {
|
|
6212
|
+
agent_id: agent.id,
|
|
6213
|
+
status: agent.status,
|
|
6214
|
+
depth: agent.depth,
|
|
6215
|
+
model: agent.model,
|
|
6216
|
+
pending_inputs: agent.pendingInputs.length,
|
|
6217
|
+
turns: agent.turns,
|
|
6218
|
+
created_at: new Date(agent.createdAtMs).toISOString(),
|
|
6219
|
+
updated_at: new Date(agent.updatedAtMs).toISOString(),
|
|
6220
|
+
...agent.lastError ? { last_error: agent.lastError } : {},
|
|
6221
|
+
...agent.lastResult ? {
|
|
6222
|
+
last_result: {
|
|
6223
|
+
text: agent.lastResult.text,
|
|
6224
|
+
thoughts: agent.lastResult.thoughts,
|
|
6225
|
+
step_count: agent.lastResult.steps.length,
|
|
6226
|
+
total_cost_usd: agent.lastResult.totalCostUsd
|
|
6227
|
+
}
|
|
6228
|
+
} : {}
|
|
6229
|
+
};
|
|
6230
|
+
}
|
|
6231
|
+
function normalizeInteger(value, fallback, min, max) {
|
|
6232
|
+
const parsed = Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
6233
|
+
return Math.max(min, Math.min(max, parsed));
|
|
6234
|
+
}
|
|
6235
|
+
function normalizeOptionalInteger(value, min, max) {
|
|
6236
|
+
if (!Number.isFinite(value)) {
|
|
6237
|
+
return void 0;
|
|
6238
|
+
}
|
|
6239
|
+
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
6240
|
+
}
|
|
6241
|
+
function trimToUndefined(value) {
|
|
6242
|
+
const trimmed = value?.trim();
|
|
6243
|
+
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
6244
|
+
}
|
|
6245
|
+
function toErrorMessage(error) {
|
|
6246
|
+
if (error instanceof Error) {
|
|
6247
|
+
return error.message;
|
|
6248
|
+
}
|
|
6249
|
+
return String(error);
|
|
6250
|
+
}
|
|
6251
|
+
function sleep2(ms) {
|
|
6252
|
+
return new Promise((resolve) => {
|
|
6253
|
+
setTimeout(resolve, ms);
|
|
6254
|
+
});
|
|
6255
|
+
}
|
|
6256
|
+
|
|
5454
6257
|
// src/tools/filesystemTools.ts
|
|
5455
6258
|
import path5 from "path";
|
|
5456
|
-
import { z as
|
|
6259
|
+
import { z as z6 } from "zod";
|
|
5457
6260
|
|
|
5458
6261
|
// src/tools/applyPatch.ts
|
|
5459
6262
|
import path4 from "path";
|
|
5460
|
-
import { z as
|
|
6263
|
+
import { z as z5 } from "zod";
|
|
5461
6264
|
|
|
5462
6265
|
// src/tools/filesystem.ts
|
|
5463
6266
|
import { promises as fs3 } from "fs";
|
|
@@ -5753,8 +6556,8 @@ var CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION = [
|
|
|
5753
6556
|
"- You must prefix new lines with `+` even when creating a new file",
|
|
5754
6557
|
"- File references can only be relative, NEVER ABSOLUTE."
|
|
5755
6558
|
].join("\n");
|
|
5756
|
-
var applyPatchToolInputSchema =
|
|
5757
|
-
input:
|
|
6559
|
+
var applyPatchToolInputSchema = z5.object({
|
|
6560
|
+
input: z5.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
5758
6561
|
});
|
|
5759
6562
|
function createApplyPatchTool(options = {}) {
|
|
5760
6563
|
return tool({
|
|
@@ -6162,100 +6965,100 @@ var MAX_GREP_LIMIT = 2e3;
|
|
|
6162
6965
|
var DEFAULT_MAX_LINE_LENGTH = 500;
|
|
6163
6966
|
var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
|
|
6164
6967
|
var DEFAULT_TAB_WIDTH = 4;
|
|
6165
|
-
var codexReadFileInputSchema =
|
|
6166
|
-
file_path:
|
|
6167
|
-
offset:
|
|
6168
|
-
limit:
|
|
6169
|
-
mode:
|
|
6170
|
-
indentation:
|
|
6171
|
-
anchor_line:
|
|
6172
|
-
max_levels:
|
|
6173
|
-
include_siblings:
|
|
6174
|
-
include_header:
|
|
6175
|
-
max_lines:
|
|
6968
|
+
var codexReadFileInputSchema = z6.object({
|
|
6969
|
+
file_path: z6.string().min(1).describe("Absolute path to the file"),
|
|
6970
|
+
offset: z6.number().int().min(1).optional().describe("The line number to start reading from. Must be 1 or greater."),
|
|
6971
|
+
limit: z6.number().int().min(1).optional().describe("The maximum number of lines to return."),
|
|
6972
|
+
mode: z6.enum(["slice", "indentation"]).optional().describe('Optional mode selector: "slice" (default) or "indentation".'),
|
|
6973
|
+
indentation: z6.object({
|
|
6974
|
+
anchor_line: z6.number().int().min(1).optional(),
|
|
6975
|
+
max_levels: z6.number().int().min(0).optional(),
|
|
6976
|
+
include_siblings: z6.boolean().optional(),
|
|
6977
|
+
include_header: z6.boolean().optional(),
|
|
6978
|
+
max_lines: z6.number().int().min(1).optional()
|
|
6176
6979
|
}).optional()
|
|
6177
6980
|
});
|
|
6178
|
-
var codexListDirInputSchema =
|
|
6179
|
-
dir_path:
|
|
6180
|
-
offset:
|
|
6181
|
-
limit:
|
|
6182
|
-
depth:
|
|
6981
|
+
var codexListDirInputSchema = z6.object({
|
|
6982
|
+
dir_path: z6.string().min(1).describe("Absolute path to the directory to list."),
|
|
6983
|
+
offset: z6.number().int().min(1).optional().describe("The entry number to start listing from. Must be 1 or greater."),
|
|
6984
|
+
limit: z6.number().int().min(1).optional().describe("The maximum number of entries to return."),
|
|
6985
|
+
depth: z6.number().int().min(1).optional().describe("The maximum directory depth to traverse. Must be 1 or greater.")
|
|
6183
6986
|
});
|
|
6184
|
-
var codexGrepFilesInputSchema =
|
|
6185
|
-
pattern:
|
|
6186
|
-
include:
|
|
6187
|
-
path:
|
|
6188
|
-
limit:
|
|
6987
|
+
var codexGrepFilesInputSchema = z6.object({
|
|
6988
|
+
pattern: z6.string().min(1).describe("Regular expression pattern to search for."),
|
|
6989
|
+
include: z6.string().optional().describe('Optional glob limiting searched files (for example "*.rs").'),
|
|
6990
|
+
path: z6.string().optional().describe("Directory or file path to search. Defaults to cwd."),
|
|
6991
|
+
limit: z6.number().int().min(1).optional().describe("Maximum number of file paths to return (defaults to 100).")
|
|
6189
6992
|
});
|
|
6190
|
-
var applyPatchInputSchema =
|
|
6191
|
-
input:
|
|
6993
|
+
var applyPatchInputSchema = z6.object({
|
|
6994
|
+
input: z6.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
6192
6995
|
});
|
|
6193
|
-
var geminiReadFileInputSchema =
|
|
6194
|
-
file_path:
|
|
6195
|
-
offset:
|
|
6196
|
-
limit:
|
|
6996
|
+
var geminiReadFileInputSchema = z6.object({
|
|
6997
|
+
file_path: z6.string().min(1),
|
|
6998
|
+
offset: z6.number().int().min(0).nullish(),
|
|
6999
|
+
limit: z6.number().int().min(1).nullish()
|
|
6197
7000
|
});
|
|
6198
|
-
var geminiReadFilesInputSchema =
|
|
6199
|
-
paths:
|
|
6200
|
-
line_offset:
|
|
6201
|
-
line_limit:
|
|
6202
|
-
char_offset:
|
|
6203
|
-
char_limit:
|
|
6204
|
-
include_line_numbers:
|
|
7001
|
+
var geminiReadFilesInputSchema = z6.object({
|
|
7002
|
+
paths: z6.array(z6.string().min(1)).min(1),
|
|
7003
|
+
line_offset: z6.number().int().min(0).nullish(),
|
|
7004
|
+
line_limit: z6.number().int().min(1).nullish(),
|
|
7005
|
+
char_offset: z6.number().int().min(0).nullish(),
|
|
7006
|
+
char_limit: z6.number().int().min(1).nullish(),
|
|
7007
|
+
include_line_numbers: z6.boolean().nullish()
|
|
6205
7008
|
}).superRefine((value, context) => {
|
|
6206
7009
|
const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
|
|
6207
7010
|
const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
|
|
6208
7011
|
if (hasLineWindow && hasCharWindow) {
|
|
6209
7012
|
context.addIssue({
|
|
6210
|
-
code:
|
|
7013
|
+
code: z6.ZodIssueCode.custom,
|
|
6211
7014
|
message: "Use either line_* or char_* window arguments, not both."
|
|
6212
7015
|
});
|
|
6213
7016
|
}
|
|
6214
7017
|
});
|
|
6215
|
-
var geminiWriteFileInputSchema =
|
|
6216
|
-
file_path:
|
|
6217
|
-
content:
|
|
7018
|
+
var geminiWriteFileInputSchema = z6.object({
|
|
7019
|
+
file_path: z6.string().min(1),
|
|
7020
|
+
content: z6.string()
|
|
6218
7021
|
});
|
|
6219
|
-
var geminiReplaceInputSchema =
|
|
6220
|
-
file_path:
|
|
6221
|
-
instruction:
|
|
6222
|
-
old_string:
|
|
6223
|
-
new_string:
|
|
6224
|
-
expected_replacements:
|
|
7022
|
+
var geminiReplaceInputSchema = z6.object({
|
|
7023
|
+
file_path: z6.string().min(1),
|
|
7024
|
+
instruction: z6.string().min(1),
|
|
7025
|
+
old_string: z6.string(),
|
|
7026
|
+
new_string: z6.string(),
|
|
7027
|
+
expected_replacements: z6.number().int().min(1).nullish()
|
|
6225
7028
|
});
|
|
6226
|
-
var geminiListDirectoryInputSchema =
|
|
6227
|
-
dir_path:
|
|
6228
|
-
ignore:
|
|
6229
|
-
file_filtering_options:
|
|
6230
|
-
respect_git_ignore:
|
|
6231
|
-
respect_gemini_ignore:
|
|
7029
|
+
var geminiListDirectoryInputSchema = z6.object({
|
|
7030
|
+
dir_path: z6.string().min(1),
|
|
7031
|
+
ignore: z6.array(z6.string()).nullish(),
|
|
7032
|
+
file_filtering_options: z6.object({
|
|
7033
|
+
respect_git_ignore: z6.boolean().nullish(),
|
|
7034
|
+
respect_gemini_ignore: z6.boolean().nullish()
|
|
6232
7035
|
}).nullish()
|
|
6233
7036
|
});
|
|
6234
|
-
var geminiRgSearchInputSchema =
|
|
6235
|
-
pattern:
|
|
6236
|
-
path:
|
|
6237
|
-
glob:
|
|
6238
|
-
case_sensitive:
|
|
6239
|
-
exclude_pattern:
|
|
6240
|
-
names_only:
|
|
6241
|
-
max_matches_per_file:
|
|
6242
|
-
max_results:
|
|
7037
|
+
var geminiRgSearchInputSchema = z6.object({
|
|
7038
|
+
pattern: z6.string().min(1),
|
|
7039
|
+
path: z6.string().nullish(),
|
|
7040
|
+
glob: z6.string().nullish(),
|
|
7041
|
+
case_sensitive: z6.boolean().nullish(),
|
|
7042
|
+
exclude_pattern: z6.string().nullish(),
|
|
7043
|
+
names_only: z6.boolean().nullish(),
|
|
7044
|
+
max_matches_per_file: z6.number().int().min(1).nullish(),
|
|
7045
|
+
max_results: z6.number().int().min(1).nullish()
|
|
6243
7046
|
});
|
|
6244
|
-
var geminiGrepSearchInputSchema =
|
|
6245
|
-
pattern:
|
|
6246
|
-
dir_path:
|
|
6247
|
-
include:
|
|
6248
|
-
exclude_pattern:
|
|
6249
|
-
names_only:
|
|
6250
|
-
max_matches_per_file:
|
|
6251
|
-
total_max_matches:
|
|
7047
|
+
var geminiGrepSearchInputSchema = z6.object({
|
|
7048
|
+
pattern: z6.string().min(1),
|
|
7049
|
+
dir_path: z6.string().nullish(),
|
|
7050
|
+
include: z6.string().nullish(),
|
|
7051
|
+
exclude_pattern: z6.string().nullish(),
|
|
7052
|
+
names_only: z6.boolean().nullish(),
|
|
7053
|
+
max_matches_per_file: z6.number().int().min(1).nullish(),
|
|
7054
|
+
total_max_matches: z6.number().int().min(1).nullish()
|
|
6252
7055
|
});
|
|
6253
|
-
var geminiGlobInputSchema =
|
|
6254
|
-
pattern:
|
|
6255
|
-
dir_path:
|
|
6256
|
-
case_sensitive:
|
|
6257
|
-
respect_git_ignore:
|
|
6258
|
-
respect_gemini_ignore:
|
|
7056
|
+
var geminiGlobInputSchema = z6.object({
|
|
7057
|
+
pattern: z6.string().min(1),
|
|
7058
|
+
dir_path: z6.string().nullish(),
|
|
7059
|
+
case_sensitive: z6.boolean().nullish(),
|
|
7060
|
+
respect_git_ignore: z6.boolean().nullish(),
|
|
7061
|
+
respect_gemini_ignore: z6.boolean().nullish()
|
|
6259
7062
|
});
|
|
6260
7063
|
function resolveFilesystemToolProfile(model, profile = "auto") {
|
|
6261
7064
|
if (profile !== "auto") {
|
|
@@ -7243,19 +8046,54 @@ function isNoEntError(error) {
|
|
|
7243
8046
|
|
|
7244
8047
|
// src/agent.ts
|
|
7245
8048
|
async function runAgentLoop(request) {
|
|
7246
|
-
|
|
8049
|
+
return await runAgentLoopInternal(request, { depth: 0 });
|
|
8050
|
+
}
|
|
8051
|
+
async function runAgentLoopInternal(request, context) {
|
|
8052
|
+
const {
|
|
8053
|
+
tools: customTools,
|
|
8054
|
+
filesystemTool,
|
|
8055
|
+
filesystem_tool,
|
|
8056
|
+
subagentTool,
|
|
8057
|
+
subagent_tool,
|
|
8058
|
+
subagents,
|
|
8059
|
+
...toolLoopRequest
|
|
8060
|
+
} = request;
|
|
7247
8061
|
const filesystemSelection = filesystemTool ?? filesystem_tool;
|
|
8062
|
+
const subagentSelection = subagentTool ?? subagent_tool ?? subagents;
|
|
7248
8063
|
const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
|
|
7249
|
-
const
|
|
8064
|
+
const resolvedSubagentConfig = resolveSubagentToolConfig(subagentSelection, context.depth);
|
|
8065
|
+
const subagentController = createSubagentController({
|
|
8066
|
+
model: request.model,
|
|
8067
|
+
depth: context.depth,
|
|
8068
|
+
customTools: customTools ?? {},
|
|
8069
|
+
filesystemSelection,
|
|
8070
|
+
subagentSelection,
|
|
8071
|
+
toolLoopRequest,
|
|
8072
|
+
resolvedSubagentConfig
|
|
8073
|
+
});
|
|
8074
|
+
const mergedTools = mergeToolSets(
|
|
8075
|
+
mergeToolSets(filesystemTools, subagentController?.tools ?? {}),
|
|
8076
|
+
customTools ?? {}
|
|
8077
|
+
);
|
|
7250
8078
|
if (Object.keys(mergedTools).length === 0) {
|
|
7251
8079
|
throw new Error(
|
|
7252
|
-
"runAgentLoop requires at least one tool. Provide `tools` or enable `
|
|
8080
|
+
"runAgentLoop requires at least one tool. Provide `tools`, enable `filesystemTool`, or enable `subagentTool`."
|
|
7253
8081
|
);
|
|
7254
8082
|
}
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
8083
|
+
const instructions = buildLoopInstructions(
|
|
8084
|
+
toolLoopRequest.instructions,
|
|
8085
|
+
resolvedSubagentConfig,
|
|
8086
|
+
context.depth
|
|
8087
|
+
);
|
|
8088
|
+
try {
|
|
8089
|
+
return await runToolLoop({
|
|
8090
|
+
...toolLoopRequest,
|
|
8091
|
+
...instructions ? { instructions } : {},
|
|
8092
|
+
tools: mergedTools
|
|
8093
|
+
});
|
|
8094
|
+
} finally {
|
|
8095
|
+
await subagentController?.closeAll();
|
|
8096
|
+
}
|
|
7259
8097
|
}
|
|
7260
8098
|
function resolveFilesystemTools(model, selection) {
|
|
7261
8099
|
if (selection === void 0 || selection === false) {
|
|
@@ -7283,13 +8121,89 @@ function mergeToolSets(base, extra) {
|
|
|
7283
8121
|
for (const [toolName, toolSpec] of Object.entries(extra)) {
|
|
7284
8122
|
if (Object.hasOwn(merged, toolName)) {
|
|
7285
8123
|
throw new Error(
|
|
7286
|
-
`Duplicate tool name "${toolName}" in runAgentLoop. Rename the
|
|
8124
|
+
`Duplicate tool name "${toolName}" in runAgentLoop. Rename one of the conflicting tools or disable an overlapping built-in tool.`
|
|
7287
8125
|
);
|
|
7288
8126
|
}
|
|
7289
8127
|
merged[toolName] = toolSpec;
|
|
7290
8128
|
}
|
|
7291
8129
|
return merged;
|
|
7292
8130
|
}
|
|
8131
|
+
function createSubagentController(params) {
|
|
8132
|
+
if (!params.resolvedSubagentConfig.enabled) {
|
|
8133
|
+
return null;
|
|
8134
|
+
}
|
|
8135
|
+
return createSubagentToolController({
|
|
8136
|
+
config: params.resolvedSubagentConfig,
|
|
8137
|
+
parentDepth: params.depth,
|
|
8138
|
+
parentModel: params.resolvedSubagentConfig.model ?? params.model,
|
|
8139
|
+
buildChildInstructions: (spawnInstructions, childDepth) => buildChildInstructions(spawnInstructions, params.resolvedSubagentConfig, childDepth),
|
|
8140
|
+
runSubagent: async (subagentRequest) => {
|
|
8141
|
+
const childCustomTools = params.resolvedSubagentConfig.inheritTools ? params.customTools : {};
|
|
8142
|
+
const childFilesystemSelection = params.resolvedSubagentConfig.inheritFilesystemTool ? params.filesystemSelection : false;
|
|
8143
|
+
return await runAgentLoopInternal(
|
|
8144
|
+
{
|
|
8145
|
+
model: subagentRequest.model,
|
|
8146
|
+
input: subagentRequest.input,
|
|
8147
|
+
instructions: subagentRequest.instructions,
|
|
8148
|
+
tools: childCustomTools,
|
|
8149
|
+
filesystemTool: childFilesystemSelection,
|
|
8150
|
+
subagentTool: params.subagentSelection,
|
|
8151
|
+
modelTools: params.toolLoopRequest.modelTools,
|
|
8152
|
+
maxSteps: subagentRequest.maxSteps,
|
|
8153
|
+
openAiReasoningEffort: params.toolLoopRequest.openAiReasoningEffort,
|
|
8154
|
+
signal: subagentRequest.signal
|
|
8155
|
+
},
|
|
8156
|
+
{ depth: params.depth + 1 }
|
|
8157
|
+
);
|
|
8158
|
+
}
|
|
8159
|
+
});
|
|
8160
|
+
}
|
|
8161
|
+
function buildLoopInstructions(baseInstructions, config, depth) {
|
|
8162
|
+
if (!config.enabled) {
|
|
8163
|
+
return trimToUndefined2(baseInstructions);
|
|
8164
|
+
}
|
|
8165
|
+
const blocks = [];
|
|
8166
|
+
const base = trimToUndefined2(baseInstructions);
|
|
8167
|
+
if (base) {
|
|
8168
|
+
blocks.push(base);
|
|
8169
|
+
}
|
|
8170
|
+
if (config.promptPattern === "codex") {
|
|
8171
|
+
blocks.push(
|
|
8172
|
+
buildCodexSubagentOrchestratorInstructions({
|
|
8173
|
+
currentDepth: depth,
|
|
8174
|
+
maxDepth: config.maxDepth,
|
|
8175
|
+
maxAgents: config.maxAgents
|
|
8176
|
+
})
|
|
8177
|
+
);
|
|
8178
|
+
}
|
|
8179
|
+
if (config.instructions) {
|
|
8180
|
+
blocks.push(config.instructions);
|
|
8181
|
+
}
|
|
8182
|
+
return blocks.length > 0 ? blocks.join("\n\n") : void 0;
|
|
8183
|
+
}
|
|
8184
|
+
function buildChildInstructions(spawnInstructions, config, childDepth) {
|
|
8185
|
+
const blocks = [];
|
|
8186
|
+
if (config.promptPattern === "codex") {
|
|
8187
|
+
blocks.push(
|
|
8188
|
+
buildCodexSubagentWorkerInstructions({
|
|
8189
|
+
depth: childDepth,
|
|
8190
|
+
maxDepth: config.maxDepth
|
|
8191
|
+
})
|
|
8192
|
+
);
|
|
8193
|
+
}
|
|
8194
|
+
if (config.instructions) {
|
|
8195
|
+
blocks.push(config.instructions);
|
|
8196
|
+
}
|
|
8197
|
+
const perSpawn = trimToUndefined2(spawnInstructions);
|
|
8198
|
+
if (perSpawn) {
|
|
8199
|
+
blocks.push(perSpawn);
|
|
8200
|
+
}
|
|
8201
|
+
return blocks.length > 0 ? blocks.join("\n\n") : void 0;
|
|
8202
|
+
}
|
|
8203
|
+
function trimToUndefined2(value) {
|
|
8204
|
+
const trimmed = value?.trim();
|
|
8205
|
+
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
8206
|
+
}
|
|
7293
8207
|
export {
|
|
7294
8208
|
CHATGPT_MODEL_IDS,
|
|
7295
8209
|
CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
|