@ljoukov/llm 3.0.3 → 3.0.6
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 +136 -8
- package/dist/index.cjs +1734 -278
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -1
- package/dist/index.d.ts +89 -1
- package/dist/index.js +1732 -278
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1582,6 +1582,123 @@ 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
|
+
var DEFAULT_OPENAI_MODEL_CONCURRENCY_CAP = 12;
|
|
1590
|
+
var DEFAULT_GOOGLE_MODEL_CONCURRENCY_CAP = 4;
|
|
1591
|
+
var DEFAULT_GOOGLE_PREVIEW_MODEL_CONCURRENCY_CAP = 2;
|
|
1592
|
+
var DEFAULT_FIREWORKS_MODEL_CONCURRENCY_CAP = 6;
|
|
1593
|
+
var MODEL_CONCURRENCY_PROVIDERS = [
|
|
1594
|
+
"openai",
|
|
1595
|
+
"google",
|
|
1596
|
+
"fireworks"
|
|
1597
|
+
];
|
|
1598
|
+
var configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1599
|
+
function clampModelConcurrencyCap(value) {
|
|
1600
|
+
if (!Number.isFinite(value)) {
|
|
1601
|
+
return DEFAULT_MODEL_CONCURRENCY_CAP;
|
|
1602
|
+
}
|
|
1603
|
+
const rounded = Math.floor(value);
|
|
1604
|
+
if (rounded < MIN_MODEL_CONCURRENCY_CAP) {
|
|
1605
|
+
return MIN_MODEL_CONCURRENCY_CAP;
|
|
1606
|
+
}
|
|
1607
|
+
if (rounded > MAX_MODEL_CONCURRENCY_CAP) {
|
|
1608
|
+
return MAX_MODEL_CONCURRENCY_CAP;
|
|
1609
|
+
}
|
|
1610
|
+
return rounded;
|
|
1611
|
+
}
|
|
1612
|
+
function normalizeModelIdForConfig(modelId) {
|
|
1613
|
+
return modelId.trim().toLowerCase();
|
|
1614
|
+
}
|
|
1615
|
+
function normalizeCap(value) {
|
|
1616
|
+
if (value === void 0 || !Number.isFinite(value)) {
|
|
1617
|
+
return void 0;
|
|
1618
|
+
}
|
|
1619
|
+
return clampModelConcurrencyCap(value);
|
|
1620
|
+
}
|
|
1621
|
+
function normalizeModelCapMap(caps) {
|
|
1622
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
1623
|
+
if (!caps) {
|
|
1624
|
+
return normalized;
|
|
1625
|
+
}
|
|
1626
|
+
for (const [modelId, cap] of Object.entries(caps)) {
|
|
1627
|
+
const modelKey = normalizeModelIdForConfig(modelId);
|
|
1628
|
+
if (!modelKey) {
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
const normalizedCap = normalizeCap(cap);
|
|
1632
|
+
if (normalizedCap === void 0) {
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
normalized.set(modelKey, normalizedCap);
|
|
1636
|
+
}
|
|
1637
|
+
return normalized;
|
|
1638
|
+
}
|
|
1639
|
+
function normalizeModelConcurrencyConfig(config) {
|
|
1640
|
+
const providerCaps = {};
|
|
1641
|
+
const providerModelCaps = {
|
|
1642
|
+
openai: /* @__PURE__ */ new Map(),
|
|
1643
|
+
google: /* @__PURE__ */ new Map(),
|
|
1644
|
+
fireworks: /* @__PURE__ */ new Map()
|
|
1645
|
+
};
|
|
1646
|
+
for (const provider of MODEL_CONCURRENCY_PROVIDERS) {
|
|
1647
|
+
const providerCap = normalizeCap(config.providerCaps?.[provider]);
|
|
1648
|
+
if (providerCap !== void 0) {
|
|
1649
|
+
providerCaps[provider] = providerCap;
|
|
1650
|
+
}
|
|
1651
|
+
providerModelCaps[provider] = new Map(
|
|
1652
|
+
normalizeModelCapMap(config.providerModelCaps?.[provider])
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
return {
|
|
1656
|
+
globalCap: normalizeCap(config.globalCap),
|
|
1657
|
+
providerCaps,
|
|
1658
|
+
modelCaps: normalizeModelCapMap(config.modelCaps),
|
|
1659
|
+
providerModelCaps
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
function resolveDefaultProviderCap(provider, modelId) {
|
|
1663
|
+
if (provider === "openai") {
|
|
1664
|
+
return DEFAULT_OPENAI_MODEL_CONCURRENCY_CAP;
|
|
1665
|
+
}
|
|
1666
|
+
if (provider === "google") {
|
|
1667
|
+
return modelId?.includes("preview") ? DEFAULT_GOOGLE_PREVIEW_MODEL_CONCURRENCY_CAP : DEFAULT_GOOGLE_MODEL_CONCURRENCY_CAP;
|
|
1668
|
+
}
|
|
1669
|
+
return DEFAULT_FIREWORKS_MODEL_CONCURRENCY_CAP;
|
|
1670
|
+
}
|
|
1671
|
+
function configureModelConcurrency(config = {}) {
|
|
1672
|
+
configuredModelConcurrency = normalizeModelConcurrencyConfig(config);
|
|
1673
|
+
}
|
|
1674
|
+
function resetModelConcurrencyConfig() {
|
|
1675
|
+
configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1676
|
+
}
|
|
1677
|
+
function resolveModelConcurrencyCap(options) {
|
|
1678
|
+
const modelId = options.modelId ? normalizeModelIdForConfig(options.modelId) : void 0;
|
|
1679
|
+
const config = options.config ? normalizeModelConcurrencyConfig(options.config) : configuredModelConcurrency;
|
|
1680
|
+
const providerModelCap = modelId ? config.providerModelCaps[options.provider].get(modelId) : void 0;
|
|
1681
|
+
if (providerModelCap !== void 0) {
|
|
1682
|
+
return providerModelCap;
|
|
1683
|
+
}
|
|
1684
|
+
const modelCap = modelId ? config.modelCaps.get(modelId) : void 0;
|
|
1685
|
+
if (modelCap !== void 0) {
|
|
1686
|
+
return modelCap;
|
|
1687
|
+
}
|
|
1688
|
+
const providerCap = config.providerCaps[options.provider];
|
|
1689
|
+
if (providerCap !== void 0) {
|
|
1690
|
+
return providerCap;
|
|
1691
|
+
}
|
|
1692
|
+
if (config.globalCap !== void 0) {
|
|
1693
|
+
return config.globalCap;
|
|
1694
|
+
}
|
|
1695
|
+
const defaultCap = normalizeCap(options.defaultCap);
|
|
1696
|
+
if (defaultCap !== void 0) {
|
|
1697
|
+
return defaultCap;
|
|
1698
|
+
}
|
|
1699
|
+
return resolveDefaultProviderCap(options.provider, modelId);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1585
1702
|
// src/utils/scheduler.ts
|
|
1586
1703
|
function sleep(ms) {
|
|
1587
1704
|
return new Promise((resolve) => {
|
|
@@ -1597,13 +1714,72 @@ function toError(value) {
|
|
|
1597
1714
|
}
|
|
1598
1715
|
return new Error("Unknown error");
|
|
1599
1716
|
}
|
|
1717
|
+
function getStatusCode(error) {
|
|
1718
|
+
if (!error || typeof error !== "object") {
|
|
1719
|
+
return void 0;
|
|
1720
|
+
}
|
|
1721
|
+
const maybe = error;
|
|
1722
|
+
const candidates = [maybe.status, maybe.statusCode];
|
|
1723
|
+
for (const candidate of candidates) {
|
|
1724
|
+
if (typeof candidate === "number") {
|
|
1725
|
+
return candidate;
|
|
1726
|
+
}
|
|
1727
|
+
if (typeof candidate === "string") {
|
|
1728
|
+
const parsed = Number.parseInt(candidate, 10);
|
|
1729
|
+
if (Number.isFinite(parsed)) {
|
|
1730
|
+
return parsed;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
if (typeof maybe.code === "number") {
|
|
1735
|
+
return maybe.code;
|
|
1736
|
+
}
|
|
1737
|
+
return void 0;
|
|
1738
|
+
}
|
|
1739
|
+
function getErrorText(error) {
|
|
1740
|
+
if (error instanceof Error) {
|
|
1741
|
+
return error.message.toLowerCase();
|
|
1742
|
+
}
|
|
1743
|
+
if (typeof error === "string") {
|
|
1744
|
+
return error.toLowerCase();
|
|
1745
|
+
}
|
|
1746
|
+
if (error && typeof error === "object") {
|
|
1747
|
+
const maybe = error;
|
|
1748
|
+
const code = typeof maybe.code === "string" ? maybe.code : "";
|
|
1749
|
+
const message = typeof maybe.message === "string" ? maybe.message : "";
|
|
1750
|
+
return `${code} ${message}`.trim().toLowerCase();
|
|
1751
|
+
}
|
|
1752
|
+
return "";
|
|
1753
|
+
}
|
|
1754
|
+
function defaultIsOverloadError(error) {
|
|
1755
|
+
const status = getStatusCode(error);
|
|
1756
|
+
if (status === 429 || status === 503 || status === 529) {
|
|
1757
|
+
return true;
|
|
1758
|
+
}
|
|
1759
|
+
const text = getErrorText(error);
|
|
1760
|
+
if (!text) {
|
|
1761
|
+
return false;
|
|
1762
|
+
}
|
|
1763
|
+
return text.includes("rate limit") || text.includes("too many requests") || text.includes("resource exhausted") || text.includes("resource_exhausted") || text.includes("overload");
|
|
1764
|
+
}
|
|
1600
1765
|
function createCallScheduler(options = {}) {
|
|
1601
1766
|
const maxParallelRequests = Math.max(1, Math.floor(options.maxParallelRequests ?? 3));
|
|
1767
|
+
const initialParallelRequests = Math.min(
|
|
1768
|
+
maxParallelRequests,
|
|
1769
|
+
Math.max(1, Math.floor(options.initialParallelRequests ?? Math.min(3, maxParallelRequests)))
|
|
1770
|
+
);
|
|
1771
|
+
const increaseAfterConsecutiveSuccesses = Math.max(
|
|
1772
|
+
1,
|
|
1773
|
+
Math.floor(options.increaseAfterConsecutiveSuccesses ?? 8)
|
|
1774
|
+
);
|
|
1602
1775
|
const minIntervalBetweenStartMs = Math.max(0, Math.floor(options.minIntervalBetweenStartMs ?? 0));
|
|
1603
1776
|
const startJitterMs = Math.max(0, Math.floor(options.startJitterMs ?? 0));
|
|
1604
1777
|
const retryPolicy = options.retry;
|
|
1778
|
+
const isOverloadError2 = options.isOverloadError ?? defaultIsOverloadError;
|
|
1605
1779
|
let activeCount = 0;
|
|
1606
1780
|
let lastStartTime = 0;
|
|
1781
|
+
let currentParallelLimit = initialParallelRequests;
|
|
1782
|
+
let consecutiveSuccesses = 0;
|
|
1607
1783
|
let startSpacingChain = Promise.resolve();
|
|
1608
1784
|
const queue = [];
|
|
1609
1785
|
async function applyStartSpacing() {
|
|
@@ -1629,11 +1805,23 @@ function createCallScheduler(options = {}) {
|
|
|
1629
1805
|
release?.();
|
|
1630
1806
|
}
|
|
1631
1807
|
}
|
|
1632
|
-
async function attemptWithRetries(fn, attempt) {
|
|
1808
|
+
async function attemptWithRetries(fn, attempt, state) {
|
|
1633
1809
|
try {
|
|
1810
|
+
const spacingStartedAtMs = Date.now();
|
|
1634
1811
|
await applyStartSpacing();
|
|
1812
|
+
const callStartedAtMs = Date.now();
|
|
1813
|
+
state.schedulerDelayMs += Math.max(0, callStartedAtMs - spacingStartedAtMs);
|
|
1814
|
+
if (state.startedAtMs === void 0) {
|
|
1815
|
+
state.startedAtMs = callStartedAtMs;
|
|
1816
|
+
}
|
|
1817
|
+
state.attempts = Math.max(state.attempts, attempt);
|
|
1635
1818
|
return await fn();
|
|
1636
1819
|
} catch (error) {
|
|
1820
|
+
if (isOverloadError2(error)) {
|
|
1821
|
+
state.overloadCount += 1;
|
|
1822
|
+
consecutiveSuccesses = 0;
|
|
1823
|
+
currentParallelLimit = Math.max(1, Math.ceil(currentParallelLimit / 2));
|
|
1824
|
+
}
|
|
1637
1825
|
const err = toError(error);
|
|
1638
1826
|
if (!retryPolicy || attempt >= retryPolicy.maxAttempts) {
|
|
1639
1827
|
throw err;
|
|
@@ -1647,13 +1835,14 @@ function createCallScheduler(options = {}) {
|
|
|
1647
1835
|
}
|
|
1648
1836
|
const normalizedDelay = Math.max(0, delay);
|
|
1649
1837
|
if (normalizedDelay > 0) {
|
|
1838
|
+
state.retryDelayMs += normalizedDelay;
|
|
1650
1839
|
await sleep(normalizedDelay);
|
|
1651
1840
|
}
|
|
1652
|
-
return attemptWithRetries(fn, attempt + 1);
|
|
1841
|
+
return attemptWithRetries(fn, attempt + 1, state);
|
|
1653
1842
|
}
|
|
1654
1843
|
}
|
|
1655
1844
|
function drainQueue() {
|
|
1656
|
-
while (activeCount <
|
|
1845
|
+
while (activeCount < currentParallelLimit && queue.length > 0) {
|
|
1657
1846
|
const task = queue.shift();
|
|
1658
1847
|
if (!task) {
|
|
1659
1848
|
continue;
|
|
@@ -1662,15 +1851,49 @@ function createCallScheduler(options = {}) {
|
|
|
1662
1851
|
void task();
|
|
1663
1852
|
}
|
|
1664
1853
|
}
|
|
1665
|
-
function run(fn) {
|
|
1854
|
+
function run(fn, runOptions = {}) {
|
|
1666
1855
|
return new Promise((resolve, reject) => {
|
|
1856
|
+
const enqueuedAtMs = Date.now();
|
|
1667
1857
|
const job = async () => {
|
|
1858
|
+
const dequeuedAtMs = Date.now();
|
|
1859
|
+
const state = {
|
|
1860
|
+
enqueuedAtMs,
|
|
1861
|
+
dequeuedAtMs,
|
|
1862
|
+
schedulerDelayMs: 0,
|
|
1863
|
+
retryDelayMs: 0,
|
|
1864
|
+
attempts: 0,
|
|
1865
|
+
overloadCount: 0
|
|
1866
|
+
};
|
|
1668
1867
|
try {
|
|
1669
|
-
const result = await attemptWithRetries(fn, 1);
|
|
1868
|
+
const result = await attemptWithRetries(fn, 1, state);
|
|
1869
|
+
state.completedAtMs = Date.now();
|
|
1870
|
+
consecutiveSuccesses += 1;
|
|
1871
|
+
if (currentParallelLimit < maxParallelRequests && consecutiveSuccesses >= increaseAfterConsecutiveSuccesses) {
|
|
1872
|
+
currentParallelLimit += 1;
|
|
1873
|
+
consecutiveSuccesses = 0;
|
|
1874
|
+
}
|
|
1670
1875
|
resolve(result);
|
|
1671
1876
|
} catch (error) {
|
|
1877
|
+
state.completedAtMs = Date.now();
|
|
1672
1878
|
reject(toError(error));
|
|
1673
1879
|
} finally {
|
|
1880
|
+
const startedAtMs = state.startedAtMs ?? state.dequeuedAtMs;
|
|
1881
|
+
const completedAtMs = state.completedAtMs ?? Date.now();
|
|
1882
|
+
const metrics = {
|
|
1883
|
+
enqueuedAtMs: state.enqueuedAtMs,
|
|
1884
|
+
dequeuedAtMs: state.dequeuedAtMs,
|
|
1885
|
+
startedAtMs,
|
|
1886
|
+
completedAtMs,
|
|
1887
|
+
queueWaitMs: Math.max(0, state.dequeuedAtMs - state.enqueuedAtMs),
|
|
1888
|
+
schedulerDelayMs: Math.max(0, state.schedulerDelayMs),
|
|
1889
|
+
retryDelayMs: Math.max(0, state.retryDelayMs),
|
|
1890
|
+
attempts: Math.max(1, state.attempts),
|
|
1891
|
+
overloadCount: Math.max(0, state.overloadCount)
|
|
1892
|
+
};
|
|
1893
|
+
try {
|
|
1894
|
+
runOptions.onSettled?.(metrics);
|
|
1895
|
+
} catch {
|
|
1896
|
+
}
|
|
1674
1897
|
activeCount -= 1;
|
|
1675
1898
|
queueMicrotask(drainQueue);
|
|
1676
1899
|
}
|
|
@@ -1756,13 +1979,28 @@ function getFireworksClient() {
|
|
|
1756
1979
|
}
|
|
1757
1980
|
|
|
1758
1981
|
// src/fireworks/calls.ts
|
|
1759
|
-
var
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1982
|
+
var DEFAULT_SCHEDULER_KEY = "__default__";
|
|
1983
|
+
var schedulerByModel = /* @__PURE__ */ new Map();
|
|
1984
|
+
function getSchedulerForModel(modelId) {
|
|
1985
|
+
const normalizedModelId = modelId?.trim();
|
|
1986
|
+
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY;
|
|
1987
|
+
const existing = schedulerByModel.get(schedulerKey);
|
|
1988
|
+
if (existing) {
|
|
1989
|
+
return existing;
|
|
1990
|
+
}
|
|
1991
|
+
const created = createCallScheduler({
|
|
1992
|
+
maxParallelRequests: resolveModelConcurrencyCap({
|
|
1993
|
+
provider: "fireworks",
|
|
1994
|
+
modelId: normalizedModelId
|
|
1995
|
+
}),
|
|
1996
|
+
minIntervalBetweenStartMs: 200,
|
|
1997
|
+
startJitterMs: 200
|
|
1998
|
+
});
|
|
1999
|
+
schedulerByModel.set(schedulerKey, created);
|
|
2000
|
+
return created;
|
|
2001
|
+
}
|
|
2002
|
+
async function runFireworksCall(fn, modelId, runOptions) {
|
|
2003
|
+
return getSchedulerForModel(modelId).run(async () => fn(getFireworksClient()), runOptions);
|
|
1766
2004
|
}
|
|
1767
2005
|
|
|
1768
2006
|
// src/fireworks/models.ts
|
|
@@ -2098,6 +2336,18 @@ function shouldRetry(error) {
|
|
|
2098
2336
|
}
|
|
2099
2337
|
return false;
|
|
2100
2338
|
}
|
|
2339
|
+
function isOverloadError(error) {
|
|
2340
|
+
const status = getStatus(error);
|
|
2341
|
+
if (status === 429 || status === 503 || status === 529) {
|
|
2342
|
+
return true;
|
|
2343
|
+
}
|
|
2344
|
+
const reason = getErrorReason(error);
|
|
2345
|
+
if (reason && RATE_LIMIT_REASONS.has(reason)) {
|
|
2346
|
+
return true;
|
|
2347
|
+
}
|
|
2348
|
+
const message = getErrorMessage(error).toLowerCase();
|
|
2349
|
+
return message.includes("rate limit") || message.includes("too many requests") || message.includes("resource exhausted") || message.includes("resource_exhausted");
|
|
2350
|
+
}
|
|
2101
2351
|
function retryDelayMs(attempt) {
|
|
2102
2352
|
const baseRetryDelayMs = 500;
|
|
2103
2353
|
const maxRetryDelayMs = 4e3;
|
|
@@ -2105,23 +2355,39 @@ function retryDelayMs(attempt) {
|
|
|
2105
2355
|
const jitter = Math.floor(Math.random() * 200);
|
|
2106
2356
|
return base + jitter;
|
|
2107
2357
|
}
|
|
2108
|
-
var
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2358
|
+
var DEFAULT_SCHEDULER_KEY2 = "__default__";
|
|
2359
|
+
var schedulerByModel2 = /* @__PURE__ */ new Map();
|
|
2360
|
+
function getSchedulerForModel2(modelId) {
|
|
2361
|
+
const normalizedModelId = modelId?.trim();
|
|
2362
|
+
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY2;
|
|
2363
|
+
const existing = schedulerByModel2.get(schedulerKey);
|
|
2364
|
+
if (existing) {
|
|
2365
|
+
return existing;
|
|
2366
|
+
}
|
|
2367
|
+
const created = createCallScheduler({
|
|
2368
|
+
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2369
|
+
provider: "google",
|
|
2370
|
+
modelId: normalizedModelId
|
|
2371
|
+
}),
|
|
2372
|
+
minIntervalBetweenStartMs: 200,
|
|
2373
|
+
startJitterMs: 200,
|
|
2374
|
+
isOverloadError,
|
|
2375
|
+
retry: {
|
|
2376
|
+
maxAttempts: 3,
|
|
2377
|
+
getDelayMs: (attempt, error) => {
|
|
2378
|
+
if (!shouldRetry(error)) {
|
|
2379
|
+
return null;
|
|
2380
|
+
}
|
|
2381
|
+
const hintedDelay = getRetryAfterMs(error);
|
|
2382
|
+
return hintedDelay ?? retryDelayMs(attempt);
|
|
2117
2383
|
}
|
|
2118
|
-
const hintedDelay = getRetryAfterMs(error);
|
|
2119
|
-
return hintedDelay ?? retryDelayMs(attempt);
|
|
2120
2384
|
}
|
|
2121
|
-
}
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2385
|
+
});
|
|
2386
|
+
schedulerByModel2.set(schedulerKey, created);
|
|
2387
|
+
return created;
|
|
2388
|
+
}
|
|
2389
|
+
async function runGeminiCall(fn, modelId, runOptions) {
|
|
2390
|
+
return getSchedulerForModel2(modelId).run(async () => fn(await getGeminiClient()), runOptions);
|
|
2125
2391
|
}
|
|
2126
2392
|
|
|
2127
2393
|
// src/openai/client.ts
|
|
@@ -2282,13 +2548,28 @@ function getOpenAiClient() {
|
|
|
2282
2548
|
|
|
2283
2549
|
// src/openai/calls.ts
|
|
2284
2550
|
var DEFAULT_OPENAI_REASONING_EFFORT = "medium";
|
|
2285
|
-
var
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2551
|
+
var DEFAULT_SCHEDULER_KEY3 = "__default__";
|
|
2552
|
+
var schedulerByModel3 = /* @__PURE__ */ new Map();
|
|
2553
|
+
function getSchedulerForModel3(modelId) {
|
|
2554
|
+
const normalizedModelId = modelId?.trim();
|
|
2555
|
+
const schedulerKey = normalizedModelId && normalizedModelId.length > 0 ? normalizedModelId : DEFAULT_SCHEDULER_KEY3;
|
|
2556
|
+
const existing = schedulerByModel3.get(schedulerKey);
|
|
2557
|
+
if (existing) {
|
|
2558
|
+
return existing;
|
|
2559
|
+
}
|
|
2560
|
+
const created = createCallScheduler({
|
|
2561
|
+
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2562
|
+
provider: "openai",
|
|
2563
|
+
modelId: normalizedModelId
|
|
2564
|
+
}),
|
|
2565
|
+
minIntervalBetweenStartMs: 200,
|
|
2566
|
+
startJitterMs: 200
|
|
2567
|
+
});
|
|
2568
|
+
schedulerByModel3.set(schedulerKey, created);
|
|
2569
|
+
return created;
|
|
2570
|
+
}
|
|
2571
|
+
async function runOpenAiCall(fn, modelId, runOptions) {
|
|
2572
|
+
return getSchedulerForModel3(modelId).run(async () => fn(getOpenAiClient()), runOptions);
|
|
2292
2573
|
}
|
|
2293
2574
|
|
|
2294
2575
|
// src/openai/models.ts
|
|
@@ -2742,9 +3023,9 @@ function isRetryableChatGptTransportError(error) {
|
|
|
2742
3023
|
return false;
|
|
2743
3024
|
}
|
|
2744
3025
|
const message = error.message.toLowerCase();
|
|
2745
|
-
return message === "terminated" || message.includes("socket hang up") || message.includes("fetch failed") || message.includes("network");
|
|
3026
|
+
return message === "terminated" || message.includes("socket hang up") || message.includes("fetch failed") || message.includes("network") || message.includes("responses websocket");
|
|
2746
3027
|
}
|
|
2747
|
-
async function collectChatGptCodexResponseWithRetry(options, maxAttempts =
|
|
3028
|
+
async function collectChatGptCodexResponseWithRetry(options, maxAttempts = 3) {
|
|
2748
3029
|
let attempt = 1;
|
|
2749
3030
|
while (true) {
|
|
2750
3031
|
try {
|
|
@@ -3649,77 +3930,153 @@ function buildToolErrorOutput(message, issues) {
|
|
|
3649
3930
|
}
|
|
3650
3931
|
return output;
|
|
3651
3932
|
}
|
|
3933
|
+
var SUBAGENT_WAIT_TOOL_NAME = "wait";
|
|
3934
|
+
function toIsoTimestamp(ms) {
|
|
3935
|
+
return new Date(ms).toISOString();
|
|
3936
|
+
}
|
|
3937
|
+
function toToolResultDuration(result) {
|
|
3938
|
+
return typeof result.durationMs === "number" && Number.isFinite(result.durationMs) ? Math.max(0, result.durationMs) : 0;
|
|
3939
|
+
}
|
|
3940
|
+
function schedulerMetricsOrDefault(metrics) {
|
|
3941
|
+
if (!metrics) {
|
|
3942
|
+
return {
|
|
3943
|
+
queueWaitMs: 0,
|
|
3944
|
+
schedulerDelayMs: 0,
|
|
3945
|
+
providerRetryDelayMs: 0,
|
|
3946
|
+
providerAttempts: 1
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
return {
|
|
3950
|
+
queueWaitMs: Math.max(0, metrics.queueWaitMs),
|
|
3951
|
+
schedulerDelayMs: Math.max(0, metrics.schedulerDelayMs),
|
|
3952
|
+
providerRetryDelayMs: Math.max(0, metrics.retryDelayMs),
|
|
3953
|
+
providerAttempts: Math.max(1, metrics.attempts),
|
|
3954
|
+
modelCallStartedAtMs: metrics.startedAtMs
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
function buildStepTiming(params) {
|
|
3958
|
+
const scheduler = schedulerMetricsOrDefault(params.schedulerMetrics);
|
|
3959
|
+
const modelCallStartedAtMs = scheduler.modelCallStartedAtMs ?? params.stepStartedAtMs;
|
|
3960
|
+
const firstModelEventAtMs = params.firstModelEventAtMs;
|
|
3961
|
+
const effectiveFirstEventAtMs = firstModelEventAtMs !== void 0 ? Math.max(modelCallStartedAtMs, firstModelEventAtMs) : params.modelCompletedAtMs;
|
|
3962
|
+
const connectionSetupMs = Math.max(0, effectiveFirstEventAtMs - modelCallStartedAtMs);
|
|
3963
|
+
const activeGenerationMs = Math.max(0, params.modelCompletedAtMs - effectiveFirstEventAtMs);
|
|
3964
|
+
return {
|
|
3965
|
+
startedAt: toIsoTimestamp(params.stepStartedAtMs),
|
|
3966
|
+
completedAt: toIsoTimestamp(params.stepCompletedAtMs),
|
|
3967
|
+
totalMs: Math.max(0, params.stepCompletedAtMs - params.stepStartedAtMs),
|
|
3968
|
+
queueWaitMs: scheduler.queueWaitMs,
|
|
3969
|
+
connectionSetupMs,
|
|
3970
|
+
activeGenerationMs,
|
|
3971
|
+
toolExecutionMs: Math.max(0, params.toolExecutionMs),
|
|
3972
|
+
waitToolMs: Math.max(0, params.waitToolMs),
|
|
3973
|
+
schedulerDelayMs: scheduler.schedulerDelayMs,
|
|
3974
|
+
providerRetryDelayMs: scheduler.providerRetryDelayMs,
|
|
3975
|
+
providerAttempts: scheduler.providerAttempts
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
function extractSpawnStartupMetrics(outputPayload) {
|
|
3979
|
+
if (!outputPayload || typeof outputPayload !== "object") {
|
|
3980
|
+
return void 0;
|
|
3981
|
+
}
|
|
3982
|
+
const outputRecord = outputPayload;
|
|
3983
|
+
const notification = typeof outputRecord.notification === "string" ? outputRecord.notification : "";
|
|
3984
|
+
if (notification !== "spawned") {
|
|
3985
|
+
return void 0;
|
|
3986
|
+
}
|
|
3987
|
+
const agent = outputRecord.agent;
|
|
3988
|
+
if (!agent || typeof agent !== "object") {
|
|
3989
|
+
return void 0;
|
|
3990
|
+
}
|
|
3991
|
+
const agentRecord = agent;
|
|
3992
|
+
const startupLatencyMs = agentRecord.spawn_startup_latency_ms;
|
|
3993
|
+
if (typeof startupLatencyMs !== "number" || !Number.isFinite(startupLatencyMs)) {
|
|
3994
|
+
return void 0;
|
|
3995
|
+
}
|
|
3996
|
+
return {
|
|
3997
|
+
spawnStartupLatencyMs: Math.max(0, startupLatencyMs)
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
3652
4000
|
async function executeToolCall(params) {
|
|
3653
4001
|
const { callKind, toolName, tool: tool2, rawInput, parseError } = params;
|
|
3654
|
-
|
|
3655
|
-
|
|
4002
|
+
const startedAtMs = Date.now();
|
|
4003
|
+
const finalize = (base, outputPayload, metrics) => {
|
|
4004
|
+
const completedAtMs = Date.now();
|
|
3656
4005
|
return {
|
|
3657
|
-
result: {
|
|
3658
|
-
|
|
4006
|
+
result: {
|
|
4007
|
+
...base,
|
|
4008
|
+
startedAt: toIsoTimestamp(startedAtMs),
|
|
4009
|
+
completedAt: toIsoTimestamp(completedAtMs),
|
|
4010
|
+
durationMs: Math.max(0, completedAtMs - startedAtMs),
|
|
4011
|
+
...metrics ? { metrics } : {}
|
|
4012
|
+
},
|
|
4013
|
+
outputPayload
|
|
3659
4014
|
};
|
|
4015
|
+
};
|
|
4016
|
+
if (!tool2) {
|
|
4017
|
+
const message = `Unknown tool: ${toolName}`;
|
|
4018
|
+
const outputPayload = buildToolErrorOutput(message);
|
|
4019
|
+
return finalize(
|
|
4020
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
4021
|
+
outputPayload
|
|
4022
|
+
);
|
|
3660
4023
|
}
|
|
3661
4024
|
if (callKind === "custom") {
|
|
3662
4025
|
if (!isCustomTool(tool2)) {
|
|
3663
4026
|
const message = `Tool ${toolName} was called as custom_tool_call but is declared as function.`;
|
|
3664
4027
|
const outputPayload = buildToolErrorOutput(message);
|
|
3665
|
-
return
|
|
3666
|
-
|
|
4028
|
+
return finalize(
|
|
4029
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
3667
4030
|
outputPayload
|
|
3668
|
-
|
|
4031
|
+
);
|
|
3669
4032
|
}
|
|
3670
4033
|
const input = typeof rawInput === "string" ? rawInput : String(rawInput ?? "");
|
|
3671
4034
|
try {
|
|
3672
4035
|
const output = await tool2.execute(input);
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
outputPayload: output
|
|
3676
|
-
};
|
|
4036
|
+
const metrics = toolName === "spawn_agent" ? extractSpawnStartupMetrics(output) : void 0;
|
|
4037
|
+
return finalize({ toolName, input, output }, output, metrics);
|
|
3677
4038
|
} catch (error) {
|
|
3678
4039
|
const message = error instanceof Error ? error.message : String(error);
|
|
3679
4040
|
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
3680
|
-
return {
|
|
3681
|
-
result: { toolName, input, output: outputPayload, error: message },
|
|
3682
|
-
outputPayload
|
|
3683
|
-
};
|
|
4041
|
+
return finalize({ toolName, input, output: outputPayload, error: message }, outputPayload);
|
|
3684
4042
|
}
|
|
3685
4043
|
}
|
|
3686
4044
|
if (isCustomTool(tool2)) {
|
|
3687
4045
|
const message = `Tool ${toolName} was called as function_call but is declared as custom.`;
|
|
3688
4046
|
const outputPayload = buildToolErrorOutput(message);
|
|
3689
|
-
return
|
|
3690
|
-
|
|
4047
|
+
return finalize(
|
|
4048
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
3691
4049
|
outputPayload
|
|
3692
|
-
|
|
4050
|
+
);
|
|
3693
4051
|
}
|
|
3694
4052
|
if (parseError) {
|
|
3695
4053
|
const message = `Invalid JSON for tool ${toolName}: ${parseError}`;
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
outputPayload:
|
|
3699
|
-
|
|
4054
|
+
const outputPayload = buildToolErrorOutput(message);
|
|
4055
|
+
return finalize(
|
|
4056
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
4057
|
+
outputPayload
|
|
4058
|
+
);
|
|
3700
4059
|
}
|
|
3701
4060
|
const parsed = tool2.inputSchema.safeParse(rawInput);
|
|
3702
4061
|
if (!parsed.success) {
|
|
3703
4062
|
const message = `Invalid tool arguments for ${toolName}: ${formatZodIssues(parsed.error.issues)}`;
|
|
3704
4063
|
const outputPayload = buildToolErrorOutput(message, parsed.error.issues);
|
|
3705
|
-
return
|
|
3706
|
-
|
|
4064
|
+
return finalize(
|
|
4065
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
3707
4066
|
outputPayload
|
|
3708
|
-
|
|
4067
|
+
);
|
|
3709
4068
|
}
|
|
3710
4069
|
try {
|
|
3711
4070
|
const output = await tool2.execute(parsed.data);
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
outputPayload: output
|
|
3715
|
-
};
|
|
4071
|
+
const metrics = toolName === "spawn_agent" ? extractSpawnStartupMetrics(output) : void 0;
|
|
4072
|
+
return finalize({ toolName, input: parsed.data, output }, output, metrics);
|
|
3716
4073
|
} catch (error) {
|
|
3717
4074
|
const message = error instanceof Error ? error.message : String(error);
|
|
3718
4075
|
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
3719
|
-
return
|
|
3720
|
-
|
|
4076
|
+
return finalize(
|
|
4077
|
+
{ toolName, input: parsed.data, output: outputPayload, error: message },
|
|
3721
4078
|
outputPayload
|
|
3722
|
-
|
|
4079
|
+
);
|
|
3723
4080
|
}
|
|
3724
4081
|
}
|
|
3725
4082
|
function buildToolLogId(turn, toolIndex) {
|
|
@@ -4050,7 +4407,7 @@ async function runTextCall(params) {
|
|
|
4050
4407
|
}
|
|
4051
4408
|
}
|
|
4052
4409
|
}
|
|
4053
|
-
});
|
|
4410
|
+
}, modelForProvider);
|
|
4054
4411
|
} else if (provider === "chatgpt") {
|
|
4055
4412
|
const chatGptInput = toChatGptInput(contents);
|
|
4056
4413
|
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
@@ -4145,7 +4502,7 @@ async function runTextCall(params) {
|
|
|
4145
4502
|
pushDelta("response", textOutput);
|
|
4146
4503
|
}
|
|
4147
4504
|
latestUsage = extractFireworksUsageTokens(response.usage);
|
|
4148
|
-
});
|
|
4505
|
+
}, modelForProvider);
|
|
4149
4506
|
} else {
|
|
4150
4507
|
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
4151
4508
|
const config = {
|
|
@@ -4213,7 +4570,7 @@ async function runTextCall(params) {
|
|
|
4213
4570
|
}
|
|
4214
4571
|
}
|
|
4215
4572
|
grounding = latestGrounding;
|
|
4216
|
-
});
|
|
4573
|
+
}, modelForProvider);
|
|
4217
4574
|
}
|
|
4218
4575
|
const mergedParts = mergeConsecutiveTextParts(responseParts);
|
|
4219
4576
|
const content = mergedParts.length > 0 ? { role: responseRole ?? "assistant", parts: mergedParts } : void 0;
|
|
@@ -4584,6 +4941,9 @@ async function runToolLoop(request) {
|
|
|
4584
4941
|
let input = toOpenAiInput(contents);
|
|
4585
4942
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
4586
4943
|
const turn = stepIndex + 1;
|
|
4944
|
+
const stepStartedAtMs = Date.now();
|
|
4945
|
+
let firstModelEventAtMs;
|
|
4946
|
+
let schedulerMetrics;
|
|
4587
4947
|
const abortController = new AbortController();
|
|
4588
4948
|
if (request.signal) {
|
|
4589
4949
|
if (request.signal.aborted) {
|
|
@@ -4602,45 +4962,59 @@ async function runToolLoop(request) {
|
|
|
4602
4962
|
const emitEvent = (ev) => {
|
|
4603
4963
|
onEvent?.(ev);
|
|
4604
4964
|
};
|
|
4605
|
-
const
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4965
|
+
const markFirstModelEvent = () => {
|
|
4966
|
+
if (firstModelEventAtMs === void 0) {
|
|
4967
|
+
firstModelEventAtMs = Date.now();
|
|
4968
|
+
}
|
|
4969
|
+
};
|
|
4970
|
+
const finalResponse = await runOpenAiCall(
|
|
4971
|
+
async (client) => {
|
|
4972
|
+
const stream = client.responses.stream(
|
|
4973
|
+
{
|
|
4974
|
+
model: providerInfo.model,
|
|
4975
|
+
input,
|
|
4976
|
+
...previousResponseId ? { previous_response_id: previousResponseId } : {},
|
|
4977
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
4978
|
+
...openAiTools.length > 0 ? { parallel_tool_calls: true } : {},
|
|
4979
|
+
reasoning,
|
|
4980
|
+
text: textConfig,
|
|
4981
|
+
include: ["reasoning.encrypted_content"]
|
|
4982
|
+
},
|
|
4983
|
+
{ signal: abortController.signal }
|
|
4984
|
+
);
|
|
4985
|
+
for await (const event of stream) {
|
|
4986
|
+
markFirstModelEvent();
|
|
4987
|
+
switch (event.type) {
|
|
4988
|
+
case "response.output_text.delta":
|
|
4989
|
+
emitEvent({
|
|
4990
|
+
type: "delta",
|
|
4991
|
+
channel: "response",
|
|
4992
|
+
text: typeof event.delta === "string" ? event.delta : ""
|
|
4993
|
+
});
|
|
4994
|
+
break;
|
|
4995
|
+
case "response.reasoning_summary_text.delta":
|
|
4996
|
+
emitEvent({
|
|
4997
|
+
type: "delta",
|
|
4998
|
+
channel: "thought",
|
|
4999
|
+
text: typeof event.delta === "string" ? event.delta : ""
|
|
5000
|
+
});
|
|
5001
|
+
break;
|
|
5002
|
+
case "response.refusal.delta":
|
|
5003
|
+
emitEvent({ type: "blocked" });
|
|
5004
|
+
break;
|
|
5005
|
+
default:
|
|
5006
|
+
break;
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
return await stream.finalResponse();
|
|
5010
|
+
},
|
|
5011
|
+
providerInfo.model,
|
|
5012
|
+
{
|
|
5013
|
+
onSettled: (metrics) => {
|
|
5014
|
+
schedulerMetrics = metrics;
|
|
4640
5015
|
}
|
|
4641
5016
|
}
|
|
4642
|
-
|
|
4643
|
-
});
|
|
5017
|
+
);
|
|
4644
5018
|
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
4645
5019
|
emitEvent({ type: "model", modelVersion });
|
|
4646
5020
|
if (finalResponse.error) {
|
|
@@ -4650,6 +5024,7 @@ async function runToolLoop(request) {
|
|
|
4650
5024
|
usageTokens = extractOpenAiUsageTokens(finalResponse.usage);
|
|
4651
5025
|
const responseText = extractOpenAiResponseParts(finalResponse).parts.filter((p) => p.type === "text" && p.thought !== true).map((p) => p.text).join("").trim();
|
|
4652
5026
|
const reasoningSummary = extractOpenAiReasoningSummary(finalResponse).trim();
|
|
5027
|
+
const modelCompletedAtMs = Date.now();
|
|
4653
5028
|
const stepCostUsd = estimateCallCostUsd({
|
|
4654
5029
|
modelId: modelVersion,
|
|
4655
5030
|
tokens: usageTokens,
|
|
@@ -4664,6 +5039,16 @@ async function runToolLoop(request) {
|
|
|
4664
5039
|
if (responseToolCalls.length === 0) {
|
|
4665
5040
|
finalText = responseText;
|
|
4666
5041
|
finalThoughts = reasoningSummary;
|
|
5042
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5043
|
+
const timing2 = buildStepTiming({
|
|
5044
|
+
stepStartedAtMs,
|
|
5045
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5046
|
+
modelCompletedAtMs,
|
|
5047
|
+
firstModelEventAtMs,
|
|
5048
|
+
schedulerMetrics,
|
|
5049
|
+
toolExecutionMs: 0,
|
|
5050
|
+
waitToolMs: 0
|
|
5051
|
+
});
|
|
4667
5052
|
steps.push({
|
|
4668
5053
|
step: steps.length + 1,
|
|
4669
5054
|
modelVersion,
|
|
@@ -4671,7 +5056,8 @@ async function runToolLoop(request) {
|
|
|
4671
5056
|
thoughts: reasoningSummary || void 0,
|
|
4672
5057
|
toolCalls: [],
|
|
4673
5058
|
usage: usageTokens,
|
|
4674
|
-
costUsd: stepCostUsd
|
|
5059
|
+
costUsd: stepCostUsd,
|
|
5060
|
+
timing: timing2
|
|
4675
5061
|
});
|
|
4676
5062
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
4677
5063
|
}
|
|
@@ -4716,8 +5102,15 @@ async function runToolLoop(request) {
|
|
|
4716
5102
|
})
|
|
4717
5103
|
);
|
|
4718
5104
|
const toolOutputs = [];
|
|
5105
|
+
let toolExecutionMs = 0;
|
|
5106
|
+
let waitToolMs = 0;
|
|
4719
5107
|
for (const { entry, result, outputPayload } of callResults) {
|
|
4720
5108
|
stepToolCalls.push({ ...result, callId: entry.call.call_id });
|
|
5109
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5110
|
+
toolExecutionMs += callDurationMs;
|
|
5111
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5112
|
+
waitToolMs += callDurationMs;
|
|
5113
|
+
}
|
|
4721
5114
|
if (entry.call.kind === "custom") {
|
|
4722
5115
|
toolOutputs.push({
|
|
4723
5116
|
type: "custom_tool_call_output",
|
|
@@ -4732,6 +5125,16 @@ async function runToolLoop(request) {
|
|
|
4732
5125
|
});
|
|
4733
5126
|
}
|
|
4734
5127
|
}
|
|
5128
|
+
const stepCompletedAtMs = Date.now();
|
|
5129
|
+
const timing = buildStepTiming({
|
|
5130
|
+
stepStartedAtMs,
|
|
5131
|
+
stepCompletedAtMs,
|
|
5132
|
+
modelCompletedAtMs,
|
|
5133
|
+
firstModelEventAtMs,
|
|
5134
|
+
schedulerMetrics,
|
|
5135
|
+
toolExecutionMs,
|
|
5136
|
+
waitToolMs
|
|
5137
|
+
});
|
|
4735
5138
|
steps.push({
|
|
4736
5139
|
step: steps.length + 1,
|
|
4737
5140
|
modelVersion,
|
|
@@ -4739,7 +5142,8 @@ async function runToolLoop(request) {
|
|
|
4739
5142
|
thoughts: reasoningSummary || void 0,
|
|
4740
5143
|
toolCalls: stepToolCalls,
|
|
4741
5144
|
usage: usageTokens,
|
|
4742
|
-
costUsd: stepCostUsd
|
|
5145
|
+
costUsd: stepCostUsd,
|
|
5146
|
+
timing
|
|
4743
5147
|
});
|
|
4744
5148
|
previousResponseId = finalResponse.id;
|
|
4745
5149
|
input = toolOutputs;
|
|
@@ -4760,6 +5164,13 @@ async function runToolLoop(request) {
|
|
|
4760
5164
|
let input = [...toolLoopInput.input];
|
|
4761
5165
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
4762
5166
|
const turn = stepIndex + 1;
|
|
5167
|
+
const stepStartedAtMs = Date.now();
|
|
5168
|
+
let firstModelEventAtMs;
|
|
5169
|
+
const markFirstModelEvent = () => {
|
|
5170
|
+
if (firstModelEventAtMs === void 0) {
|
|
5171
|
+
firstModelEventAtMs = Date.now();
|
|
5172
|
+
}
|
|
5173
|
+
};
|
|
4763
5174
|
const response = await collectChatGptCodexResponseWithRetry({
|
|
4764
5175
|
sessionId: conversationId,
|
|
4765
5176
|
request: {
|
|
@@ -4782,13 +5193,16 @@ async function runToolLoop(request) {
|
|
|
4782
5193
|
signal: request.signal,
|
|
4783
5194
|
onDelta: (delta) => {
|
|
4784
5195
|
if (delta.thoughtDelta) {
|
|
5196
|
+
markFirstModelEvent();
|
|
4785
5197
|
request.onEvent?.({ type: "delta", channel: "thought", text: delta.thoughtDelta });
|
|
4786
5198
|
}
|
|
4787
5199
|
if (delta.textDelta) {
|
|
5200
|
+
markFirstModelEvent();
|
|
4788
5201
|
request.onEvent?.({ type: "delta", channel: "response", text: delta.textDelta });
|
|
4789
5202
|
}
|
|
4790
5203
|
}
|
|
4791
5204
|
});
|
|
5205
|
+
const modelCompletedAtMs = Date.now();
|
|
4792
5206
|
const modelVersion = response.model ? `chatgpt-${response.model}` : request.model;
|
|
4793
5207
|
const usageTokens = extractChatGptUsageTokens(response.usage);
|
|
4794
5208
|
const stepCostUsd = estimateCallCostUsd({
|
|
@@ -4803,6 +5217,15 @@ async function runToolLoop(request) {
|
|
|
4803
5217
|
if (responseToolCalls.length === 0) {
|
|
4804
5218
|
finalText = responseText;
|
|
4805
5219
|
finalThoughts = reasoningSummaryText;
|
|
5220
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5221
|
+
const timing2 = buildStepTiming({
|
|
5222
|
+
stepStartedAtMs,
|
|
5223
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5224
|
+
modelCompletedAtMs,
|
|
5225
|
+
firstModelEventAtMs,
|
|
5226
|
+
toolExecutionMs: 0,
|
|
5227
|
+
waitToolMs: 0
|
|
5228
|
+
});
|
|
4806
5229
|
steps.push({
|
|
4807
5230
|
step: steps.length + 1,
|
|
4808
5231
|
modelVersion,
|
|
@@ -4810,7 +5233,8 @@ async function runToolLoop(request) {
|
|
|
4810
5233
|
thoughts: reasoningSummaryText || void 0,
|
|
4811
5234
|
toolCalls: [],
|
|
4812
5235
|
usage: usageTokens,
|
|
4813
|
-
costUsd: stepCostUsd
|
|
5236
|
+
costUsd: stepCostUsd,
|
|
5237
|
+
timing: timing2
|
|
4814
5238
|
});
|
|
4815
5239
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
4816
5240
|
}
|
|
@@ -4850,8 +5274,15 @@ async function runToolLoop(request) {
|
|
|
4850
5274
|
);
|
|
4851
5275
|
})
|
|
4852
5276
|
);
|
|
5277
|
+
let toolExecutionMs = 0;
|
|
5278
|
+
let waitToolMs = 0;
|
|
4853
5279
|
for (const { entry, result, outputPayload } of callResults) {
|
|
4854
5280
|
toolCalls.push({ ...result, callId: entry.ids.callId });
|
|
5281
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5282
|
+
toolExecutionMs += callDurationMs;
|
|
5283
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5284
|
+
waitToolMs += callDurationMs;
|
|
5285
|
+
}
|
|
4855
5286
|
if (entry.call.kind === "custom") {
|
|
4856
5287
|
toolOutputs.push({
|
|
4857
5288
|
type: "custom_tool_call",
|
|
@@ -4882,6 +5313,15 @@ async function runToolLoop(request) {
|
|
|
4882
5313
|
});
|
|
4883
5314
|
}
|
|
4884
5315
|
}
|
|
5316
|
+
const stepCompletedAtMs = Date.now();
|
|
5317
|
+
const timing = buildStepTiming({
|
|
5318
|
+
stepStartedAtMs,
|
|
5319
|
+
stepCompletedAtMs,
|
|
5320
|
+
modelCompletedAtMs,
|
|
5321
|
+
firstModelEventAtMs,
|
|
5322
|
+
toolExecutionMs,
|
|
5323
|
+
waitToolMs
|
|
5324
|
+
});
|
|
4885
5325
|
steps.push({
|
|
4886
5326
|
step: steps.length + 1,
|
|
4887
5327
|
modelVersion,
|
|
@@ -4889,7 +5329,8 @@ async function runToolLoop(request) {
|
|
|
4889
5329
|
thoughts: reasoningSummaryText || void 0,
|
|
4890
5330
|
toolCalls,
|
|
4891
5331
|
usage: usageTokens,
|
|
4892
|
-
costUsd: stepCostUsd
|
|
5332
|
+
costUsd: stepCostUsd,
|
|
5333
|
+
timing
|
|
4893
5334
|
});
|
|
4894
5335
|
input = input.concat(toolOutputs);
|
|
4895
5336
|
}
|
|
@@ -4905,18 +5346,29 @@ async function runToolLoop(request) {
|
|
|
4905
5346
|
const messages = toFireworksMessages(contents);
|
|
4906
5347
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
4907
5348
|
const turn = stepIndex + 1;
|
|
4908
|
-
const
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
5349
|
+
const stepStartedAtMs = Date.now();
|
|
5350
|
+
let schedulerMetrics;
|
|
5351
|
+
const response = await runFireworksCall(
|
|
5352
|
+
async (client) => {
|
|
5353
|
+
return await client.chat.completions.create(
|
|
5354
|
+
{
|
|
5355
|
+
model: providerInfo.model,
|
|
5356
|
+
messages,
|
|
5357
|
+
tools: fireworksTools,
|
|
5358
|
+
tool_choice: "auto",
|
|
5359
|
+
parallel_tool_calls: true
|
|
5360
|
+
},
|
|
5361
|
+
{ signal: request.signal }
|
|
5362
|
+
);
|
|
5363
|
+
},
|
|
5364
|
+
providerInfo.model,
|
|
5365
|
+
{
|
|
5366
|
+
onSettled: (metrics) => {
|
|
5367
|
+
schedulerMetrics = metrics;
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
);
|
|
5371
|
+
const modelCompletedAtMs = Date.now();
|
|
4920
5372
|
const modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
4921
5373
|
request.onEvent?.({ type: "model", modelVersion });
|
|
4922
5374
|
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
@@ -4947,6 +5399,15 @@ async function runToolLoop(request) {
|
|
|
4947
5399
|
if (responseToolCalls.length === 0) {
|
|
4948
5400
|
finalText = responseText;
|
|
4949
5401
|
finalThoughts = "";
|
|
5402
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5403
|
+
const timing2 = buildStepTiming({
|
|
5404
|
+
stepStartedAtMs,
|
|
5405
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5406
|
+
modelCompletedAtMs,
|
|
5407
|
+
schedulerMetrics,
|
|
5408
|
+
toolExecutionMs: 0,
|
|
5409
|
+
waitToolMs: 0
|
|
5410
|
+
});
|
|
4950
5411
|
steps.push({
|
|
4951
5412
|
step: steps.length + 1,
|
|
4952
5413
|
modelVersion,
|
|
@@ -4954,7 +5415,8 @@ async function runToolLoop(request) {
|
|
|
4954
5415
|
thoughts: void 0,
|
|
4955
5416
|
toolCalls: [],
|
|
4956
5417
|
usage: usageTokens,
|
|
4957
|
-
costUsd: stepCostUsd
|
|
5418
|
+
costUsd: stepCostUsd,
|
|
5419
|
+
timing: timing2
|
|
4958
5420
|
});
|
|
4959
5421
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
4960
5422
|
}
|
|
@@ -4989,8 +5451,15 @@ async function runToolLoop(request) {
|
|
|
4989
5451
|
);
|
|
4990
5452
|
const assistantToolCalls = [];
|
|
4991
5453
|
const toolMessages = [];
|
|
5454
|
+
let toolExecutionMs = 0;
|
|
5455
|
+
let waitToolMs = 0;
|
|
4992
5456
|
for (const { entry, result, outputPayload } of callResults) {
|
|
4993
5457
|
stepToolCalls.push({ ...result, callId: entry.call.id });
|
|
5458
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5459
|
+
toolExecutionMs += callDurationMs;
|
|
5460
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5461
|
+
waitToolMs += callDurationMs;
|
|
5462
|
+
}
|
|
4994
5463
|
assistantToolCalls.push({
|
|
4995
5464
|
id: entry.call.id,
|
|
4996
5465
|
type: "function",
|
|
@@ -5005,6 +5474,15 @@ async function runToolLoop(request) {
|
|
|
5005
5474
|
content: mergeToolOutput(outputPayload)
|
|
5006
5475
|
});
|
|
5007
5476
|
}
|
|
5477
|
+
const stepCompletedAtMs = Date.now();
|
|
5478
|
+
const timing = buildStepTiming({
|
|
5479
|
+
stepStartedAtMs,
|
|
5480
|
+
stepCompletedAtMs,
|
|
5481
|
+
modelCompletedAtMs,
|
|
5482
|
+
schedulerMetrics,
|
|
5483
|
+
toolExecutionMs,
|
|
5484
|
+
waitToolMs
|
|
5485
|
+
});
|
|
5008
5486
|
steps.push({
|
|
5009
5487
|
step: steps.length + 1,
|
|
5010
5488
|
modelVersion,
|
|
@@ -5012,7 +5490,8 @@ async function runToolLoop(request) {
|
|
|
5012
5490
|
thoughts: void 0,
|
|
5013
5491
|
toolCalls: stepToolCalls,
|
|
5014
5492
|
usage: usageTokens,
|
|
5015
|
-
costUsd: stepCostUsd
|
|
5493
|
+
costUsd: stepCostUsd,
|
|
5494
|
+
timing
|
|
5016
5495
|
});
|
|
5017
5496
|
messages.push({
|
|
5018
5497
|
role: "assistant",
|
|
@@ -5028,6 +5507,14 @@ async function runToolLoop(request) {
|
|
|
5028
5507
|
const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
|
|
5029
5508
|
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
5030
5509
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5510
|
+
const stepStartedAtMs = Date.now();
|
|
5511
|
+
let firstModelEventAtMs;
|
|
5512
|
+
let schedulerMetrics;
|
|
5513
|
+
const markFirstModelEvent = () => {
|
|
5514
|
+
if (firstModelEventAtMs === void 0) {
|
|
5515
|
+
firstModelEventAtMs = Date.now();
|
|
5516
|
+
}
|
|
5517
|
+
};
|
|
5031
5518
|
const config = {
|
|
5032
5519
|
maxOutputTokens: 32e3,
|
|
5033
5520
|
tools: geminiTools,
|
|
@@ -5039,81 +5526,91 @@ async function runToolLoop(request) {
|
|
|
5039
5526
|
thinkingConfig: resolveGeminiThinkingConfig(request.model)
|
|
5040
5527
|
};
|
|
5041
5528
|
const onEvent = request.onEvent;
|
|
5042
|
-
const response = await runGeminiCall(
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
const
|
|
5076
|
-
|
|
5077
|
-
const
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5529
|
+
const response = await runGeminiCall(
|
|
5530
|
+
async (client) => {
|
|
5531
|
+
const stream = await client.models.generateContentStream({
|
|
5532
|
+
model: request.model,
|
|
5533
|
+
contents: geminiContents,
|
|
5534
|
+
config
|
|
5535
|
+
});
|
|
5536
|
+
let responseText = "";
|
|
5537
|
+
let thoughtsText = "";
|
|
5538
|
+
const modelParts = [];
|
|
5539
|
+
const functionCalls = [];
|
|
5540
|
+
const seenFunctionCallIds = /* @__PURE__ */ new Set();
|
|
5541
|
+
const seenFunctionCallKeys = /* @__PURE__ */ new Set();
|
|
5542
|
+
let latestUsageMetadata;
|
|
5543
|
+
let resolvedModelVersion;
|
|
5544
|
+
for await (const chunk of stream) {
|
|
5545
|
+
markFirstModelEvent();
|
|
5546
|
+
if (chunk.modelVersion) {
|
|
5547
|
+
resolvedModelVersion = chunk.modelVersion;
|
|
5548
|
+
onEvent?.({ type: "model", modelVersion: chunk.modelVersion });
|
|
5549
|
+
}
|
|
5550
|
+
if (chunk.usageMetadata) {
|
|
5551
|
+
latestUsageMetadata = chunk.usageMetadata;
|
|
5552
|
+
}
|
|
5553
|
+
const candidates = chunk.candidates;
|
|
5554
|
+
if (!candidates || candidates.length === 0) {
|
|
5555
|
+
continue;
|
|
5556
|
+
}
|
|
5557
|
+
const primary = candidates[0];
|
|
5558
|
+
const parts = primary?.content?.parts;
|
|
5559
|
+
if (!parts || parts.length === 0) {
|
|
5560
|
+
continue;
|
|
5561
|
+
}
|
|
5562
|
+
for (const part of parts) {
|
|
5563
|
+
modelParts.push(part);
|
|
5564
|
+
const call = part.functionCall;
|
|
5565
|
+
if (call) {
|
|
5566
|
+
const id = typeof call.id === "string" ? call.id : "";
|
|
5567
|
+
const shouldAdd = (() => {
|
|
5568
|
+
if (id.length > 0) {
|
|
5569
|
+
if (seenFunctionCallIds.has(id)) {
|
|
5570
|
+
return false;
|
|
5571
|
+
}
|
|
5572
|
+
seenFunctionCallIds.add(id);
|
|
5573
|
+
return true;
|
|
5574
|
+
}
|
|
5575
|
+
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
5576
|
+
if (seenFunctionCallKeys.has(key)) {
|
|
5081
5577
|
return false;
|
|
5082
5578
|
}
|
|
5083
|
-
|
|
5579
|
+
seenFunctionCallKeys.add(key);
|
|
5084
5580
|
return true;
|
|
5581
|
+
})();
|
|
5582
|
+
if (shouldAdd) {
|
|
5583
|
+
functionCalls.push(call);
|
|
5085
5584
|
}
|
|
5086
|
-
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
5087
|
-
if (seenFunctionCallKeys.has(key)) {
|
|
5088
|
-
return false;
|
|
5089
|
-
}
|
|
5090
|
-
seenFunctionCallKeys.add(key);
|
|
5091
|
-
return true;
|
|
5092
|
-
})();
|
|
5093
|
-
if (shouldAdd) {
|
|
5094
|
-
functionCalls.push(call);
|
|
5095
5585
|
}
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5586
|
+
if (typeof part.text === "string" && part.text.length > 0) {
|
|
5587
|
+
if (part.thought) {
|
|
5588
|
+
thoughtsText += part.text;
|
|
5589
|
+
onEvent?.({ type: "delta", channel: "thought", text: part.text });
|
|
5590
|
+
} else {
|
|
5591
|
+
responseText += part.text;
|
|
5592
|
+
onEvent?.({ type: "delta", channel: "response", text: part.text });
|
|
5593
|
+
}
|
|
5104
5594
|
}
|
|
5105
5595
|
}
|
|
5106
5596
|
}
|
|
5597
|
+
return {
|
|
5598
|
+
responseText,
|
|
5599
|
+
thoughtsText,
|
|
5600
|
+
functionCalls,
|
|
5601
|
+
modelParts,
|
|
5602
|
+
usageMetadata: latestUsageMetadata,
|
|
5603
|
+
modelVersion: resolvedModelVersion ?? request.model
|
|
5604
|
+
};
|
|
5605
|
+
},
|
|
5606
|
+
request.model,
|
|
5607
|
+
{
|
|
5608
|
+
onSettled: (metrics) => {
|
|
5609
|
+
schedulerMetrics = metrics;
|
|
5610
|
+
}
|
|
5107
5611
|
}
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
thoughtsText,
|
|
5111
|
-
functionCalls,
|
|
5112
|
-
modelParts,
|
|
5113
|
-
usageMetadata: latestUsageMetadata,
|
|
5114
|
-
modelVersion: resolvedModelVersion ?? request.model
|
|
5115
|
-
};
|
|
5116
|
-
});
|
|
5612
|
+
);
|
|
5613
|
+
const modelCompletedAtMs = Date.now();
|
|
5117
5614
|
const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
5118
5615
|
const modelVersion = response.modelVersion ?? request.model;
|
|
5119
5616
|
const stepCostUsd = estimateCallCostUsd({
|
|
@@ -5125,6 +5622,16 @@ async function runToolLoop(request) {
|
|
|
5125
5622
|
if (response.functionCalls.length === 0) {
|
|
5126
5623
|
finalText = response.responseText.trim();
|
|
5127
5624
|
finalThoughts = response.thoughtsText.trim();
|
|
5625
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5626
|
+
const timing2 = buildStepTiming({
|
|
5627
|
+
stepStartedAtMs,
|
|
5628
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5629
|
+
modelCompletedAtMs,
|
|
5630
|
+
firstModelEventAtMs,
|
|
5631
|
+
schedulerMetrics,
|
|
5632
|
+
toolExecutionMs: 0,
|
|
5633
|
+
waitToolMs: 0
|
|
5634
|
+
});
|
|
5128
5635
|
steps.push({
|
|
5129
5636
|
step: steps.length + 1,
|
|
5130
5637
|
modelVersion,
|
|
@@ -5132,7 +5639,8 @@ async function runToolLoop(request) {
|
|
|
5132
5639
|
thoughts: finalThoughts || void 0,
|
|
5133
5640
|
toolCalls: [],
|
|
5134
5641
|
usage: usageTokens,
|
|
5135
|
-
costUsd: stepCostUsd
|
|
5642
|
+
costUsd: stepCostUsd,
|
|
5643
|
+
timing: timing2
|
|
5136
5644
|
});
|
|
5137
5645
|
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5138
5646
|
}
|
|
@@ -5182,8 +5690,15 @@ async function runToolLoop(request) {
|
|
|
5182
5690
|
);
|
|
5183
5691
|
})
|
|
5184
5692
|
);
|
|
5693
|
+
let toolExecutionMs = 0;
|
|
5694
|
+
let waitToolMs = 0;
|
|
5185
5695
|
for (const { entry, result, outputPayload } of callResults) {
|
|
5186
5696
|
toolCalls.push({ ...result, callId: entry.call.id });
|
|
5697
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5698
|
+
toolExecutionMs += callDurationMs;
|
|
5699
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5700
|
+
waitToolMs += callDurationMs;
|
|
5701
|
+
}
|
|
5187
5702
|
const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
|
|
5188
5703
|
responseParts.push({
|
|
5189
5704
|
functionResponse: {
|
|
@@ -5193,6 +5708,16 @@ async function runToolLoop(request) {
|
|
|
5193
5708
|
}
|
|
5194
5709
|
});
|
|
5195
5710
|
}
|
|
5711
|
+
const stepCompletedAtMs = Date.now();
|
|
5712
|
+
const timing = buildStepTiming({
|
|
5713
|
+
stepStartedAtMs,
|
|
5714
|
+
stepCompletedAtMs,
|
|
5715
|
+
modelCompletedAtMs,
|
|
5716
|
+
firstModelEventAtMs,
|
|
5717
|
+
schedulerMetrics,
|
|
5718
|
+
toolExecutionMs,
|
|
5719
|
+
waitToolMs
|
|
5720
|
+
});
|
|
5196
5721
|
steps.push({
|
|
5197
5722
|
step: steps.length + 1,
|
|
5198
5723
|
modelVersion,
|
|
@@ -5200,7 +5725,8 @@ async function runToolLoop(request) {
|
|
|
5200
5725
|
thoughts: response.thoughtsText.trim() || void 0,
|
|
5201
5726
|
toolCalls,
|
|
5202
5727
|
usage: usageTokens,
|
|
5203
|
-
costUsd: stepCostUsd
|
|
5728
|
+
costUsd: stepCostUsd,
|
|
5729
|
+
timing
|
|
5204
5730
|
});
|
|
5205
5731
|
geminiContents.push({ role: "user", parts: responseParts });
|
|
5206
5732
|
}
|
|
@@ -5451,13 +5977,648 @@ function appendMarkdownSourcesSection(value, sources) {
|
|
|
5451
5977
|
${lines}`;
|
|
5452
5978
|
}
|
|
5453
5979
|
|
|
5980
|
+
// src/agent.ts
|
|
5981
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
5982
|
+
|
|
5983
|
+
// src/agent/subagents.ts
|
|
5984
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
5985
|
+
import { z as z4 } from "zod";
|
|
5986
|
+
var DEFAULT_SUBAGENT_MAX_AGENTS = 4;
|
|
5987
|
+
var DEFAULT_SUBAGENT_MAX_DEPTH = 2;
|
|
5988
|
+
var DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS = 1500;
|
|
5989
|
+
var DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS = 9e4;
|
|
5990
|
+
var MAX_SUBAGENT_MAX_AGENTS = 64;
|
|
5991
|
+
var MAX_SUBAGENT_MAX_DEPTH = 12;
|
|
5992
|
+
var MAX_SUBAGENT_MAX_STEPS = 64;
|
|
5993
|
+
var MAX_SUBAGENT_WAIT_TIMEOUT_MS = 6e5;
|
|
5994
|
+
var SUBAGENT_CONTROL_TOOL_NAMES = ["send_input", "resume_agent", "wait", "close_agent"];
|
|
5995
|
+
var subagentInputItemSchema = z4.object({
|
|
5996
|
+
text: z4.string().optional(),
|
|
5997
|
+
image_url: z4.string().optional(),
|
|
5998
|
+
name: z4.string().optional(),
|
|
5999
|
+
path: z4.string().optional(),
|
|
6000
|
+
type: z4.string().optional()
|
|
6001
|
+
}).passthrough();
|
|
6002
|
+
var spawnAgentInputSchema = z4.object({
|
|
6003
|
+
prompt: z4.string().optional().describe("Initial prompt for the subagent."),
|
|
6004
|
+
message: z4.string().optional().describe("Codex-style alias for prompt."),
|
|
6005
|
+
items: z4.array(subagentInputItemSchema).optional().describe("Optional Codex-style input items."),
|
|
6006
|
+
agent_type: z4.string().optional().describe("Codex-style agent type hint."),
|
|
6007
|
+
instructions: z4.string().optional().describe("Optional extra instructions for this subagent instance."),
|
|
6008
|
+
model: z4.string().optional().describe("Optional model override. Must be one of this package's supported text model ids."),
|
|
6009
|
+
max_steps: z4.number().int().min(1).max(MAX_SUBAGENT_MAX_STEPS).optional().describe("Optional max step budget for each subagent run.")
|
|
6010
|
+
}).refine((value) => Boolean(resolvePromptValue(value.prompt, value.message, value.items)), {
|
|
6011
|
+
message: "Either prompt, message, or items must contain non-empty input."
|
|
6012
|
+
});
|
|
6013
|
+
var sendInputSchema = z4.object({
|
|
6014
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
6015
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id."),
|
|
6016
|
+
input: z4.string().optional().describe("New user input queued for the subagent."),
|
|
6017
|
+
message: z4.string().optional().describe("Codex-style alias for input."),
|
|
6018
|
+
items: z4.array(subagentInputItemSchema).optional().describe("Optional Codex-style input items."),
|
|
6019
|
+
interrupt: z4.boolean().optional().describe("If true and currently running, aborts active run before queuing input.")
|
|
6020
|
+
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
6021
|
+
message: "agent_id (or id) is required."
|
|
6022
|
+
}).refine((value) => Boolean(resolvePromptValue(value.input, value.message, value.items)), {
|
|
6023
|
+
message: "input (or message/items) is required."
|
|
6024
|
+
});
|
|
6025
|
+
var resumeAgentSchema = z4.object({
|
|
6026
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
6027
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id.")
|
|
6028
|
+
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
6029
|
+
message: "agent_id (or id) is required."
|
|
6030
|
+
});
|
|
6031
|
+
var waitSchema = z4.object({
|
|
6032
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
6033
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id."),
|
|
6034
|
+
ids: z4.array(z4.string().min(1)).optional().describe("Codex-style list of agent ids."),
|
|
6035
|
+
timeout_ms: z4.number().int().min(1).optional().describe("Optional wait timeout in milliseconds.")
|
|
6036
|
+
}).refine(
|
|
6037
|
+
(value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)) || Array.isArray(value.ids) && value.ids.length > 0,
|
|
6038
|
+
{
|
|
6039
|
+
message: "agent_id/id or ids is required."
|
|
6040
|
+
}
|
|
6041
|
+
);
|
|
6042
|
+
var closeSchema = z4.object({
|
|
6043
|
+
agent_id: z4.string().optional().describe("Target subagent id."),
|
|
6044
|
+
id: z4.string().optional().describe("Codex-style alias for agent_id.")
|
|
6045
|
+
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
6046
|
+
message: "agent_id (or id) is required."
|
|
6047
|
+
});
|
|
6048
|
+
function resolveSubagentToolConfig(selection, currentDepth) {
|
|
6049
|
+
const defaults = {
|
|
6050
|
+
maxAgents: DEFAULT_SUBAGENT_MAX_AGENTS,
|
|
6051
|
+
maxDepth: DEFAULT_SUBAGENT_MAX_DEPTH,
|
|
6052
|
+
defaultWaitTimeoutMs: DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS,
|
|
6053
|
+
maxWaitTimeoutMs: DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS,
|
|
6054
|
+
promptPattern: "codex",
|
|
6055
|
+
inheritTools: true,
|
|
6056
|
+
inheritFilesystemTool: true
|
|
6057
|
+
};
|
|
6058
|
+
if (selection === void 0 || selection === false) {
|
|
6059
|
+
return {
|
|
6060
|
+
enabled: false,
|
|
6061
|
+
...defaults
|
|
6062
|
+
};
|
|
6063
|
+
}
|
|
6064
|
+
const config = selection === true ? {} : selection;
|
|
6065
|
+
const maxAgents = normalizeInteger(
|
|
6066
|
+
config.maxAgents,
|
|
6067
|
+
defaults.maxAgents,
|
|
6068
|
+
1,
|
|
6069
|
+
MAX_SUBAGENT_MAX_AGENTS
|
|
6070
|
+
);
|
|
6071
|
+
const maxDepth = normalizeInteger(config.maxDepth, defaults.maxDepth, 1, MAX_SUBAGENT_MAX_DEPTH);
|
|
6072
|
+
const defaultWaitTimeoutMs = normalizeInteger(
|
|
6073
|
+
config.defaultWaitTimeoutMs,
|
|
6074
|
+
defaults.defaultWaitTimeoutMs,
|
|
6075
|
+
1,
|
|
6076
|
+
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
6077
|
+
);
|
|
6078
|
+
const maxWaitTimeoutMs = normalizeInteger(
|
|
6079
|
+
config.maxWaitTimeoutMs,
|
|
6080
|
+
defaults.maxWaitTimeoutMs,
|
|
6081
|
+
defaultWaitTimeoutMs,
|
|
6082
|
+
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
6083
|
+
);
|
|
6084
|
+
const promptPattern = config.promptPattern ?? defaults.promptPattern;
|
|
6085
|
+
const instructions = trimToUndefined(config.instructions);
|
|
6086
|
+
const maxSteps = normalizeOptionalInteger(config.maxSteps, 1, MAX_SUBAGENT_MAX_STEPS);
|
|
6087
|
+
const enabled = config.enabled !== false && currentDepth < maxDepth;
|
|
6088
|
+
return {
|
|
6089
|
+
enabled,
|
|
6090
|
+
maxAgents,
|
|
6091
|
+
maxDepth,
|
|
6092
|
+
defaultWaitTimeoutMs,
|
|
6093
|
+
maxWaitTimeoutMs,
|
|
6094
|
+
promptPattern,
|
|
6095
|
+
...instructions ? { instructions } : {},
|
|
6096
|
+
...config.model ? { model: config.model } : {},
|
|
6097
|
+
...maxSteps ? { maxSteps } : {},
|
|
6098
|
+
inheritTools: config.inheritTools !== false,
|
|
6099
|
+
inheritFilesystemTool: config.inheritFilesystemTool !== false
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
6102
|
+
function buildCodexSubagentOrchestratorInstructions(params) {
|
|
6103
|
+
return [
|
|
6104
|
+
"Subagent orchestration tools are available: spawn_agent, send_input, resume_agent, wait, close_agent.",
|
|
6105
|
+
"Use this control pattern:",
|
|
6106
|
+
"1. spawn_agent with a focused prompt.",
|
|
6107
|
+
"2. wait on that agent_id until it is no longer running.",
|
|
6108
|
+
"3. For follow-up turns, send_input then resume_agent.",
|
|
6109
|
+
"4. close_agent when delegation is complete.",
|
|
6110
|
+
`Limits: max active subagents ${params.maxAgents}, max depth ${params.maxDepth}, current depth ${params.currentDepth}.`
|
|
6111
|
+
].join("\n");
|
|
6112
|
+
}
|
|
6113
|
+
function buildCodexSubagentWorkerInstructions(params) {
|
|
6114
|
+
return [
|
|
6115
|
+
`You are a delegated subagent at depth ${params.depth}/${params.maxDepth}.`,
|
|
6116
|
+
"Focus on the delegated task, use available tools when needed, and return concise actionable output.",
|
|
6117
|
+
"If blocked, report the blocker explicitly."
|
|
6118
|
+
].join("\n");
|
|
6119
|
+
}
|
|
6120
|
+
function createSubagentToolController(options) {
|
|
6121
|
+
if (!options.config.enabled) {
|
|
6122
|
+
return {
|
|
6123
|
+
tools: {},
|
|
6124
|
+
closeAll: async () => {
|
|
6125
|
+
}
|
|
6126
|
+
};
|
|
6127
|
+
}
|
|
6128
|
+
const agents = /* @__PURE__ */ new Map();
|
|
6129
|
+
const tools = {
|
|
6130
|
+
spawn_agent: tool({
|
|
6131
|
+
description: "Spawns a subagent asynchronously. Returns immediately with agent status and id.",
|
|
6132
|
+
inputSchema: spawnAgentInputSchema,
|
|
6133
|
+
execute: async (input) => {
|
|
6134
|
+
if (countActiveAgents(agents) >= options.config.maxAgents) {
|
|
6135
|
+
throw new Error(
|
|
6136
|
+
`Subagent limit reached (${options.config.maxAgents}). Close existing agents before spawning new ones.`
|
|
6137
|
+
);
|
|
6138
|
+
}
|
|
6139
|
+
const childDepth = options.parentDepth + 1;
|
|
6140
|
+
if (childDepth > options.config.maxDepth) {
|
|
6141
|
+
throw new Error(
|
|
6142
|
+
`Subagent depth limit reached (${options.config.maxDepth}). Cannot spawn at depth ${childDepth}.`
|
|
6143
|
+
);
|
|
6144
|
+
}
|
|
6145
|
+
let model = options.config.model ?? options.parentModel;
|
|
6146
|
+
if (input.model) {
|
|
6147
|
+
if (!isLlmTextModelId(input.model)) {
|
|
6148
|
+
throw new Error(`Unsupported subagent model id: ${input.model}`);
|
|
6149
|
+
}
|
|
6150
|
+
model = input.model;
|
|
6151
|
+
}
|
|
6152
|
+
const id = `agent_${randomBytes2(6).toString("hex")}`;
|
|
6153
|
+
const now = Date.now();
|
|
6154
|
+
const initialPrompt = resolvePromptValue(input.prompt, input.message, input.items);
|
|
6155
|
+
if (!initialPrompt) {
|
|
6156
|
+
throw new Error("spawn_agent requires prompt/message/items with non-empty text.");
|
|
6157
|
+
}
|
|
6158
|
+
const agent = {
|
|
6159
|
+
id,
|
|
6160
|
+
depth: childDepth,
|
|
6161
|
+
model,
|
|
6162
|
+
status: "idle",
|
|
6163
|
+
createdAtMs: now,
|
|
6164
|
+
updatedAtMs: now,
|
|
6165
|
+
pendingInputs: [initialPrompt],
|
|
6166
|
+
history: [],
|
|
6167
|
+
...options.buildChildInstructions ? {
|
|
6168
|
+
instructions: trimToUndefined(
|
|
6169
|
+
options.buildChildInstructions(input.instructions, childDepth)
|
|
6170
|
+
)
|
|
6171
|
+
} : input.instructions ? { instructions: trimToUndefined(input.instructions) } : {},
|
|
6172
|
+
...input.max_steps ? { maxSteps: input.max_steps } : options.config.maxSteps ? { maxSteps: options.config.maxSteps } : {},
|
|
6173
|
+
turns: 0,
|
|
6174
|
+
notification: "spawned",
|
|
6175
|
+
notificationMessage: `Spawned subagent ${id}.`,
|
|
6176
|
+
version: 1,
|
|
6177
|
+
waiters: /* @__PURE__ */ new Set()
|
|
6178
|
+
};
|
|
6179
|
+
agents.set(id, agent);
|
|
6180
|
+
startRun(agent, options);
|
|
6181
|
+
return buildToolResponse(agent, {
|
|
6182
|
+
notification: "spawned",
|
|
6183
|
+
message: `Spawned subagent ${id}.`
|
|
6184
|
+
});
|
|
6185
|
+
}
|
|
6186
|
+
}),
|
|
6187
|
+
send_input: tool({
|
|
6188
|
+
description: "Queues new input for an existing subagent.",
|
|
6189
|
+
inputSchema: sendInputSchema,
|
|
6190
|
+
execute: async (input) => {
|
|
6191
|
+
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
6192
|
+
if (!agentId) {
|
|
6193
|
+
throw new Error("send_input requires agent_id or id.");
|
|
6194
|
+
}
|
|
6195
|
+
const agent = requireAgent(agents, agentId);
|
|
6196
|
+
const nextInput = resolvePromptValue(input.input, input.message, input.items);
|
|
6197
|
+
if (!nextInput) {
|
|
6198
|
+
throw new Error("send_input requires input/message/items with non-empty text.");
|
|
6199
|
+
}
|
|
6200
|
+
if (agent.status === "closed") {
|
|
6201
|
+
throw new Error(`Subagent ${agent.id} is closed.`);
|
|
6202
|
+
}
|
|
6203
|
+
if (input.interrupt && agent.abortController) {
|
|
6204
|
+
agent.abortController.abort("send_input_interrupt");
|
|
6205
|
+
agent.pendingInputs.unshift(nextInput);
|
|
6206
|
+
setNotification(agent, "input_queued", `Interrupted ${agent.id} and queued new input.`);
|
|
6207
|
+
return buildToolResponse(agent);
|
|
6208
|
+
}
|
|
6209
|
+
agent.pendingInputs.push(nextInput);
|
|
6210
|
+
setNotification(agent, "input_queued", `Queued input for ${agent.id}.`);
|
|
6211
|
+
return buildToolResponse(agent);
|
|
6212
|
+
}
|
|
6213
|
+
}),
|
|
6214
|
+
resume_agent: tool({
|
|
6215
|
+
description: "Resumes a subagent run when queued input is available.",
|
|
6216
|
+
inputSchema: resumeAgentSchema,
|
|
6217
|
+
execute: async (input) => {
|
|
6218
|
+
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
6219
|
+
if (!agentId) {
|
|
6220
|
+
throw new Error("resume_agent requires agent_id or id.");
|
|
6221
|
+
}
|
|
6222
|
+
const agent = requireAgent(agents, agentId);
|
|
6223
|
+
if (agent.status === "closed") {
|
|
6224
|
+
setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
|
|
6225
|
+
return buildToolResponse(agent, {
|
|
6226
|
+
notification: "already_closed",
|
|
6227
|
+
message: `Subagent ${agent.id} is already closed.`
|
|
6228
|
+
});
|
|
6229
|
+
}
|
|
6230
|
+
const outcome = startRun(agent, options);
|
|
6231
|
+
if (outcome === "started") {
|
|
6232
|
+
return buildToolResponse(agent, {
|
|
6233
|
+
notification: "run_started",
|
|
6234
|
+
message: `Started subagent ${agent.id}.`
|
|
6235
|
+
});
|
|
6236
|
+
}
|
|
6237
|
+
if (outcome === "already_running") {
|
|
6238
|
+
setNotification(agent, "already_running", `Subagent ${agent.id} is already running.`);
|
|
6239
|
+
return buildToolResponse(agent);
|
|
6240
|
+
}
|
|
6241
|
+
setNotification(agent, "no_pending_input", `Subagent ${agent.id} has no queued input.`);
|
|
6242
|
+
return buildToolResponse(agent);
|
|
6243
|
+
}
|
|
6244
|
+
}),
|
|
6245
|
+
wait: tool({
|
|
6246
|
+
description: "Waits for a running subagent to change state or until timeout. Returns current status.",
|
|
6247
|
+
inputSchema: waitSchema,
|
|
6248
|
+
execute: async (input) => {
|
|
6249
|
+
const usesIdsArray = Array.isArray(input.ids) && input.ids.length > 0;
|
|
6250
|
+
const ids = resolveAgentIdList(input.agent_id, input.id, input.ids);
|
|
6251
|
+
if (ids.length === 0) {
|
|
6252
|
+
throw new Error("wait requires agent_id/id or ids.");
|
|
6253
|
+
}
|
|
6254
|
+
const timeoutMs = normalizeInteger(
|
|
6255
|
+
input.timeout_ms,
|
|
6256
|
+
options.config.defaultWaitTimeoutMs,
|
|
6257
|
+
1,
|
|
6258
|
+
options.config.maxWaitTimeoutMs
|
|
6259
|
+
);
|
|
6260
|
+
if (usesIdsArray) {
|
|
6261
|
+
const status = await waitForAnyAgentStatus(agents, ids, timeoutMs);
|
|
6262
|
+
return { status, timed_out: Object.keys(status).length === 0, timeout_ms: timeoutMs };
|
|
6263
|
+
}
|
|
6264
|
+
const agent = requireAgent(agents, ids[0]);
|
|
6265
|
+
if (agent.status === "running") {
|
|
6266
|
+
const completed = await waitUntilNotRunning(agent, timeoutMs);
|
|
6267
|
+
if (!completed) {
|
|
6268
|
+
setNotification(
|
|
6269
|
+
agent,
|
|
6270
|
+
"timeout",
|
|
6271
|
+
`Timed out after ${timeoutMs}ms while waiting for ${agent.id}.`
|
|
6272
|
+
);
|
|
6273
|
+
return buildToolResponse(agent, void 0, { timed_out: true, timeout_ms: timeoutMs });
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
return buildToolResponse(agent, void 0, { timed_out: false, timeout_ms: timeoutMs });
|
|
6277
|
+
}
|
|
6278
|
+
}),
|
|
6279
|
+
close_agent: tool({
|
|
6280
|
+
description: "Closes a subagent and aborts its current run if it is still running.",
|
|
6281
|
+
inputSchema: closeSchema,
|
|
6282
|
+
execute: async (input) => {
|
|
6283
|
+
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
6284
|
+
if (!agentId) {
|
|
6285
|
+
throw new Error("close_agent requires agent_id or id.");
|
|
6286
|
+
}
|
|
6287
|
+
const agent = requireAgent(agents, agentId);
|
|
6288
|
+
if (agent.status === "closed") {
|
|
6289
|
+
setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
|
|
6290
|
+
return buildToolResponse(agent, void 0, { cancelled: false });
|
|
6291
|
+
}
|
|
6292
|
+
const cancelled = closeSubagent(agent, `Closed ${agent.id}.`);
|
|
6293
|
+
return buildToolResponse(
|
|
6294
|
+
agent,
|
|
6295
|
+
{ notification: "closed", message: `Closed ${agent.id}.` },
|
|
6296
|
+
{ cancelled }
|
|
6297
|
+
);
|
|
6298
|
+
}
|
|
6299
|
+
})
|
|
6300
|
+
};
|
|
6301
|
+
return {
|
|
6302
|
+
tools,
|
|
6303
|
+
closeAll: async () => {
|
|
6304
|
+
const running = [];
|
|
6305
|
+
for (const agent of agents.values()) {
|
|
6306
|
+
if (agent.status !== "closed") {
|
|
6307
|
+
closeSubagent(agent, `Parent agent loop closed ${agent.id}.`);
|
|
6308
|
+
}
|
|
6309
|
+
if (agent.runningPromise) {
|
|
6310
|
+
running.push(agent.runningPromise);
|
|
6311
|
+
}
|
|
6312
|
+
}
|
|
6313
|
+
if (running.length > 0) {
|
|
6314
|
+
await Promise.race([Promise.allSettled(running), sleep2(2e3)]);
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
};
|
|
6318
|
+
}
|
|
6319
|
+
function requireAgent(agents, id) {
|
|
6320
|
+
const agent = agents.get(id);
|
|
6321
|
+
if (!agent) {
|
|
6322
|
+
throw new Error(`Unknown subagent id: ${id}`);
|
|
6323
|
+
}
|
|
6324
|
+
return agent;
|
|
6325
|
+
}
|
|
6326
|
+
function resolveAgentIdValue(agentId, idAlias) {
|
|
6327
|
+
const preferred = agentId?.trim();
|
|
6328
|
+
if (preferred) {
|
|
6329
|
+
return preferred;
|
|
6330
|
+
}
|
|
6331
|
+
const alias = idAlias?.trim();
|
|
6332
|
+
return alias ?? "";
|
|
6333
|
+
}
|
|
6334
|
+
function resolveAgentIdList(agentId, idAlias, ids) {
|
|
6335
|
+
if (Array.isArray(ids) && ids.length > 0) {
|
|
6336
|
+
return [...new Set(ids.map((value) => value.trim()).filter(Boolean))];
|
|
6337
|
+
}
|
|
6338
|
+
const single = resolveAgentIdValue(agentId, idAlias);
|
|
6339
|
+
return single ? [single] : [];
|
|
6340
|
+
}
|
|
6341
|
+
function resolvePromptValue(prompt, message, items) {
|
|
6342
|
+
const promptValue = prompt?.trim();
|
|
6343
|
+
if (promptValue) {
|
|
6344
|
+
return promptValue;
|
|
6345
|
+
}
|
|
6346
|
+
const messageValue = message?.trim();
|
|
6347
|
+
if (messageValue) {
|
|
6348
|
+
return messageValue;
|
|
6349
|
+
}
|
|
6350
|
+
const itemText = resolveInputItemsText(items);
|
|
6351
|
+
return itemText ?? "";
|
|
6352
|
+
}
|
|
6353
|
+
function resolveInputItemsText(items) {
|
|
6354
|
+
if (!items || items.length === 0) {
|
|
6355
|
+
return void 0;
|
|
6356
|
+
}
|
|
6357
|
+
const lines = [];
|
|
6358
|
+
for (const item of items) {
|
|
6359
|
+
if (typeof item.text === "string" && item.text.trim().length > 0) {
|
|
6360
|
+
lines.push(item.text.trim());
|
|
6361
|
+
continue;
|
|
6362
|
+
}
|
|
6363
|
+
const itemType = typeof item.type === "string" ? item.type.trim() : "";
|
|
6364
|
+
const name = typeof item.name === "string" ? item.name.trim() : "";
|
|
6365
|
+
const path6 = typeof item.path === "string" ? item.path.trim() : "";
|
|
6366
|
+
const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
|
|
6367
|
+
const compact = [itemType, name, path6 || imageUrl].filter(Boolean).join(" ");
|
|
6368
|
+
if (compact) {
|
|
6369
|
+
lines.push(compact);
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
if (lines.length === 0) {
|
|
6373
|
+
return void 0;
|
|
6374
|
+
}
|
|
6375
|
+
return lines.join("\n");
|
|
6376
|
+
}
|
|
6377
|
+
function countActiveAgents(agents) {
|
|
6378
|
+
let count = 0;
|
|
6379
|
+
for (const agent of agents.values()) {
|
|
6380
|
+
if (agent.status !== "closed") {
|
|
6381
|
+
count += 1;
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
return count;
|
|
6385
|
+
}
|
|
6386
|
+
async function waitForAnyAgentStatus(agents, ids, timeoutMs) {
|
|
6387
|
+
const requested = ids.map((id) => requireAgent(agents, id));
|
|
6388
|
+
const deadline = Date.now() + timeoutMs;
|
|
6389
|
+
while (true) {
|
|
6390
|
+
const status = {};
|
|
6391
|
+
for (const agent of requested) {
|
|
6392
|
+
if (agent.status !== "running") {
|
|
6393
|
+
status[agent.id] = buildSnapshot(agent);
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6396
|
+
if (Object.keys(status).length > 0) {
|
|
6397
|
+
return status;
|
|
6398
|
+
}
|
|
6399
|
+
const remaining = deadline - Date.now();
|
|
6400
|
+
if (remaining <= 0) {
|
|
6401
|
+
return {};
|
|
6402
|
+
}
|
|
6403
|
+
await Promise.race(
|
|
6404
|
+
requested.map(async (agent) => {
|
|
6405
|
+
const changed = await waitForVersionChange(agent, agent.version, remaining);
|
|
6406
|
+
if (!changed) {
|
|
6407
|
+
return;
|
|
6408
|
+
}
|
|
6409
|
+
})
|
|
6410
|
+
);
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
function setNotification(agent, notification, message) {
|
|
6414
|
+
agent.notification = notification;
|
|
6415
|
+
agent.notificationMessage = message;
|
|
6416
|
+
agent.updatedAtMs = Date.now();
|
|
6417
|
+
agent.version += 1;
|
|
6418
|
+
notifyWaiters(agent);
|
|
6419
|
+
}
|
|
6420
|
+
function setLifecycle(agent, status, notification, message) {
|
|
6421
|
+
agent.status = status;
|
|
6422
|
+
setNotification(agent, notification, message);
|
|
6423
|
+
}
|
|
6424
|
+
function notifyWaiters(agent) {
|
|
6425
|
+
if (agent.waiters.size === 0) {
|
|
6426
|
+
return;
|
|
6427
|
+
}
|
|
6428
|
+
const waiters = [...agent.waiters];
|
|
6429
|
+
agent.waiters.clear();
|
|
6430
|
+
for (const notify of waiters) {
|
|
6431
|
+
notify();
|
|
6432
|
+
}
|
|
6433
|
+
}
|
|
6434
|
+
function startRun(agent, options) {
|
|
6435
|
+
if (agent.runningPromise) {
|
|
6436
|
+
return "already_running";
|
|
6437
|
+
}
|
|
6438
|
+
const nextInput = agent.pendingInputs.shift();
|
|
6439
|
+
if (!nextInput) {
|
|
6440
|
+
return "no_pending_input";
|
|
6441
|
+
}
|
|
6442
|
+
const input = [...agent.history, { role: "user", content: nextInput }];
|
|
6443
|
+
const abortController = new AbortController();
|
|
6444
|
+
const runStartedAtMs = Date.now();
|
|
6445
|
+
agent.abortController = abortController;
|
|
6446
|
+
if (agent.firstRunStartedAtMs === void 0) {
|
|
6447
|
+
agent.firstRunStartedAtMs = runStartedAtMs;
|
|
6448
|
+
}
|
|
6449
|
+
agent.lastRunStartedAtMs = runStartedAtMs;
|
|
6450
|
+
agent.lastError = void 0;
|
|
6451
|
+
setLifecycle(
|
|
6452
|
+
agent,
|
|
6453
|
+
"running",
|
|
6454
|
+
"run_started",
|
|
6455
|
+
`Subagent ${agent.id} started run ${agent.turns + 1}.`
|
|
6456
|
+
);
|
|
6457
|
+
const runPromise = (async () => {
|
|
6458
|
+
try {
|
|
6459
|
+
const result = await options.runSubagent({
|
|
6460
|
+
agentId: agent.id,
|
|
6461
|
+
depth: agent.depth,
|
|
6462
|
+
model: agent.model,
|
|
6463
|
+
input,
|
|
6464
|
+
instructions: agent.instructions,
|
|
6465
|
+
maxSteps: agent.maxSteps,
|
|
6466
|
+
signal: abortController.signal
|
|
6467
|
+
});
|
|
6468
|
+
if (agent.status === "closed") {
|
|
6469
|
+
return;
|
|
6470
|
+
}
|
|
6471
|
+
agent.lastResult = result;
|
|
6472
|
+
agent.lastError = void 0;
|
|
6473
|
+
agent.turns += 1;
|
|
6474
|
+
agent.history = [...input, { role: "assistant", content: result.text }];
|
|
6475
|
+
setLifecycle(
|
|
6476
|
+
agent,
|
|
6477
|
+
"idle",
|
|
6478
|
+
"run_completed",
|
|
6479
|
+
`Subagent ${agent.id} completed run ${agent.turns}.`
|
|
6480
|
+
);
|
|
6481
|
+
} catch (error) {
|
|
6482
|
+
if (agent.status === "closed") {
|
|
6483
|
+
return;
|
|
6484
|
+
}
|
|
6485
|
+
if (abortController.signal.aborted) {
|
|
6486
|
+
setLifecycle(agent, "idle", "input_queued", `Subagent ${agent.id} run interrupted.`);
|
|
6487
|
+
return;
|
|
6488
|
+
}
|
|
6489
|
+
const message = toErrorMessage(error);
|
|
6490
|
+
agent.lastError = message;
|
|
6491
|
+
setLifecycle(agent, "failed", "run_failed", `Subagent ${agent.id} failed: ${message}`);
|
|
6492
|
+
} finally {
|
|
6493
|
+
const runCompletedAtMs = Date.now();
|
|
6494
|
+
agent.lastRunCompletedAtMs = runCompletedAtMs;
|
|
6495
|
+
agent.lastRunDurationMs = Math.max(0, runCompletedAtMs - runStartedAtMs);
|
|
6496
|
+
agent.runningPromise = void 0;
|
|
6497
|
+
agent.abortController = void 0;
|
|
6498
|
+
}
|
|
6499
|
+
})();
|
|
6500
|
+
agent.runningPromise = runPromise;
|
|
6501
|
+
return "started";
|
|
6502
|
+
}
|
|
6503
|
+
function closeSubagent(agent, message) {
|
|
6504
|
+
const cancelled = Boolean(agent.runningPromise);
|
|
6505
|
+
agent.pendingInputs = [];
|
|
6506
|
+
if (agent.abortController) {
|
|
6507
|
+
agent.abortController.abort("close_agent");
|
|
6508
|
+
}
|
|
6509
|
+
setLifecycle(agent, "closed", "closed", message);
|
|
6510
|
+
return cancelled;
|
|
6511
|
+
}
|
|
6512
|
+
async function waitUntilNotRunning(agent, timeoutMs) {
|
|
6513
|
+
const deadline = Date.now() + timeoutMs;
|
|
6514
|
+
while (agent.status === "running") {
|
|
6515
|
+
const remaining = deadline - Date.now();
|
|
6516
|
+
if (remaining <= 0) {
|
|
6517
|
+
return false;
|
|
6518
|
+
}
|
|
6519
|
+
const currentVersion = agent.version;
|
|
6520
|
+
const changed = await waitForVersionChange(agent, currentVersion, remaining);
|
|
6521
|
+
if (!changed) {
|
|
6522
|
+
return false;
|
|
6523
|
+
}
|
|
6524
|
+
}
|
|
6525
|
+
return true;
|
|
6526
|
+
}
|
|
6527
|
+
async function waitForVersionChange(agent, version, timeoutMs) {
|
|
6528
|
+
if (agent.version !== version) {
|
|
6529
|
+
return true;
|
|
6530
|
+
}
|
|
6531
|
+
return await new Promise((resolve) => {
|
|
6532
|
+
const waiter = () => {
|
|
6533
|
+
cleanup();
|
|
6534
|
+
resolve(true);
|
|
6535
|
+
};
|
|
6536
|
+
const timeout = setTimeout(() => {
|
|
6537
|
+
cleanup();
|
|
6538
|
+
resolve(false);
|
|
6539
|
+
}, timeoutMs);
|
|
6540
|
+
const cleanup = () => {
|
|
6541
|
+
clearTimeout(timeout);
|
|
6542
|
+
agent.waiters.delete(waiter);
|
|
6543
|
+
};
|
|
6544
|
+
agent.waiters.add(waiter);
|
|
6545
|
+
});
|
|
6546
|
+
}
|
|
6547
|
+
function buildToolResponse(agent, override, extra = {}) {
|
|
6548
|
+
const notification = override?.notification ?? agent.notification;
|
|
6549
|
+
const message = override?.message ?? agent.notificationMessage;
|
|
6550
|
+
const snapshot = buildSnapshot(agent);
|
|
6551
|
+
return {
|
|
6552
|
+
agent_id: snapshot.agent_id,
|
|
6553
|
+
notification,
|
|
6554
|
+
message,
|
|
6555
|
+
status: snapshot.status,
|
|
6556
|
+
agent: snapshot,
|
|
6557
|
+
tool_availability: snapshot.status === "closed" ? [] : [...SUBAGENT_CONTROL_TOOL_NAMES],
|
|
6558
|
+
...extra
|
|
6559
|
+
};
|
|
6560
|
+
}
|
|
6561
|
+
function buildSnapshot(agent) {
|
|
6562
|
+
return {
|
|
6563
|
+
agent_id: agent.id,
|
|
6564
|
+
status: agent.status,
|
|
6565
|
+
depth: agent.depth,
|
|
6566
|
+
model: agent.model,
|
|
6567
|
+
pending_inputs: agent.pendingInputs.length,
|
|
6568
|
+
turns: agent.turns,
|
|
6569
|
+
created_at: new Date(agent.createdAtMs).toISOString(),
|
|
6570
|
+
updated_at: new Date(agent.updatedAtMs).toISOString(),
|
|
6571
|
+
...agent.firstRunStartedAtMs ? {
|
|
6572
|
+
first_run_started_at: new Date(agent.firstRunStartedAtMs).toISOString(),
|
|
6573
|
+
spawn_startup_latency_ms: Math.max(0, agent.firstRunStartedAtMs - agent.createdAtMs)
|
|
6574
|
+
} : {},
|
|
6575
|
+
...agent.lastRunStartedAtMs ? { last_run_started_at: new Date(agent.lastRunStartedAtMs).toISOString() } : {},
|
|
6576
|
+
...agent.lastRunCompletedAtMs ? { last_run_completed_at: new Date(agent.lastRunCompletedAtMs).toISOString() } : {},
|
|
6577
|
+
...typeof agent.lastRunDurationMs === "number" ? { last_run_duration_ms: Math.max(0, agent.lastRunDurationMs) } : {},
|
|
6578
|
+
...agent.lastError ? { last_error: agent.lastError } : {},
|
|
6579
|
+
...agent.lastResult ? {
|
|
6580
|
+
last_result: {
|
|
6581
|
+
text: agent.lastResult.text,
|
|
6582
|
+
thoughts: agent.lastResult.thoughts,
|
|
6583
|
+
step_count: agent.lastResult.steps.length,
|
|
6584
|
+
total_cost_usd: agent.lastResult.totalCostUsd
|
|
6585
|
+
}
|
|
6586
|
+
} : {}
|
|
6587
|
+
};
|
|
6588
|
+
}
|
|
6589
|
+
function normalizeInteger(value, fallback, min, max) {
|
|
6590
|
+
const parsed = Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
6591
|
+
return Math.max(min, Math.min(max, parsed));
|
|
6592
|
+
}
|
|
6593
|
+
function normalizeOptionalInteger(value, min, max) {
|
|
6594
|
+
if (!Number.isFinite(value)) {
|
|
6595
|
+
return void 0;
|
|
6596
|
+
}
|
|
6597
|
+
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
6598
|
+
}
|
|
6599
|
+
function trimToUndefined(value) {
|
|
6600
|
+
const trimmed = value?.trim();
|
|
6601
|
+
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
6602
|
+
}
|
|
6603
|
+
function toErrorMessage(error) {
|
|
6604
|
+
if (error instanceof Error) {
|
|
6605
|
+
return error.message;
|
|
6606
|
+
}
|
|
6607
|
+
return String(error);
|
|
6608
|
+
}
|
|
6609
|
+
function sleep2(ms) {
|
|
6610
|
+
return new Promise((resolve) => {
|
|
6611
|
+
setTimeout(resolve, ms);
|
|
6612
|
+
});
|
|
6613
|
+
}
|
|
6614
|
+
|
|
5454
6615
|
// src/tools/filesystemTools.ts
|
|
5455
6616
|
import path5 from "path";
|
|
5456
|
-
import { z as
|
|
6617
|
+
import { z as z6 } from "zod";
|
|
5457
6618
|
|
|
5458
6619
|
// src/tools/applyPatch.ts
|
|
5459
6620
|
import path4 from "path";
|
|
5460
|
-
import { z as
|
|
6621
|
+
import { z as z5 } from "zod";
|
|
5461
6622
|
|
|
5462
6623
|
// src/tools/filesystem.ts
|
|
5463
6624
|
import { promises as fs3 } from "fs";
|
|
@@ -5753,8 +6914,8 @@ var CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION = [
|
|
|
5753
6914
|
"- You must prefix new lines with `+` even when creating a new file",
|
|
5754
6915
|
"- File references can only be relative, NEVER ABSOLUTE."
|
|
5755
6916
|
].join("\n");
|
|
5756
|
-
var applyPatchToolInputSchema =
|
|
5757
|
-
input:
|
|
6917
|
+
var applyPatchToolInputSchema = z5.object({
|
|
6918
|
+
input: z5.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
5758
6919
|
});
|
|
5759
6920
|
function createApplyPatchTool(options = {}) {
|
|
5760
6921
|
return tool({
|
|
@@ -6162,100 +7323,100 @@ var MAX_GREP_LIMIT = 2e3;
|
|
|
6162
7323
|
var DEFAULT_MAX_LINE_LENGTH = 500;
|
|
6163
7324
|
var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
|
|
6164
7325
|
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:
|
|
7326
|
+
var codexReadFileInputSchema = z6.object({
|
|
7327
|
+
file_path: z6.string().min(1).describe("Absolute path to the file"),
|
|
7328
|
+
offset: z6.number().int().min(1).optional().describe("The line number to start reading from. Must be 1 or greater."),
|
|
7329
|
+
limit: z6.number().int().min(1).optional().describe("The maximum number of lines to return."),
|
|
7330
|
+
mode: z6.enum(["slice", "indentation"]).optional().describe('Optional mode selector: "slice" (default) or "indentation".'),
|
|
7331
|
+
indentation: z6.object({
|
|
7332
|
+
anchor_line: z6.number().int().min(1).optional(),
|
|
7333
|
+
max_levels: z6.number().int().min(0).optional(),
|
|
7334
|
+
include_siblings: z6.boolean().optional(),
|
|
7335
|
+
include_header: z6.boolean().optional(),
|
|
7336
|
+
max_lines: z6.number().int().min(1).optional()
|
|
6176
7337
|
}).optional()
|
|
6177
7338
|
});
|
|
6178
|
-
var codexListDirInputSchema =
|
|
6179
|
-
dir_path:
|
|
6180
|
-
offset:
|
|
6181
|
-
limit:
|
|
6182
|
-
depth:
|
|
7339
|
+
var codexListDirInputSchema = z6.object({
|
|
7340
|
+
dir_path: z6.string().min(1).describe("Absolute path to the directory to list."),
|
|
7341
|
+
offset: z6.number().int().min(1).optional().describe("The entry number to start listing from. Must be 1 or greater."),
|
|
7342
|
+
limit: z6.number().int().min(1).optional().describe("The maximum number of entries to return."),
|
|
7343
|
+
depth: z6.number().int().min(1).optional().describe("The maximum directory depth to traverse. Must be 1 or greater.")
|
|
6183
7344
|
});
|
|
6184
|
-
var codexGrepFilesInputSchema =
|
|
6185
|
-
pattern:
|
|
6186
|
-
include:
|
|
6187
|
-
path:
|
|
6188
|
-
limit:
|
|
7345
|
+
var codexGrepFilesInputSchema = z6.object({
|
|
7346
|
+
pattern: z6.string().min(1).describe("Regular expression pattern to search for."),
|
|
7347
|
+
include: z6.string().optional().describe('Optional glob limiting searched files (for example "*.rs").'),
|
|
7348
|
+
path: z6.string().optional().describe("Directory or file path to search. Defaults to cwd."),
|
|
7349
|
+
limit: z6.number().int().min(1).optional().describe("Maximum number of file paths to return (defaults to 100).")
|
|
6189
7350
|
});
|
|
6190
|
-
var applyPatchInputSchema =
|
|
6191
|
-
input:
|
|
7351
|
+
var applyPatchInputSchema = z6.object({
|
|
7352
|
+
input: z6.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
6192
7353
|
});
|
|
6193
|
-
var geminiReadFileInputSchema =
|
|
6194
|
-
file_path:
|
|
6195
|
-
offset:
|
|
6196
|
-
limit:
|
|
7354
|
+
var geminiReadFileInputSchema = z6.object({
|
|
7355
|
+
file_path: z6.string().min(1),
|
|
7356
|
+
offset: z6.number().int().min(0).nullish(),
|
|
7357
|
+
limit: z6.number().int().min(1).nullish()
|
|
6197
7358
|
});
|
|
6198
|
-
var geminiReadFilesInputSchema =
|
|
6199
|
-
paths:
|
|
6200
|
-
line_offset:
|
|
6201
|
-
line_limit:
|
|
6202
|
-
char_offset:
|
|
6203
|
-
char_limit:
|
|
6204
|
-
include_line_numbers:
|
|
7359
|
+
var geminiReadFilesInputSchema = z6.object({
|
|
7360
|
+
paths: z6.array(z6.string().min(1)).min(1),
|
|
7361
|
+
line_offset: z6.number().int().min(0).nullish(),
|
|
7362
|
+
line_limit: z6.number().int().min(1).nullish(),
|
|
7363
|
+
char_offset: z6.number().int().min(0).nullish(),
|
|
7364
|
+
char_limit: z6.number().int().min(1).nullish(),
|
|
7365
|
+
include_line_numbers: z6.boolean().nullish()
|
|
6205
7366
|
}).superRefine((value, context) => {
|
|
6206
7367
|
const hasLineWindow = value.line_offset !== void 0 || value.line_limit !== void 0;
|
|
6207
7368
|
const hasCharWindow = value.char_offset !== void 0 || value.char_limit !== void 0;
|
|
6208
7369
|
if (hasLineWindow && hasCharWindow) {
|
|
6209
7370
|
context.addIssue({
|
|
6210
|
-
code:
|
|
7371
|
+
code: z6.ZodIssueCode.custom,
|
|
6211
7372
|
message: "Use either line_* or char_* window arguments, not both."
|
|
6212
7373
|
});
|
|
6213
7374
|
}
|
|
6214
7375
|
});
|
|
6215
|
-
var geminiWriteFileInputSchema =
|
|
6216
|
-
file_path:
|
|
6217
|
-
content:
|
|
7376
|
+
var geminiWriteFileInputSchema = z6.object({
|
|
7377
|
+
file_path: z6.string().min(1),
|
|
7378
|
+
content: z6.string()
|
|
6218
7379
|
});
|
|
6219
|
-
var geminiReplaceInputSchema =
|
|
6220
|
-
file_path:
|
|
6221
|
-
instruction:
|
|
6222
|
-
old_string:
|
|
6223
|
-
new_string:
|
|
6224
|
-
expected_replacements:
|
|
7380
|
+
var geminiReplaceInputSchema = z6.object({
|
|
7381
|
+
file_path: z6.string().min(1),
|
|
7382
|
+
instruction: z6.string().min(1),
|
|
7383
|
+
old_string: z6.string(),
|
|
7384
|
+
new_string: z6.string(),
|
|
7385
|
+
expected_replacements: z6.number().int().min(1).nullish()
|
|
6225
7386
|
});
|
|
6226
|
-
var geminiListDirectoryInputSchema =
|
|
6227
|
-
dir_path:
|
|
6228
|
-
ignore:
|
|
6229
|
-
file_filtering_options:
|
|
6230
|
-
respect_git_ignore:
|
|
6231
|
-
respect_gemini_ignore:
|
|
7387
|
+
var geminiListDirectoryInputSchema = z6.object({
|
|
7388
|
+
dir_path: z6.string().min(1),
|
|
7389
|
+
ignore: z6.array(z6.string()).nullish(),
|
|
7390
|
+
file_filtering_options: z6.object({
|
|
7391
|
+
respect_git_ignore: z6.boolean().nullish(),
|
|
7392
|
+
respect_gemini_ignore: z6.boolean().nullish()
|
|
6232
7393
|
}).nullish()
|
|
6233
7394
|
});
|
|
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:
|
|
7395
|
+
var geminiRgSearchInputSchema = z6.object({
|
|
7396
|
+
pattern: z6.string().min(1),
|
|
7397
|
+
path: z6.string().nullish(),
|
|
7398
|
+
glob: z6.string().nullish(),
|
|
7399
|
+
case_sensitive: z6.boolean().nullish(),
|
|
7400
|
+
exclude_pattern: z6.string().nullish(),
|
|
7401
|
+
names_only: z6.boolean().nullish(),
|
|
7402
|
+
max_matches_per_file: z6.number().int().min(1).nullish(),
|
|
7403
|
+
max_results: z6.number().int().min(1).nullish()
|
|
6243
7404
|
});
|
|
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:
|
|
7405
|
+
var geminiGrepSearchInputSchema = z6.object({
|
|
7406
|
+
pattern: z6.string().min(1),
|
|
7407
|
+
dir_path: z6.string().nullish(),
|
|
7408
|
+
include: z6.string().nullish(),
|
|
7409
|
+
exclude_pattern: z6.string().nullish(),
|
|
7410
|
+
names_only: z6.boolean().nullish(),
|
|
7411
|
+
max_matches_per_file: z6.number().int().min(1).nullish(),
|
|
7412
|
+
total_max_matches: z6.number().int().min(1).nullish()
|
|
6252
7413
|
});
|
|
6253
|
-
var geminiGlobInputSchema =
|
|
6254
|
-
pattern:
|
|
6255
|
-
dir_path:
|
|
6256
|
-
case_sensitive:
|
|
6257
|
-
respect_git_ignore:
|
|
6258
|
-
respect_gemini_ignore:
|
|
7414
|
+
var geminiGlobInputSchema = z6.object({
|
|
7415
|
+
pattern: z6.string().min(1),
|
|
7416
|
+
dir_path: z6.string().nullish(),
|
|
7417
|
+
case_sensitive: z6.boolean().nullish(),
|
|
7418
|
+
respect_git_ignore: z6.boolean().nullish(),
|
|
7419
|
+
respect_gemini_ignore: z6.boolean().nullish()
|
|
6259
7420
|
});
|
|
6260
7421
|
function resolveFilesystemToolProfile(model, profile = "auto") {
|
|
6261
7422
|
if (profile !== "auto") {
|
|
@@ -7243,19 +8404,107 @@ function isNoEntError(error) {
|
|
|
7243
8404
|
|
|
7244
8405
|
// src/agent.ts
|
|
7245
8406
|
async function runAgentLoop(request) {
|
|
7246
|
-
const
|
|
8407
|
+
const telemetry = createAgentTelemetrySession(request.telemetry);
|
|
8408
|
+
try {
|
|
8409
|
+
return await runAgentLoopInternal(request, { depth: 0, telemetry });
|
|
8410
|
+
} finally {
|
|
8411
|
+
await telemetry?.flush();
|
|
8412
|
+
}
|
|
8413
|
+
}
|
|
8414
|
+
async function runAgentLoopInternal(request, context) {
|
|
8415
|
+
const {
|
|
8416
|
+
tools: customTools,
|
|
8417
|
+
filesystemTool,
|
|
8418
|
+
filesystem_tool,
|
|
8419
|
+
subagentTool,
|
|
8420
|
+
subagent_tool,
|
|
8421
|
+
subagents,
|
|
8422
|
+
telemetry,
|
|
8423
|
+
...toolLoopRequest
|
|
8424
|
+
} = request;
|
|
8425
|
+
const telemetrySession = context.telemetry ?? createAgentTelemetrySession(telemetry);
|
|
8426
|
+
const runId = randomRunId();
|
|
8427
|
+
const startedAtMs = Date.now();
|
|
7247
8428
|
const filesystemSelection = filesystemTool ?? filesystem_tool;
|
|
8429
|
+
const subagentSelection = subagentTool ?? subagent_tool ?? subagents;
|
|
7248
8430
|
const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
|
|
7249
|
-
const
|
|
8431
|
+
const resolvedSubagentConfig = resolveSubagentToolConfig(subagentSelection, context.depth);
|
|
8432
|
+
const subagentController = createSubagentController({
|
|
8433
|
+
runId,
|
|
8434
|
+
model: request.model,
|
|
8435
|
+
depth: context.depth,
|
|
8436
|
+
telemetry: telemetrySession,
|
|
8437
|
+
customTools: customTools ?? {},
|
|
8438
|
+
filesystemSelection,
|
|
8439
|
+
subagentSelection,
|
|
8440
|
+
toolLoopRequest,
|
|
8441
|
+
resolvedSubagentConfig
|
|
8442
|
+
});
|
|
8443
|
+
const mergedTools = mergeToolSets(
|
|
8444
|
+
mergeToolSets(filesystemTools, subagentController?.tools ?? {}),
|
|
8445
|
+
customTools ?? {}
|
|
8446
|
+
);
|
|
7250
8447
|
if (Object.keys(mergedTools).length === 0) {
|
|
7251
8448
|
throw new Error(
|
|
7252
|
-
"runAgentLoop requires at least one tool. Provide `tools` or enable `
|
|
8449
|
+
"runAgentLoop requires at least one tool. Provide `tools`, enable `filesystemTool`, or enable `subagentTool`."
|
|
7253
8450
|
);
|
|
7254
8451
|
}
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
8452
|
+
const instructions = buildLoopInstructions(
|
|
8453
|
+
toolLoopRequest.instructions,
|
|
8454
|
+
resolvedSubagentConfig,
|
|
8455
|
+
context.depth
|
|
8456
|
+
);
|
|
8457
|
+
const emitTelemetry = createAgentTelemetryEmitter({
|
|
8458
|
+
session: telemetrySession,
|
|
8459
|
+
runId,
|
|
8460
|
+
parentRunId: context.parentRunId,
|
|
8461
|
+
depth: context.depth,
|
|
8462
|
+
model: request.model
|
|
8463
|
+
});
|
|
8464
|
+
emitTelemetry({
|
|
8465
|
+
type: "agent.run.started",
|
|
8466
|
+
inputMode: typeof request.input === "string" ? "string" : "messages",
|
|
8467
|
+
customToolCount: Object.keys(customTools ?? {}).length,
|
|
8468
|
+
mergedToolCount: Object.keys(mergedTools).length,
|
|
8469
|
+
filesystemToolsEnabled: Object.keys(filesystemTools).length > 0,
|
|
8470
|
+
subagentToolsEnabled: resolvedSubagentConfig.enabled
|
|
7258
8471
|
});
|
|
8472
|
+
const sourceOnEvent = toolLoopRequest.onEvent;
|
|
8473
|
+
const includeLlmStreamEvents = telemetrySession?.includeLlmStreamEvents === true;
|
|
8474
|
+
const wrappedOnEvent = sourceOnEvent || includeLlmStreamEvents ? (event) => {
|
|
8475
|
+
sourceOnEvent?.(event);
|
|
8476
|
+
if (includeLlmStreamEvents) {
|
|
8477
|
+
emitTelemetry({ type: "agent.run.stream", event });
|
|
8478
|
+
}
|
|
8479
|
+
} : void 0;
|
|
8480
|
+
try {
|
|
8481
|
+
const result = await runToolLoop({
|
|
8482
|
+
...toolLoopRequest,
|
|
8483
|
+
...instructions ? { instructions } : {},
|
|
8484
|
+
...wrappedOnEvent ? { onEvent: wrappedOnEvent } : {},
|
|
8485
|
+
tools: mergedTools
|
|
8486
|
+
});
|
|
8487
|
+
emitTelemetry({
|
|
8488
|
+
type: "agent.run.completed",
|
|
8489
|
+
success: true,
|
|
8490
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
8491
|
+
stepCount: result.steps.length,
|
|
8492
|
+
toolCallCount: countToolCalls(result),
|
|
8493
|
+
totalCostUsd: result.totalCostUsd,
|
|
8494
|
+
usage: summarizeResultUsage(result)
|
|
8495
|
+
});
|
|
8496
|
+
return result;
|
|
8497
|
+
} catch (error) {
|
|
8498
|
+
emitTelemetry({
|
|
8499
|
+
type: "agent.run.completed",
|
|
8500
|
+
success: false,
|
|
8501
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
8502
|
+
error: toErrorMessage2(error)
|
|
8503
|
+
});
|
|
8504
|
+
throw error;
|
|
8505
|
+
} finally {
|
|
8506
|
+
await subagentController?.closeAll();
|
|
8507
|
+
}
|
|
7259
8508
|
}
|
|
7260
8509
|
function resolveFilesystemTools(model, selection) {
|
|
7261
8510
|
if (selection === void 0 || selection === false) {
|
|
@@ -7283,13 +8532,216 @@ function mergeToolSets(base, extra) {
|
|
|
7283
8532
|
for (const [toolName, toolSpec] of Object.entries(extra)) {
|
|
7284
8533
|
if (Object.hasOwn(merged, toolName)) {
|
|
7285
8534
|
throw new Error(
|
|
7286
|
-
`Duplicate tool name "${toolName}" in runAgentLoop. Rename the
|
|
8535
|
+
`Duplicate tool name "${toolName}" in runAgentLoop. Rename one of the conflicting tools or disable an overlapping built-in tool.`
|
|
7287
8536
|
);
|
|
7288
8537
|
}
|
|
7289
8538
|
merged[toolName] = toolSpec;
|
|
7290
8539
|
}
|
|
7291
8540
|
return merged;
|
|
7292
8541
|
}
|
|
8542
|
+
function createSubagentController(params) {
|
|
8543
|
+
if (!params.resolvedSubagentConfig.enabled) {
|
|
8544
|
+
return null;
|
|
8545
|
+
}
|
|
8546
|
+
return createSubagentToolController({
|
|
8547
|
+
config: params.resolvedSubagentConfig,
|
|
8548
|
+
parentDepth: params.depth,
|
|
8549
|
+
parentModel: params.resolvedSubagentConfig.model ?? params.model,
|
|
8550
|
+
buildChildInstructions: (spawnInstructions, childDepth) => buildChildInstructions(spawnInstructions, params.resolvedSubagentConfig, childDepth),
|
|
8551
|
+
runSubagent: async (subagentRequest) => {
|
|
8552
|
+
const childCustomTools = params.resolvedSubagentConfig.inheritTools ? params.customTools : {};
|
|
8553
|
+
const childFilesystemSelection = params.resolvedSubagentConfig.inheritFilesystemTool ? params.filesystemSelection : false;
|
|
8554
|
+
return await runAgentLoopInternal(
|
|
8555
|
+
{
|
|
8556
|
+
model: subagentRequest.model,
|
|
8557
|
+
input: subagentRequest.input,
|
|
8558
|
+
instructions: subagentRequest.instructions,
|
|
8559
|
+
tools: childCustomTools,
|
|
8560
|
+
filesystemTool: childFilesystemSelection,
|
|
8561
|
+
subagentTool: params.subagentSelection,
|
|
8562
|
+
modelTools: params.toolLoopRequest.modelTools,
|
|
8563
|
+
maxSteps: subagentRequest.maxSteps,
|
|
8564
|
+
openAiReasoningEffort: params.toolLoopRequest.openAiReasoningEffort,
|
|
8565
|
+
signal: subagentRequest.signal
|
|
8566
|
+
},
|
|
8567
|
+
{
|
|
8568
|
+
depth: params.depth + 1,
|
|
8569
|
+
parentRunId: params.runId,
|
|
8570
|
+
telemetry: params.telemetry
|
|
8571
|
+
}
|
|
8572
|
+
);
|
|
8573
|
+
}
|
|
8574
|
+
});
|
|
8575
|
+
}
|
|
8576
|
+
function buildLoopInstructions(baseInstructions, config, depth) {
|
|
8577
|
+
if (!config.enabled) {
|
|
8578
|
+
return trimToUndefined2(baseInstructions);
|
|
8579
|
+
}
|
|
8580
|
+
const blocks = [];
|
|
8581
|
+
const base = trimToUndefined2(baseInstructions);
|
|
8582
|
+
if (base) {
|
|
8583
|
+
blocks.push(base);
|
|
8584
|
+
}
|
|
8585
|
+
if (config.promptPattern === "codex") {
|
|
8586
|
+
blocks.push(
|
|
8587
|
+
buildCodexSubagentOrchestratorInstructions({
|
|
8588
|
+
currentDepth: depth,
|
|
8589
|
+
maxDepth: config.maxDepth,
|
|
8590
|
+
maxAgents: config.maxAgents
|
|
8591
|
+
})
|
|
8592
|
+
);
|
|
8593
|
+
}
|
|
8594
|
+
if (config.instructions) {
|
|
8595
|
+
blocks.push(config.instructions);
|
|
8596
|
+
}
|
|
8597
|
+
return blocks.length > 0 ? blocks.join("\n\n") : void 0;
|
|
8598
|
+
}
|
|
8599
|
+
function buildChildInstructions(spawnInstructions, config, childDepth) {
|
|
8600
|
+
const blocks = [];
|
|
8601
|
+
if (config.promptPattern === "codex") {
|
|
8602
|
+
blocks.push(
|
|
8603
|
+
buildCodexSubagentWorkerInstructions({
|
|
8604
|
+
depth: childDepth,
|
|
8605
|
+
maxDepth: config.maxDepth
|
|
8606
|
+
})
|
|
8607
|
+
);
|
|
8608
|
+
}
|
|
8609
|
+
if (config.instructions) {
|
|
8610
|
+
blocks.push(config.instructions);
|
|
8611
|
+
}
|
|
8612
|
+
const perSpawn = trimToUndefined2(spawnInstructions);
|
|
8613
|
+
if (perSpawn) {
|
|
8614
|
+
blocks.push(perSpawn);
|
|
8615
|
+
}
|
|
8616
|
+
return blocks.length > 0 ? blocks.join("\n\n") : void 0;
|
|
8617
|
+
}
|
|
8618
|
+
function trimToUndefined2(value) {
|
|
8619
|
+
const trimmed = value?.trim();
|
|
8620
|
+
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
8621
|
+
}
|
|
8622
|
+
function randomRunId() {
|
|
8623
|
+
return randomBytes3(8).toString("hex");
|
|
8624
|
+
}
|
|
8625
|
+
function toIsoNow() {
|
|
8626
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
8627
|
+
}
|
|
8628
|
+
function toErrorMessage2(error) {
|
|
8629
|
+
if (error instanceof Error && error.message) {
|
|
8630
|
+
return error.message;
|
|
8631
|
+
}
|
|
8632
|
+
if (typeof error === "string") {
|
|
8633
|
+
return error;
|
|
8634
|
+
}
|
|
8635
|
+
return "Unknown error";
|
|
8636
|
+
}
|
|
8637
|
+
function countToolCalls(result) {
|
|
8638
|
+
let count = 0;
|
|
8639
|
+
for (const step of result.steps) {
|
|
8640
|
+
count += step.toolCalls.length;
|
|
8641
|
+
}
|
|
8642
|
+
return count;
|
|
8643
|
+
}
|
|
8644
|
+
function sumUsageValue(current, next) {
|
|
8645
|
+
if (typeof next !== "number" || !Number.isFinite(next)) {
|
|
8646
|
+
return current;
|
|
8647
|
+
}
|
|
8648
|
+
const normalizedNext = Math.max(0, next);
|
|
8649
|
+
if (typeof current !== "number" || !Number.isFinite(current)) {
|
|
8650
|
+
return normalizedNext;
|
|
8651
|
+
}
|
|
8652
|
+
return Math.max(0, current) + normalizedNext;
|
|
8653
|
+
}
|
|
8654
|
+
function summarizeResultUsage(result) {
|
|
8655
|
+
let summary;
|
|
8656
|
+
for (const step of result.steps) {
|
|
8657
|
+
const usage = step.usage;
|
|
8658
|
+
if (!usage) {
|
|
8659
|
+
continue;
|
|
8660
|
+
}
|
|
8661
|
+
summary = {
|
|
8662
|
+
promptTokens: sumUsageValue(summary?.promptTokens, usage.promptTokens),
|
|
8663
|
+
cachedTokens: sumUsageValue(summary?.cachedTokens, usage.cachedTokens),
|
|
8664
|
+
responseTokens: sumUsageValue(summary?.responseTokens, usage.responseTokens),
|
|
8665
|
+
responseImageTokens: sumUsageValue(summary?.responseImageTokens, usage.responseImageTokens),
|
|
8666
|
+
thinkingTokens: sumUsageValue(summary?.thinkingTokens, usage.thinkingTokens),
|
|
8667
|
+
totalTokens: sumUsageValue(summary?.totalTokens, usage.totalTokens),
|
|
8668
|
+
toolUsePromptTokens: sumUsageValue(summary?.toolUsePromptTokens, usage.toolUsePromptTokens)
|
|
8669
|
+
};
|
|
8670
|
+
}
|
|
8671
|
+
return summary;
|
|
8672
|
+
}
|
|
8673
|
+
function isPromiseLike(value) {
|
|
8674
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
8675
|
+
}
|
|
8676
|
+
function isAgentTelemetrySink(value) {
|
|
8677
|
+
return typeof value === "object" && value !== null && typeof value.emit === "function";
|
|
8678
|
+
}
|
|
8679
|
+
function resolveTelemetrySelection(telemetry) {
|
|
8680
|
+
if (!telemetry) {
|
|
8681
|
+
return void 0;
|
|
8682
|
+
}
|
|
8683
|
+
if (isAgentTelemetrySink(telemetry)) {
|
|
8684
|
+
return { sink: telemetry };
|
|
8685
|
+
}
|
|
8686
|
+
if (isAgentTelemetrySink(telemetry.sink)) {
|
|
8687
|
+
return telemetry;
|
|
8688
|
+
}
|
|
8689
|
+
throw new Error("Invalid runAgentLoop telemetry config: expected a sink with emit(event).");
|
|
8690
|
+
}
|
|
8691
|
+
function createAgentTelemetrySession(telemetry) {
|
|
8692
|
+
const config = resolveTelemetrySelection(telemetry);
|
|
8693
|
+
if (!config) {
|
|
8694
|
+
return void 0;
|
|
8695
|
+
}
|
|
8696
|
+
const pending = /* @__PURE__ */ new Set();
|
|
8697
|
+
const trackPromise = (promise) => {
|
|
8698
|
+
pending.add(promise);
|
|
8699
|
+
promise.finally(() => {
|
|
8700
|
+
pending.delete(promise);
|
|
8701
|
+
});
|
|
8702
|
+
};
|
|
8703
|
+
const emit = (event) => {
|
|
8704
|
+
try {
|
|
8705
|
+
const output = config.sink.emit(event);
|
|
8706
|
+
if (isPromiseLike(output)) {
|
|
8707
|
+
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
8708
|
+
trackPromise(task);
|
|
8709
|
+
}
|
|
8710
|
+
} catch {
|
|
8711
|
+
}
|
|
8712
|
+
};
|
|
8713
|
+
const flush = async () => {
|
|
8714
|
+
while (pending.size > 0) {
|
|
8715
|
+
await Promise.allSettled([...pending]);
|
|
8716
|
+
}
|
|
8717
|
+
if (typeof config.sink.flush === "function") {
|
|
8718
|
+
try {
|
|
8719
|
+
await config.sink.flush();
|
|
8720
|
+
} catch {
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
};
|
|
8724
|
+
return {
|
|
8725
|
+
includeLlmStreamEvents: config.includeLlmStreamEvents === true,
|
|
8726
|
+
emit,
|
|
8727
|
+
flush
|
|
8728
|
+
};
|
|
8729
|
+
}
|
|
8730
|
+
function createAgentTelemetryEmitter(params) {
|
|
8731
|
+
return (event) => {
|
|
8732
|
+
if (!params.session) {
|
|
8733
|
+
return;
|
|
8734
|
+
}
|
|
8735
|
+
params.session.emit({
|
|
8736
|
+
...event,
|
|
8737
|
+
timestamp: toIsoNow(),
|
|
8738
|
+
runId: params.runId,
|
|
8739
|
+
...params.parentRunId ? { parentRunId: params.parentRunId } : {},
|
|
8740
|
+
depth: params.depth,
|
|
8741
|
+
model: params.model
|
|
8742
|
+
});
|
|
8743
|
+
};
|
|
8744
|
+
}
|
|
7293
8745
|
export {
|
|
7294
8746
|
CHATGPT_MODEL_IDS,
|
|
7295
8747
|
CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
|
|
@@ -7312,6 +8764,7 @@ export {
|
|
|
7312
8764
|
appendMarkdownSourcesSection,
|
|
7313
8765
|
applyPatch,
|
|
7314
8766
|
configureGemini,
|
|
8767
|
+
configureModelConcurrency,
|
|
7315
8768
|
convertGooglePartsToLlmParts,
|
|
7316
8769
|
createApplyPatchTool,
|
|
7317
8770
|
createCodexApplyPatchTool,
|
|
@@ -7356,6 +8809,7 @@ export {
|
|
|
7356
8809
|
loadLocalEnv,
|
|
7357
8810
|
parseJsonFromLlmText,
|
|
7358
8811
|
refreshChatGptOauthToken,
|
|
8812
|
+
resetModelConcurrencyConfig,
|
|
7359
8813
|
resolveFilesystemToolProfile,
|
|
7360
8814
|
resolveFireworksModelId,
|
|
7361
8815
|
runAgentLoop,
|