@ljoukov/llm 3.0.4 → 3.0.8
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 +193 -19
- package/dist/index.cjs +1929 -788
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -19
- package/dist/index.d.ts +149 -19
- package/dist/index.js +1924 -788
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -51,6 +51,7 @@ __export(index_exports, {
|
|
|
51
51
|
appendMarkdownSourcesSection: () => appendMarkdownSourcesSection,
|
|
52
52
|
applyPatch: () => applyPatch,
|
|
53
53
|
configureGemini: () => configureGemini,
|
|
54
|
+
configureModelConcurrency: () => configureModelConcurrency,
|
|
54
55
|
convertGooglePartsToLlmParts: () => convertGooglePartsToLlmParts,
|
|
55
56
|
createApplyPatchTool: () => createApplyPatchTool,
|
|
56
57
|
createCodexApplyPatchTool: () => createCodexApplyPatchTool,
|
|
@@ -70,6 +71,7 @@ __export(index_exports, {
|
|
|
70
71
|
createReadFilesTool: () => createReadFilesTool,
|
|
71
72
|
createReplaceTool: () => createReplaceTool,
|
|
72
73
|
createRgSearchTool: () => createRgSearchTool,
|
|
74
|
+
createToolLoopSteeringChannel: () => createToolLoopSteeringChannel,
|
|
73
75
|
createWriteFileTool: () => createWriteFileTool,
|
|
74
76
|
customTool: () => customTool,
|
|
75
77
|
encodeChatGptAuthJson: () => encodeChatGptAuthJson,
|
|
@@ -95,13 +97,16 @@ __export(index_exports, {
|
|
|
95
97
|
loadLocalEnv: () => loadLocalEnv,
|
|
96
98
|
parseJsonFromLlmText: () => parseJsonFromLlmText,
|
|
97
99
|
refreshChatGptOauthToken: () => refreshChatGptOauthToken,
|
|
100
|
+
resetModelConcurrencyConfig: () => resetModelConcurrencyConfig,
|
|
98
101
|
resolveFilesystemToolProfile: () => resolveFilesystemToolProfile,
|
|
99
102
|
resolveFireworksModelId: () => resolveFireworksModelId,
|
|
100
103
|
runAgentLoop: () => runAgentLoop,
|
|
101
104
|
runToolLoop: () => runToolLoop,
|
|
102
105
|
sanitisePartForLogging: () => sanitisePartForLogging,
|
|
106
|
+
streamAgentLoop: () => streamAgentLoop,
|
|
103
107
|
streamJson: () => streamJson,
|
|
104
108
|
streamText: () => streamText,
|
|
109
|
+
streamToolLoop: () => streamToolLoop,
|
|
105
110
|
stripCodexCitationMarkers: () => stripCodexCitationMarkers,
|
|
106
111
|
toGeminiJsonSchema: () => toGeminiJsonSchema,
|
|
107
112
|
tool: () => tool
|
|
@@ -1693,23 +1698,16 @@ function parseEventBlock(raw) {
|
|
|
1693
1698
|
var MIN_MODEL_CONCURRENCY_CAP = 1;
|
|
1694
1699
|
var MAX_MODEL_CONCURRENCY_CAP = 64;
|
|
1695
1700
|
var DEFAULT_MODEL_CONCURRENCY_CAP = 3;
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
}
|
|
1707
|
-
const parsed = Number.parseInt(normalized, 10);
|
|
1708
|
-
if (!Number.isFinite(parsed)) {
|
|
1709
|
-
return void 0;
|
|
1710
|
-
}
|
|
1711
|
-
return parsed;
|
|
1712
|
-
}
|
|
1701
|
+
var DEFAULT_OPENAI_MODEL_CONCURRENCY_CAP = 12;
|
|
1702
|
+
var DEFAULT_GOOGLE_MODEL_CONCURRENCY_CAP = 4;
|
|
1703
|
+
var DEFAULT_GOOGLE_PREVIEW_MODEL_CONCURRENCY_CAP = 2;
|
|
1704
|
+
var DEFAULT_FIREWORKS_MODEL_CONCURRENCY_CAP = 6;
|
|
1705
|
+
var MODEL_CONCURRENCY_PROVIDERS = [
|
|
1706
|
+
"openai",
|
|
1707
|
+
"google",
|
|
1708
|
+
"fireworks"
|
|
1709
|
+
];
|
|
1710
|
+
var configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1713
1711
|
function clampModelConcurrencyCap(value) {
|
|
1714
1712
|
if (!Number.isFinite(value)) {
|
|
1715
1713
|
return DEFAULT_MODEL_CONCURRENCY_CAP;
|
|
@@ -1723,30 +1721,94 @@ function clampModelConcurrencyCap(value) {
|
|
|
1723
1721
|
}
|
|
1724
1722
|
return rounded;
|
|
1725
1723
|
}
|
|
1726
|
-
function
|
|
1727
|
-
return modelId.trim().
|
|
1724
|
+
function normalizeModelIdForConfig(modelId) {
|
|
1725
|
+
return modelId.trim().toLowerCase();
|
|
1728
1726
|
}
|
|
1729
|
-
function
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
const parsed = parsePositiveInteger(env[key]);
|
|
1744
|
-
if (parsed === void 0) {
|
|
1727
|
+
function normalizeCap(value) {
|
|
1728
|
+
if (value === void 0 || !Number.isFinite(value)) {
|
|
1729
|
+
return void 0;
|
|
1730
|
+
}
|
|
1731
|
+
return clampModelConcurrencyCap(value);
|
|
1732
|
+
}
|
|
1733
|
+
function normalizeModelCapMap(caps) {
|
|
1734
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
1735
|
+
if (!caps) {
|
|
1736
|
+
return normalized;
|
|
1737
|
+
}
|
|
1738
|
+
for (const [modelId, cap] of Object.entries(caps)) {
|
|
1739
|
+
const modelKey = normalizeModelIdForConfig(modelId);
|
|
1740
|
+
if (!modelKey) {
|
|
1745
1741
|
continue;
|
|
1746
1742
|
}
|
|
1747
|
-
|
|
1743
|
+
const normalizedCap = normalizeCap(cap);
|
|
1744
|
+
if (normalizedCap === void 0) {
|
|
1745
|
+
continue;
|
|
1746
|
+
}
|
|
1747
|
+
normalized.set(modelKey, normalizedCap);
|
|
1748
|
+
}
|
|
1749
|
+
return normalized;
|
|
1750
|
+
}
|
|
1751
|
+
function normalizeModelConcurrencyConfig(config) {
|
|
1752
|
+
const providerCaps = {};
|
|
1753
|
+
const providerModelCaps = {
|
|
1754
|
+
openai: /* @__PURE__ */ new Map(),
|
|
1755
|
+
google: /* @__PURE__ */ new Map(),
|
|
1756
|
+
fireworks: /* @__PURE__ */ new Map()
|
|
1757
|
+
};
|
|
1758
|
+
for (const provider of MODEL_CONCURRENCY_PROVIDERS) {
|
|
1759
|
+
const providerCap = normalizeCap(config.providerCaps?.[provider]);
|
|
1760
|
+
if (providerCap !== void 0) {
|
|
1761
|
+
providerCaps[provider] = providerCap;
|
|
1762
|
+
}
|
|
1763
|
+
providerModelCaps[provider] = new Map(
|
|
1764
|
+
normalizeModelCapMap(config.providerModelCaps?.[provider])
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
return {
|
|
1768
|
+
globalCap: normalizeCap(config.globalCap),
|
|
1769
|
+
providerCaps,
|
|
1770
|
+
modelCaps: normalizeModelCapMap(config.modelCaps),
|
|
1771
|
+
providerModelCaps
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
function resolveDefaultProviderCap(provider, modelId) {
|
|
1775
|
+
if (provider === "openai") {
|
|
1776
|
+
return DEFAULT_OPENAI_MODEL_CONCURRENCY_CAP;
|
|
1777
|
+
}
|
|
1778
|
+
if (provider === "google") {
|
|
1779
|
+
return modelId?.includes("preview") ? DEFAULT_GOOGLE_PREVIEW_MODEL_CONCURRENCY_CAP : DEFAULT_GOOGLE_MODEL_CONCURRENCY_CAP;
|
|
1780
|
+
}
|
|
1781
|
+
return DEFAULT_FIREWORKS_MODEL_CONCURRENCY_CAP;
|
|
1782
|
+
}
|
|
1783
|
+
function configureModelConcurrency(config = {}) {
|
|
1784
|
+
configuredModelConcurrency = normalizeModelConcurrencyConfig(config);
|
|
1785
|
+
}
|
|
1786
|
+
function resetModelConcurrencyConfig() {
|
|
1787
|
+
configuredModelConcurrency = normalizeModelConcurrencyConfig({});
|
|
1788
|
+
}
|
|
1789
|
+
function resolveModelConcurrencyCap(options) {
|
|
1790
|
+
const modelId = options.modelId ? normalizeModelIdForConfig(options.modelId) : void 0;
|
|
1791
|
+
const config = options.config ? normalizeModelConcurrencyConfig(options.config) : configuredModelConcurrency;
|
|
1792
|
+
const providerModelCap = modelId ? config.providerModelCaps[options.provider].get(modelId) : void 0;
|
|
1793
|
+
if (providerModelCap !== void 0) {
|
|
1794
|
+
return providerModelCap;
|
|
1795
|
+
}
|
|
1796
|
+
const modelCap = modelId ? config.modelCaps.get(modelId) : void 0;
|
|
1797
|
+
if (modelCap !== void 0) {
|
|
1798
|
+
return modelCap;
|
|
1748
1799
|
}
|
|
1749
|
-
|
|
1800
|
+
const providerCap = config.providerCaps[options.provider];
|
|
1801
|
+
if (providerCap !== void 0) {
|
|
1802
|
+
return providerCap;
|
|
1803
|
+
}
|
|
1804
|
+
if (config.globalCap !== void 0) {
|
|
1805
|
+
return config.globalCap;
|
|
1806
|
+
}
|
|
1807
|
+
const defaultCap = normalizeCap(options.defaultCap);
|
|
1808
|
+
if (defaultCap !== void 0) {
|
|
1809
|
+
return defaultCap;
|
|
1810
|
+
}
|
|
1811
|
+
return resolveDefaultProviderCap(options.provider, modelId);
|
|
1750
1812
|
}
|
|
1751
1813
|
|
|
1752
1814
|
// src/utils/scheduler.ts
|
|
@@ -1855,12 +1917,20 @@ function createCallScheduler(options = {}) {
|
|
|
1855
1917
|
release?.();
|
|
1856
1918
|
}
|
|
1857
1919
|
}
|
|
1858
|
-
async function attemptWithRetries(fn, attempt) {
|
|
1920
|
+
async function attemptWithRetries(fn, attempt, state) {
|
|
1859
1921
|
try {
|
|
1922
|
+
const spacingStartedAtMs = Date.now();
|
|
1860
1923
|
await applyStartSpacing();
|
|
1924
|
+
const callStartedAtMs = Date.now();
|
|
1925
|
+
state.schedulerDelayMs += Math.max(0, callStartedAtMs - spacingStartedAtMs);
|
|
1926
|
+
if (state.startedAtMs === void 0) {
|
|
1927
|
+
state.startedAtMs = callStartedAtMs;
|
|
1928
|
+
}
|
|
1929
|
+
state.attempts = Math.max(state.attempts, attempt);
|
|
1861
1930
|
return await fn();
|
|
1862
1931
|
} catch (error) {
|
|
1863
1932
|
if (isOverloadError2(error)) {
|
|
1933
|
+
state.overloadCount += 1;
|
|
1864
1934
|
consecutiveSuccesses = 0;
|
|
1865
1935
|
currentParallelLimit = Math.max(1, Math.ceil(currentParallelLimit / 2));
|
|
1866
1936
|
}
|
|
@@ -1877,9 +1947,10 @@ function createCallScheduler(options = {}) {
|
|
|
1877
1947
|
}
|
|
1878
1948
|
const normalizedDelay = Math.max(0, delay);
|
|
1879
1949
|
if (normalizedDelay > 0) {
|
|
1950
|
+
state.retryDelayMs += normalizedDelay;
|
|
1880
1951
|
await sleep(normalizedDelay);
|
|
1881
1952
|
}
|
|
1882
|
-
return attemptWithRetries(fn, attempt + 1);
|
|
1953
|
+
return attemptWithRetries(fn, attempt + 1, state);
|
|
1883
1954
|
}
|
|
1884
1955
|
}
|
|
1885
1956
|
function drainQueue() {
|
|
@@ -1892,11 +1963,22 @@ function createCallScheduler(options = {}) {
|
|
|
1892
1963
|
void task();
|
|
1893
1964
|
}
|
|
1894
1965
|
}
|
|
1895
|
-
function run(fn) {
|
|
1966
|
+
function run(fn, runOptions = {}) {
|
|
1896
1967
|
return new Promise((resolve, reject) => {
|
|
1968
|
+
const enqueuedAtMs = Date.now();
|
|
1897
1969
|
const job = async () => {
|
|
1970
|
+
const dequeuedAtMs = Date.now();
|
|
1971
|
+
const state = {
|
|
1972
|
+
enqueuedAtMs,
|
|
1973
|
+
dequeuedAtMs,
|
|
1974
|
+
schedulerDelayMs: 0,
|
|
1975
|
+
retryDelayMs: 0,
|
|
1976
|
+
attempts: 0,
|
|
1977
|
+
overloadCount: 0
|
|
1978
|
+
};
|
|
1898
1979
|
try {
|
|
1899
|
-
const result = await attemptWithRetries(fn, 1);
|
|
1980
|
+
const result = await attemptWithRetries(fn, 1, state);
|
|
1981
|
+
state.completedAtMs = Date.now();
|
|
1900
1982
|
consecutiveSuccesses += 1;
|
|
1901
1983
|
if (currentParallelLimit < maxParallelRequests && consecutiveSuccesses >= increaseAfterConsecutiveSuccesses) {
|
|
1902
1984
|
currentParallelLimit += 1;
|
|
@@ -1904,8 +1986,26 @@ function createCallScheduler(options = {}) {
|
|
|
1904
1986
|
}
|
|
1905
1987
|
resolve(result);
|
|
1906
1988
|
} catch (error) {
|
|
1989
|
+
state.completedAtMs = Date.now();
|
|
1907
1990
|
reject(toError(error));
|
|
1908
1991
|
} finally {
|
|
1992
|
+
const startedAtMs = state.startedAtMs ?? state.dequeuedAtMs;
|
|
1993
|
+
const completedAtMs = state.completedAtMs ?? Date.now();
|
|
1994
|
+
const metrics = {
|
|
1995
|
+
enqueuedAtMs: state.enqueuedAtMs,
|
|
1996
|
+
dequeuedAtMs: state.dequeuedAtMs,
|
|
1997
|
+
startedAtMs,
|
|
1998
|
+
completedAtMs,
|
|
1999
|
+
queueWaitMs: Math.max(0, state.dequeuedAtMs - state.enqueuedAtMs),
|
|
2000
|
+
schedulerDelayMs: Math.max(0, state.schedulerDelayMs),
|
|
2001
|
+
retryDelayMs: Math.max(0, state.retryDelayMs),
|
|
2002
|
+
attempts: Math.max(1, state.attempts),
|
|
2003
|
+
overloadCount: Math.max(0, state.overloadCount)
|
|
2004
|
+
};
|
|
2005
|
+
try {
|
|
2006
|
+
runOptions.onSettled?.(metrics);
|
|
2007
|
+
} catch {
|
|
2008
|
+
}
|
|
1909
2009
|
activeCount -= 1;
|
|
1910
2010
|
queueMicrotask(drainQueue);
|
|
1911
2011
|
}
|
|
@@ -2002,7 +2102,7 @@ function getSchedulerForModel(modelId) {
|
|
|
2002
2102
|
}
|
|
2003
2103
|
const created = createCallScheduler({
|
|
2004
2104
|
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2005
|
-
|
|
2105
|
+
provider: "fireworks",
|
|
2006
2106
|
modelId: normalizedModelId
|
|
2007
2107
|
}),
|
|
2008
2108
|
minIntervalBetweenStartMs: 200,
|
|
@@ -2011,8 +2111,8 @@ function getSchedulerForModel(modelId) {
|
|
|
2011
2111
|
schedulerByModel.set(schedulerKey, created);
|
|
2012
2112
|
return created;
|
|
2013
2113
|
}
|
|
2014
|
-
async function runFireworksCall(fn, modelId) {
|
|
2015
|
-
return getSchedulerForModel(modelId).run(async () => fn(getFireworksClient()));
|
|
2114
|
+
async function runFireworksCall(fn, modelId, runOptions) {
|
|
2115
|
+
return getSchedulerForModel(modelId).run(async () => fn(getFireworksClient()), runOptions);
|
|
2016
2116
|
}
|
|
2017
2117
|
|
|
2018
2118
|
// src/fireworks/models.ts
|
|
@@ -2378,7 +2478,7 @@ function getSchedulerForModel2(modelId) {
|
|
|
2378
2478
|
}
|
|
2379
2479
|
const created = createCallScheduler({
|
|
2380
2480
|
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2381
|
-
|
|
2481
|
+
provider: "google",
|
|
2382
2482
|
modelId: normalizedModelId
|
|
2383
2483
|
}),
|
|
2384
2484
|
minIntervalBetweenStartMs: 200,
|
|
@@ -2398,8 +2498,8 @@ function getSchedulerForModel2(modelId) {
|
|
|
2398
2498
|
schedulerByModel2.set(schedulerKey, created);
|
|
2399
2499
|
return created;
|
|
2400
2500
|
}
|
|
2401
|
-
async function runGeminiCall(fn, modelId) {
|
|
2402
|
-
return getSchedulerForModel2(modelId).run(async () => fn(await getGeminiClient()));
|
|
2501
|
+
async function runGeminiCall(fn, modelId, runOptions) {
|
|
2502
|
+
return getSchedulerForModel2(modelId).run(async () => fn(await getGeminiClient()), runOptions);
|
|
2403
2503
|
}
|
|
2404
2504
|
|
|
2405
2505
|
// src/openai/client.ts
|
|
@@ -2571,7 +2671,7 @@ function getSchedulerForModel3(modelId) {
|
|
|
2571
2671
|
}
|
|
2572
2672
|
const created = createCallScheduler({
|
|
2573
2673
|
maxParallelRequests: resolveModelConcurrencyCap({
|
|
2574
|
-
|
|
2674
|
+
provider: "openai",
|
|
2575
2675
|
modelId: normalizedModelId
|
|
2576
2676
|
}),
|
|
2577
2677
|
minIntervalBetweenStartMs: 200,
|
|
@@ -2580,8 +2680,8 @@ function getSchedulerForModel3(modelId) {
|
|
|
2580
2680
|
schedulerByModel3.set(schedulerKey, created);
|
|
2581
2681
|
return created;
|
|
2582
2682
|
}
|
|
2583
|
-
async function runOpenAiCall(fn, modelId) {
|
|
2584
|
-
return getSchedulerForModel3(modelId).run(async () => fn(getOpenAiClient()));
|
|
2683
|
+
async function runOpenAiCall(fn, modelId, runOptions) {
|
|
2684
|
+
return getSchedulerForModel3(modelId).run(async () => fn(getOpenAiClient()), runOptions);
|
|
2585
2685
|
}
|
|
2586
2686
|
|
|
2587
2687
|
// src/openai/models.ts
|
|
@@ -3035,9 +3135,9 @@ function isRetryableChatGptTransportError(error) {
|
|
|
3035
3135
|
return false;
|
|
3036
3136
|
}
|
|
3037
3137
|
const message = error.message.toLowerCase();
|
|
3038
|
-
return message === "terminated" || message.includes("socket hang up") || message.includes("fetch failed") || message.includes("network");
|
|
3138
|
+
return message === "terminated" || message.includes("socket hang up") || message.includes("fetch failed") || message.includes("network") || message.includes("responses websocket");
|
|
3039
3139
|
}
|
|
3040
|
-
async function collectChatGptCodexResponseWithRetry(options, maxAttempts =
|
|
3140
|
+
async function collectChatGptCodexResponseWithRetry(options, maxAttempts = 3) {
|
|
3041
3141
|
let attempt = 1;
|
|
3042
3142
|
while (true) {
|
|
3043
3143
|
try {
|
|
@@ -3942,77 +4042,153 @@ function buildToolErrorOutput(message, issues) {
|
|
|
3942
4042
|
}
|
|
3943
4043
|
return output;
|
|
3944
4044
|
}
|
|
4045
|
+
var SUBAGENT_WAIT_TOOL_NAME = "wait";
|
|
4046
|
+
function toIsoTimestamp(ms) {
|
|
4047
|
+
return new Date(ms).toISOString();
|
|
4048
|
+
}
|
|
4049
|
+
function toToolResultDuration(result) {
|
|
4050
|
+
return typeof result.durationMs === "number" && Number.isFinite(result.durationMs) ? Math.max(0, result.durationMs) : 0;
|
|
4051
|
+
}
|
|
4052
|
+
function schedulerMetricsOrDefault(metrics) {
|
|
4053
|
+
if (!metrics) {
|
|
4054
|
+
return {
|
|
4055
|
+
queueWaitMs: 0,
|
|
4056
|
+
schedulerDelayMs: 0,
|
|
4057
|
+
providerRetryDelayMs: 0,
|
|
4058
|
+
providerAttempts: 1
|
|
4059
|
+
};
|
|
4060
|
+
}
|
|
4061
|
+
return {
|
|
4062
|
+
queueWaitMs: Math.max(0, metrics.queueWaitMs),
|
|
4063
|
+
schedulerDelayMs: Math.max(0, metrics.schedulerDelayMs),
|
|
4064
|
+
providerRetryDelayMs: Math.max(0, metrics.retryDelayMs),
|
|
4065
|
+
providerAttempts: Math.max(1, metrics.attempts),
|
|
4066
|
+
modelCallStartedAtMs: metrics.startedAtMs
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
4069
|
+
function buildStepTiming(params) {
|
|
4070
|
+
const scheduler = schedulerMetricsOrDefault(params.schedulerMetrics);
|
|
4071
|
+
const modelCallStartedAtMs = scheduler.modelCallStartedAtMs ?? params.stepStartedAtMs;
|
|
4072
|
+
const firstModelEventAtMs = params.firstModelEventAtMs;
|
|
4073
|
+
const effectiveFirstEventAtMs = firstModelEventAtMs !== void 0 ? Math.max(modelCallStartedAtMs, firstModelEventAtMs) : params.modelCompletedAtMs;
|
|
4074
|
+
const connectionSetupMs = Math.max(0, effectiveFirstEventAtMs - modelCallStartedAtMs);
|
|
4075
|
+
const activeGenerationMs = Math.max(0, params.modelCompletedAtMs - effectiveFirstEventAtMs);
|
|
4076
|
+
return {
|
|
4077
|
+
startedAt: toIsoTimestamp(params.stepStartedAtMs),
|
|
4078
|
+
completedAt: toIsoTimestamp(params.stepCompletedAtMs),
|
|
4079
|
+
totalMs: Math.max(0, params.stepCompletedAtMs - params.stepStartedAtMs),
|
|
4080
|
+
queueWaitMs: scheduler.queueWaitMs,
|
|
4081
|
+
connectionSetupMs,
|
|
4082
|
+
activeGenerationMs,
|
|
4083
|
+
toolExecutionMs: Math.max(0, params.toolExecutionMs),
|
|
4084
|
+
waitToolMs: Math.max(0, params.waitToolMs),
|
|
4085
|
+
schedulerDelayMs: scheduler.schedulerDelayMs,
|
|
4086
|
+
providerRetryDelayMs: scheduler.providerRetryDelayMs,
|
|
4087
|
+
providerAttempts: scheduler.providerAttempts
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
function extractSpawnStartupMetrics(outputPayload) {
|
|
4091
|
+
if (!outputPayload || typeof outputPayload !== "object") {
|
|
4092
|
+
return void 0;
|
|
4093
|
+
}
|
|
4094
|
+
const outputRecord = outputPayload;
|
|
4095
|
+
const notification = typeof outputRecord.notification === "string" ? outputRecord.notification : "";
|
|
4096
|
+
if (notification !== "spawned") {
|
|
4097
|
+
return void 0;
|
|
4098
|
+
}
|
|
4099
|
+
const agent = outputRecord.agent;
|
|
4100
|
+
if (!agent || typeof agent !== "object") {
|
|
4101
|
+
return void 0;
|
|
4102
|
+
}
|
|
4103
|
+
const agentRecord = agent;
|
|
4104
|
+
const startupLatencyMs = agentRecord.spawn_startup_latency_ms;
|
|
4105
|
+
if (typeof startupLatencyMs !== "number" || !Number.isFinite(startupLatencyMs)) {
|
|
4106
|
+
return void 0;
|
|
4107
|
+
}
|
|
4108
|
+
return {
|
|
4109
|
+
spawnStartupLatencyMs: Math.max(0, startupLatencyMs)
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
3945
4112
|
async function executeToolCall(params) {
|
|
3946
4113
|
const { callKind, toolName, tool: tool2, rawInput, parseError } = params;
|
|
3947
|
-
|
|
3948
|
-
|
|
4114
|
+
const startedAtMs = Date.now();
|
|
4115
|
+
const finalize = (base, outputPayload, metrics) => {
|
|
4116
|
+
const completedAtMs = Date.now();
|
|
3949
4117
|
return {
|
|
3950
|
-
result: {
|
|
3951
|
-
|
|
4118
|
+
result: {
|
|
4119
|
+
...base,
|
|
4120
|
+
startedAt: toIsoTimestamp(startedAtMs),
|
|
4121
|
+
completedAt: toIsoTimestamp(completedAtMs),
|
|
4122
|
+
durationMs: Math.max(0, completedAtMs - startedAtMs),
|
|
4123
|
+
...metrics ? { metrics } : {}
|
|
4124
|
+
},
|
|
4125
|
+
outputPayload
|
|
3952
4126
|
};
|
|
4127
|
+
};
|
|
4128
|
+
if (!tool2) {
|
|
4129
|
+
const message = `Unknown tool: ${toolName}`;
|
|
4130
|
+
const outputPayload = buildToolErrorOutput(message);
|
|
4131
|
+
return finalize(
|
|
4132
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
4133
|
+
outputPayload
|
|
4134
|
+
);
|
|
3953
4135
|
}
|
|
3954
4136
|
if (callKind === "custom") {
|
|
3955
4137
|
if (!isCustomTool(tool2)) {
|
|
3956
4138
|
const message = `Tool ${toolName} was called as custom_tool_call but is declared as function.`;
|
|
3957
4139
|
const outputPayload = buildToolErrorOutput(message);
|
|
3958
|
-
return
|
|
3959
|
-
|
|
4140
|
+
return finalize(
|
|
4141
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
3960
4142
|
outputPayload
|
|
3961
|
-
|
|
4143
|
+
);
|
|
3962
4144
|
}
|
|
3963
4145
|
const input = typeof rawInput === "string" ? rawInput : String(rawInput ?? "");
|
|
3964
4146
|
try {
|
|
3965
4147
|
const output = await tool2.execute(input);
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
outputPayload: output
|
|
3969
|
-
};
|
|
4148
|
+
const metrics = toolName === "spawn_agent" ? extractSpawnStartupMetrics(output) : void 0;
|
|
4149
|
+
return finalize({ toolName, input, output }, output, metrics);
|
|
3970
4150
|
} catch (error) {
|
|
3971
4151
|
const message = error instanceof Error ? error.message : String(error);
|
|
3972
4152
|
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
3973
|
-
return {
|
|
3974
|
-
result: { toolName, input, output: outputPayload, error: message },
|
|
3975
|
-
outputPayload
|
|
3976
|
-
};
|
|
4153
|
+
return finalize({ toolName, input, output: outputPayload, error: message }, outputPayload);
|
|
3977
4154
|
}
|
|
3978
4155
|
}
|
|
3979
4156
|
if (isCustomTool(tool2)) {
|
|
3980
4157
|
const message = `Tool ${toolName} was called as function_call but is declared as custom.`;
|
|
3981
4158
|
const outputPayload = buildToolErrorOutput(message);
|
|
3982
|
-
return
|
|
3983
|
-
|
|
4159
|
+
return finalize(
|
|
4160
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
3984
4161
|
outputPayload
|
|
3985
|
-
|
|
4162
|
+
);
|
|
3986
4163
|
}
|
|
3987
4164
|
if (parseError) {
|
|
3988
4165
|
const message = `Invalid JSON for tool ${toolName}: ${parseError}`;
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
outputPayload:
|
|
3992
|
-
|
|
4166
|
+
const outputPayload = buildToolErrorOutput(message);
|
|
4167
|
+
return finalize(
|
|
4168
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
4169
|
+
outputPayload
|
|
4170
|
+
);
|
|
3993
4171
|
}
|
|
3994
4172
|
const parsed = tool2.inputSchema.safeParse(rawInput);
|
|
3995
4173
|
if (!parsed.success) {
|
|
3996
4174
|
const message = `Invalid tool arguments for ${toolName}: ${formatZodIssues(parsed.error.issues)}`;
|
|
3997
4175
|
const outputPayload = buildToolErrorOutput(message, parsed.error.issues);
|
|
3998
|
-
return
|
|
3999
|
-
|
|
4176
|
+
return finalize(
|
|
4177
|
+
{ toolName, input: rawInput, output: outputPayload, error: message },
|
|
4000
4178
|
outputPayload
|
|
4001
|
-
|
|
4179
|
+
);
|
|
4002
4180
|
}
|
|
4003
4181
|
try {
|
|
4004
4182
|
const output = await tool2.execute(parsed.data);
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
outputPayload: output
|
|
4008
|
-
};
|
|
4183
|
+
const metrics = toolName === "spawn_agent" ? extractSpawnStartupMetrics(output) : void 0;
|
|
4184
|
+
return finalize({ toolName, input: parsed.data, output }, output, metrics);
|
|
4009
4185
|
} catch (error) {
|
|
4010
4186
|
const message = error instanceof Error ? error.message : String(error);
|
|
4011
4187
|
const outputPayload = buildToolErrorOutput(`Tool ${toolName} failed: ${message}`);
|
|
4012
|
-
return
|
|
4013
|
-
|
|
4188
|
+
return finalize(
|
|
4189
|
+
{ toolName, input: parsed.data, output: outputPayload, error: message },
|
|
4014
4190
|
outputPayload
|
|
4015
|
-
|
|
4191
|
+
);
|
|
4016
4192
|
}
|
|
4017
4193
|
}
|
|
4018
4194
|
function buildToolLogId(turn, toolIndex) {
|
|
@@ -4735,6 +4911,102 @@ var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
|
4735
4911
|
function resolveToolLoopContents(input) {
|
|
4736
4912
|
return resolveTextContents(input);
|
|
4737
4913
|
}
|
|
4914
|
+
var toolLoopSteeringInternals = /* @__PURE__ */ new WeakMap();
|
|
4915
|
+
function createToolLoopSteeringChannel() {
|
|
4916
|
+
const pending = [];
|
|
4917
|
+
let closed = false;
|
|
4918
|
+
const channel = {
|
|
4919
|
+
append: (input) => {
|
|
4920
|
+
if (closed) {
|
|
4921
|
+
return { accepted: false, queuedCount: pending.length };
|
|
4922
|
+
}
|
|
4923
|
+
const normalized = normalizeToolLoopSteeringInput(input);
|
|
4924
|
+
if (normalized.length === 0) {
|
|
4925
|
+
return { accepted: false, queuedCount: pending.length };
|
|
4926
|
+
}
|
|
4927
|
+
pending.push(...normalized);
|
|
4928
|
+
return { accepted: true, queuedCount: pending.length };
|
|
4929
|
+
},
|
|
4930
|
+
steer: (input) => channel.append(input),
|
|
4931
|
+
pendingCount: () => pending.length,
|
|
4932
|
+
close: () => {
|
|
4933
|
+
if (closed) {
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
closed = true;
|
|
4937
|
+
pending.length = 0;
|
|
4938
|
+
}
|
|
4939
|
+
};
|
|
4940
|
+
const internalState = {
|
|
4941
|
+
drainPendingContents: () => {
|
|
4942
|
+
if (pending.length === 0) {
|
|
4943
|
+
return [];
|
|
4944
|
+
}
|
|
4945
|
+
return pending.splice(0, pending.length);
|
|
4946
|
+
},
|
|
4947
|
+
close: channel.close
|
|
4948
|
+
};
|
|
4949
|
+
toolLoopSteeringInternals.set(channel, internalState);
|
|
4950
|
+
return channel;
|
|
4951
|
+
}
|
|
4952
|
+
function resolveToolLoopSteeringInternal(steering) {
|
|
4953
|
+
if (!steering) {
|
|
4954
|
+
return void 0;
|
|
4955
|
+
}
|
|
4956
|
+
const internal = toolLoopSteeringInternals.get(steering);
|
|
4957
|
+
if (!internal) {
|
|
4958
|
+
throw new Error(
|
|
4959
|
+
"Invalid tool loop steering channel. Use createToolLoopSteeringChannel() to construct one."
|
|
4960
|
+
);
|
|
4961
|
+
}
|
|
4962
|
+
return internal;
|
|
4963
|
+
}
|
|
4964
|
+
function normalizeToolLoopSteeringInput(input) {
|
|
4965
|
+
const messages = typeof input === "string" ? [{ role: "user", content: input }] : Array.isArray(input) ? input : [input];
|
|
4966
|
+
const normalized = [];
|
|
4967
|
+
for (const message of messages) {
|
|
4968
|
+
const role = message.role ?? "user";
|
|
4969
|
+
if (role !== "user") {
|
|
4970
|
+
throw new Error("Tool loop steering only accepts role='user' messages.");
|
|
4971
|
+
}
|
|
4972
|
+
if (typeof message.content === "string") {
|
|
4973
|
+
if (message.content.length === 0) {
|
|
4974
|
+
continue;
|
|
4975
|
+
}
|
|
4976
|
+
normalized.push({
|
|
4977
|
+
role: "user",
|
|
4978
|
+
parts: [{ type: "text", text: message.content }]
|
|
4979
|
+
});
|
|
4980
|
+
continue;
|
|
4981
|
+
}
|
|
4982
|
+
if (!Array.isArray(message.content) || message.content.length === 0) {
|
|
4983
|
+
continue;
|
|
4984
|
+
}
|
|
4985
|
+
const parts = [];
|
|
4986
|
+
for (const part of message.content) {
|
|
4987
|
+
if (part.type === "text") {
|
|
4988
|
+
parts.push({ type: "text", text: part.text });
|
|
4989
|
+
} else {
|
|
4990
|
+
parts.push({ type: "inlineData", data: part.data, mimeType: part.mimeType });
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
if (parts.length > 0) {
|
|
4994
|
+
normalized.push({ role: "user", parts });
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
return normalized;
|
|
4998
|
+
}
|
|
4999
|
+
function toChatGptAssistantMessage(text) {
|
|
5000
|
+
if (!text) {
|
|
5001
|
+
return void 0;
|
|
5002
|
+
}
|
|
5003
|
+
return {
|
|
5004
|
+
type: "message",
|
|
5005
|
+
role: "assistant",
|
|
5006
|
+
status: "completed",
|
|
5007
|
+
content: [{ type: "output_text", text }]
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
4738
5010
|
function isCustomTool(toolDef) {
|
|
4739
5011
|
return toolDef.type === "custom";
|
|
4740
5012
|
}
|
|
@@ -4853,411 +5125,908 @@ async function runToolLoop(request) {
|
|
|
4853
5125
|
}
|
|
4854
5126
|
const maxSteps = Math.max(1, Math.floor(request.maxSteps ?? DEFAULT_TOOL_LOOP_MAX_STEPS));
|
|
4855
5127
|
const providerInfo = resolveProvider(request.model);
|
|
5128
|
+
const steeringInternal = resolveToolLoopSteeringInternal(request.steering);
|
|
4856
5129
|
const steps = [];
|
|
4857
5130
|
let totalCostUsd = 0;
|
|
4858
5131
|
let finalText = "";
|
|
4859
5132
|
let finalThoughts = "";
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
const reasoning = {
|
|
4873
|
-
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
4874
|
-
summary: "detailed"
|
|
4875
|
-
};
|
|
4876
|
-
let previousResponseId;
|
|
4877
|
-
let input = toOpenAiInput(contents);
|
|
4878
|
-
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
4879
|
-
const turn = stepIndex + 1;
|
|
4880
|
-
const abortController = new AbortController();
|
|
4881
|
-
if (request.signal) {
|
|
4882
|
-
if (request.signal.aborted) {
|
|
4883
|
-
abortController.abort(request.signal.reason);
|
|
4884
|
-
} else {
|
|
4885
|
-
request.signal.addEventListener(
|
|
4886
|
-
"abort",
|
|
4887
|
-
() => abortController.abort(request.signal?.reason),
|
|
4888
|
-
{ once: true }
|
|
4889
|
-
);
|
|
4890
|
-
}
|
|
4891
|
-
}
|
|
4892
|
-
const onEvent = request.onEvent;
|
|
4893
|
-
let modelVersion = request.model;
|
|
4894
|
-
let usageTokens;
|
|
4895
|
-
const emitEvent = (ev) => {
|
|
4896
|
-
onEvent?.(ev);
|
|
5133
|
+
try {
|
|
5134
|
+
if (providerInfo.provider === "openai") {
|
|
5135
|
+
const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
|
|
5136
|
+
const openAiNativeTools = toOpenAiTools(request.modelTools);
|
|
5137
|
+
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
|
|
5138
|
+
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
5139
|
+
providerInfo.model,
|
|
5140
|
+
request.openAiReasoningEffort
|
|
5141
|
+
);
|
|
5142
|
+
const textConfig = {
|
|
5143
|
+
format: { type: "text" },
|
|
5144
|
+
verbosity: resolveOpenAiVerbosity(providerInfo.model)
|
|
4897
5145
|
};
|
|
4898
|
-
const
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
5146
|
+
const reasoning = {
|
|
5147
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5148
|
+
summary: "detailed"
|
|
5149
|
+
};
|
|
5150
|
+
let previousResponseId;
|
|
5151
|
+
let input = toOpenAiInput(contents);
|
|
5152
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5153
|
+
const turn = stepIndex + 1;
|
|
5154
|
+
const stepStartedAtMs = Date.now();
|
|
5155
|
+
let firstModelEventAtMs;
|
|
5156
|
+
let schedulerMetrics;
|
|
5157
|
+
const abortController = new AbortController();
|
|
5158
|
+
if (request.signal) {
|
|
5159
|
+
if (request.signal.aborted) {
|
|
5160
|
+
abortController.abort(request.signal.reason);
|
|
5161
|
+
} else {
|
|
5162
|
+
request.signal.addEventListener(
|
|
5163
|
+
"abort",
|
|
5164
|
+
() => abortController.abort(request.signal?.reason),
|
|
5165
|
+
{ once: true }
|
|
5166
|
+
);
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
const onEvent = request.onEvent;
|
|
5170
|
+
let modelVersion = request.model;
|
|
5171
|
+
let usageTokens;
|
|
5172
|
+
let thoughtDeltaEmitted = false;
|
|
5173
|
+
const emitEvent = (ev) => {
|
|
5174
|
+
onEvent?.(ev);
|
|
5175
|
+
};
|
|
5176
|
+
const markFirstModelEvent = () => {
|
|
5177
|
+
if (firstModelEventAtMs === void 0) {
|
|
5178
|
+
firstModelEventAtMs = Date.now();
|
|
5179
|
+
}
|
|
5180
|
+
};
|
|
5181
|
+
const finalResponse = await runOpenAiCall(
|
|
5182
|
+
async (client) => {
|
|
5183
|
+
const stream = client.responses.stream(
|
|
5184
|
+
{
|
|
5185
|
+
model: providerInfo.model,
|
|
5186
|
+
input,
|
|
5187
|
+
...previousResponseId ? { previous_response_id: previousResponseId } : {},
|
|
5188
|
+
...openAiTools.length > 0 ? { tools: openAiTools } : {},
|
|
5189
|
+
...openAiTools.length > 0 ? { parallel_tool_calls: true } : {},
|
|
5190
|
+
reasoning,
|
|
5191
|
+
text: textConfig,
|
|
5192
|
+
include: ["reasoning.encrypted_content"]
|
|
5193
|
+
},
|
|
5194
|
+
{ signal: abortController.signal }
|
|
5195
|
+
);
|
|
5196
|
+
for await (const event of stream) {
|
|
5197
|
+
markFirstModelEvent();
|
|
5198
|
+
switch (event.type) {
|
|
5199
|
+
case "response.output_text.delta":
|
|
5200
|
+
emitEvent({
|
|
5201
|
+
type: "delta",
|
|
5202
|
+
channel: "response",
|
|
5203
|
+
text: typeof event.delta === "string" ? event.delta : ""
|
|
5204
|
+
});
|
|
5205
|
+
break;
|
|
5206
|
+
case "response.reasoning_summary_text.delta":
|
|
5207
|
+
thoughtDeltaEmitted = true;
|
|
5208
|
+
emitEvent({
|
|
5209
|
+
type: "delta",
|
|
5210
|
+
channel: "thought",
|
|
5211
|
+
text: typeof event.delta === "string" ? event.delta : ""
|
|
5212
|
+
});
|
|
5213
|
+
break;
|
|
5214
|
+
case "response.refusal.delta":
|
|
5215
|
+
emitEvent({ type: "blocked" });
|
|
5216
|
+
break;
|
|
5217
|
+
default:
|
|
5218
|
+
break;
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
return await stream.finalResponse();
|
|
4909
5222
|
},
|
|
4910
|
-
|
|
5223
|
+
providerInfo.model,
|
|
5224
|
+
{
|
|
5225
|
+
onSettled: (metrics) => {
|
|
5226
|
+
schedulerMetrics = metrics;
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
4911
5229
|
);
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
5230
|
+
modelVersion = typeof finalResponse.model === "string" ? finalResponse.model : request.model;
|
|
5231
|
+
emitEvent({ type: "model", modelVersion });
|
|
5232
|
+
if (finalResponse.error) {
|
|
5233
|
+
const message = typeof finalResponse.error.message === "string" ? finalResponse.error.message : "OpenAI response failed";
|
|
5234
|
+
throw new Error(message);
|
|
5235
|
+
}
|
|
5236
|
+
usageTokens = extractOpenAiUsageTokens(finalResponse.usage);
|
|
5237
|
+
const responseText = extractOpenAiResponseParts(finalResponse).parts.filter((p) => p.type === "text" && p.thought !== true).map((p) => p.text).join("").trim();
|
|
5238
|
+
const reasoningSummary = extractOpenAiReasoningSummary(finalResponse).trim();
|
|
5239
|
+
if (!thoughtDeltaEmitted && reasoningSummary.length > 0) {
|
|
5240
|
+
emitEvent({ type: "delta", channel: "thought", text: reasoningSummary });
|
|
5241
|
+
}
|
|
5242
|
+
const modelCompletedAtMs = Date.now();
|
|
5243
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
5244
|
+
modelId: modelVersion,
|
|
5245
|
+
tokens: usageTokens,
|
|
5246
|
+
responseImages: 0
|
|
5247
|
+
});
|
|
5248
|
+
totalCostUsd += stepCostUsd;
|
|
5249
|
+
if (usageTokens) {
|
|
5250
|
+
emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
|
|
5251
|
+
}
|
|
5252
|
+
const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
|
|
5253
|
+
const stepToolCalls = [];
|
|
5254
|
+
if (responseToolCalls.length === 0) {
|
|
5255
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5256
|
+
const steeringItems2 = steeringInput2.length > 0 ? toOpenAiInput(steeringInput2) : [];
|
|
5257
|
+
finalText = responseText;
|
|
5258
|
+
finalThoughts = reasoningSummary;
|
|
5259
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5260
|
+
const timing2 = buildStepTiming({
|
|
5261
|
+
stepStartedAtMs,
|
|
5262
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5263
|
+
modelCompletedAtMs,
|
|
5264
|
+
firstModelEventAtMs,
|
|
5265
|
+
schedulerMetrics,
|
|
5266
|
+
toolExecutionMs: 0,
|
|
5267
|
+
waitToolMs: 0
|
|
5268
|
+
});
|
|
5269
|
+
steps.push({
|
|
5270
|
+
step: steps.length + 1,
|
|
5271
|
+
modelVersion,
|
|
5272
|
+
text: responseText || void 0,
|
|
5273
|
+
thoughts: reasoningSummary || void 0,
|
|
5274
|
+
toolCalls: [],
|
|
5275
|
+
usage: usageTokens,
|
|
5276
|
+
costUsd: stepCostUsd,
|
|
5277
|
+
timing: timing2
|
|
5278
|
+
});
|
|
5279
|
+
if (steeringItems2.length === 0) {
|
|
5280
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
4933
5281
|
}
|
|
5282
|
+
previousResponseId = finalResponse.id;
|
|
5283
|
+
input = steeringItems2;
|
|
5284
|
+
continue;
|
|
4934
5285
|
}
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
5286
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
5287
|
+
const toolIndex = index + 1;
|
|
5288
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
5289
|
+
const toolName = call.name;
|
|
5290
|
+
if (call.kind === "custom") {
|
|
5291
|
+
return {
|
|
5292
|
+
call,
|
|
5293
|
+
toolName,
|
|
5294
|
+
value: call.input,
|
|
5295
|
+
parseError: void 0,
|
|
5296
|
+
toolId,
|
|
5297
|
+
turn,
|
|
5298
|
+
toolIndex
|
|
5299
|
+
};
|
|
5300
|
+
}
|
|
5301
|
+
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
5302
|
+
return { call, toolName, value, parseError, toolId, turn, toolIndex };
|
|
5303
|
+
});
|
|
5304
|
+
for (const entry of callInputs) {
|
|
5305
|
+
emitEvent({
|
|
5306
|
+
type: "tool_call",
|
|
5307
|
+
phase: "started",
|
|
5308
|
+
turn: entry.turn,
|
|
5309
|
+
toolIndex: entry.toolIndex,
|
|
5310
|
+
toolName: entry.toolName,
|
|
5311
|
+
toolId: entry.toolId,
|
|
5312
|
+
callKind: entry.call.kind,
|
|
5313
|
+
callId: entry.call.call_id,
|
|
5314
|
+
input: entry.value
|
|
5315
|
+
});
|
|
5316
|
+
}
|
|
5317
|
+
const callResults = await Promise.all(
|
|
5318
|
+
callInputs.map(async (entry) => {
|
|
5319
|
+
return await toolCallContextStorage.run(
|
|
5320
|
+
{
|
|
5321
|
+
toolName: entry.toolName,
|
|
5322
|
+
toolId: entry.toolId,
|
|
5323
|
+
turn: entry.turn,
|
|
5324
|
+
toolIndex: entry.toolIndex
|
|
5325
|
+
},
|
|
5326
|
+
async () => {
|
|
5327
|
+
const { result, outputPayload } = await executeToolCall({
|
|
5328
|
+
callKind: entry.call.kind,
|
|
5329
|
+
toolName: entry.toolName,
|
|
5330
|
+
tool: request.tools[entry.toolName],
|
|
5331
|
+
rawInput: entry.value,
|
|
5332
|
+
parseError: entry.parseError
|
|
5333
|
+
});
|
|
5334
|
+
return { entry, result, outputPayload };
|
|
5335
|
+
}
|
|
5336
|
+
);
|
|
5337
|
+
})
|
|
5338
|
+
);
|
|
5339
|
+
const toolOutputs = [];
|
|
5340
|
+
let toolExecutionMs = 0;
|
|
5341
|
+
let waitToolMs = 0;
|
|
5342
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
5343
|
+
stepToolCalls.push({ ...result, callId: entry.call.call_id });
|
|
5344
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5345
|
+
toolExecutionMs += callDurationMs;
|
|
5346
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5347
|
+
waitToolMs += callDurationMs;
|
|
5348
|
+
}
|
|
5349
|
+
emitEvent({
|
|
5350
|
+
type: "tool_call",
|
|
5351
|
+
phase: "completed",
|
|
5352
|
+
turn: entry.turn,
|
|
5353
|
+
toolIndex: entry.toolIndex,
|
|
5354
|
+
toolName: entry.toolName,
|
|
5355
|
+
toolId: entry.toolId,
|
|
5356
|
+
callKind: entry.call.kind,
|
|
5357
|
+
callId: entry.call.call_id,
|
|
5358
|
+
input: entry.value,
|
|
5359
|
+
output: result.output,
|
|
5360
|
+
error: result.error,
|
|
5361
|
+
durationMs: result.durationMs
|
|
5362
|
+
});
|
|
5363
|
+
if (entry.call.kind === "custom") {
|
|
5364
|
+
toolOutputs.push({
|
|
5365
|
+
type: "custom_tool_call_output",
|
|
5366
|
+
call_id: entry.call.call_id,
|
|
5367
|
+
output: mergeToolOutput(outputPayload)
|
|
5368
|
+
});
|
|
5369
|
+
} else {
|
|
5370
|
+
toolOutputs.push({
|
|
5371
|
+
type: "function_call_output",
|
|
5372
|
+
call_id: entry.call.call_id,
|
|
5373
|
+
output: mergeToolOutput(outputPayload)
|
|
5374
|
+
});
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
const stepCompletedAtMs = Date.now();
|
|
5378
|
+
const timing = buildStepTiming({
|
|
5379
|
+
stepStartedAtMs,
|
|
5380
|
+
stepCompletedAtMs,
|
|
5381
|
+
modelCompletedAtMs,
|
|
5382
|
+
firstModelEventAtMs,
|
|
5383
|
+
schedulerMetrics,
|
|
5384
|
+
toolExecutionMs,
|
|
5385
|
+
waitToolMs
|
|
5386
|
+
});
|
|
4960
5387
|
steps.push({
|
|
4961
5388
|
step: steps.length + 1,
|
|
4962
5389
|
modelVersion,
|
|
4963
5390
|
text: responseText || void 0,
|
|
4964
5391
|
thoughts: reasoningSummary || void 0,
|
|
4965
|
-
toolCalls:
|
|
5392
|
+
toolCalls: stepToolCalls,
|
|
4966
5393
|
usage: usageTokens,
|
|
4967
|
-
costUsd: stepCostUsd
|
|
5394
|
+
costUsd: stepCostUsd,
|
|
5395
|
+
timing
|
|
4968
5396
|
});
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
5397
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
5398
|
+
const steeringItems = steeringInput.length > 0 ? toOpenAiInput(steeringInput) : [];
|
|
5399
|
+
previousResponseId = finalResponse.id;
|
|
5400
|
+
input = steeringItems.length > 0 ? toolOutputs.concat(steeringItems) : toolOutputs;
|
|
5401
|
+
}
|
|
5402
|
+
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5403
|
+
}
|
|
5404
|
+
if (providerInfo.provider === "chatgpt") {
|
|
5405
|
+
const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
|
|
5406
|
+
const openAiNativeTools = toOpenAiTools(request.modelTools);
|
|
5407
|
+
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
|
|
5408
|
+
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
5409
|
+
request.model,
|
|
5410
|
+
request.openAiReasoningEffort
|
|
5411
|
+
);
|
|
5412
|
+
const toolLoopInput = toChatGptInput(contents);
|
|
5413
|
+
const conversationId = `tool-loop-${(0, import_node_crypto.randomBytes)(8).toString("hex")}`;
|
|
5414
|
+
const promptCacheKey = conversationId;
|
|
5415
|
+
let input = [...toolLoopInput.input];
|
|
5416
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5417
|
+
const turn = stepIndex + 1;
|
|
5418
|
+
const stepStartedAtMs = Date.now();
|
|
5419
|
+
let firstModelEventAtMs;
|
|
5420
|
+
let thoughtDeltaEmitted = false;
|
|
5421
|
+
const markFirstModelEvent = () => {
|
|
5422
|
+
if (firstModelEventAtMs === void 0) {
|
|
5423
|
+
firstModelEventAtMs = Date.now();
|
|
5424
|
+
}
|
|
5425
|
+
};
|
|
5426
|
+
const response = await collectChatGptCodexResponseWithRetry({
|
|
5427
|
+
sessionId: conversationId,
|
|
5428
|
+
request: {
|
|
5429
|
+
model: providerInfo.model,
|
|
5430
|
+
store: false,
|
|
5431
|
+
stream: true,
|
|
5432
|
+
instructions: toolLoopInput.instructions ?? "You are a helpful assistant.",
|
|
5433
|
+
input,
|
|
5434
|
+
prompt_cache_key: promptCacheKey,
|
|
5435
|
+
include: ["reasoning.encrypted_content"],
|
|
5436
|
+
tools: openAiTools,
|
|
5437
|
+
tool_choice: "auto",
|
|
5438
|
+
parallel_tool_calls: true,
|
|
5439
|
+
reasoning: {
|
|
5440
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5441
|
+
summary: "detailed"
|
|
4997
5442
|
},
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
});
|
|
5006
|
-
return { entry, result, outputPayload };
|
|
5443
|
+
text: { verbosity: resolveOpenAiVerbosity(request.model) }
|
|
5444
|
+
},
|
|
5445
|
+
signal: request.signal,
|
|
5446
|
+
onDelta: (delta) => {
|
|
5447
|
+
if (delta.thoughtDelta) {
|
|
5448
|
+
markFirstModelEvent();
|
|
5449
|
+
thoughtDeltaEmitted = true;
|
|
5450
|
+
request.onEvent?.({ type: "delta", channel: "thought", text: delta.thoughtDelta });
|
|
5007
5451
|
}
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5452
|
+
if (delta.textDelta) {
|
|
5453
|
+
markFirstModelEvent();
|
|
5454
|
+
request.onEvent?.({ type: "delta", channel: "response", text: delta.textDelta });
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
});
|
|
5458
|
+
const modelCompletedAtMs = Date.now();
|
|
5459
|
+
const modelVersion = response.model ? `chatgpt-${response.model}` : request.model;
|
|
5460
|
+
const usageTokens = extractChatGptUsageTokens(response.usage);
|
|
5461
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
5462
|
+
modelId: modelVersion,
|
|
5463
|
+
tokens: usageTokens,
|
|
5464
|
+
responseImages: 0
|
|
5465
|
+
});
|
|
5466
|
+
totalCostUsd += stepCostUsd;
|
|
5467
|
+
const responseText = (response.text ?? "").trim();
|
|
5468
|
+
const reasoningSummaryText = (response.reasoningSummaryText ?? "").trim();
|
|
5469
|
+
if (!thoughtDeltaEmitted && reasoningSummaryText.length > 0) {
|
|
5470
|
+
request.onEvent?.({ type: "delta", channel: "thought", text: reasoningSummaryText });
|
|
5471
|
+
}
|
|
5472
|
+
const responseToolCalls = response.toolCalls ?? [];
|
|
5473
|
+
if (responseToolCalls.length === 0) {
|
|
5474
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5475
|
+
const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2).input : [];
|
|
5476
|
+
finalText = responseText;
|
|
5477
|
+
finalThoughts = reasoningSummaryText;
|
|
5478
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5479
|
+
const timing2 = buildStepTiming({
|
|
5480
|
+
stepStartedAtMs,
|
|
5481
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5482
|
+
modelCompletedAtMs,
|
|
5483
|
+
firstModelEventAtMs,
|
|
5484
|
+
toolExecutionMs: 0,
|
|
5485
|
+
waitToolMs: 0
|
|
5025
5486
|
});
|
|
5487
|
+
steps.push({
|
|
5488
|
+
step: steps.length + 1,
|
|
5489
|
+
modelVersion,
|
|
5490
|
+
text: responseText || void 0,
|
|
5491
|
+
thoughts: reasoningSummaryText || void 0,
|
|
5492
|
+
toolCalls: [],
|
|
5493
|
+
usage: usageTokens,
|
|
5494
|
+
costUsd: stepCostUsd,
|
|
5495
|
+
timing: timing2
|
|
5496
|
+
});
|
|
5497
|
+
if (steeringItems2.length === 0) {
|
|
5498
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5499
|
+
}
|
|
5500
|
+
const assistantItem = toChatGptAssistantMessage(responseText);
|
|
5501
|
+
input = assistantItem ? input.concat(assistantItem, steeringItems2) : input.concat(steeringItems2);
|
|
5502
|
+
continue;
|
|
5026
5503
|
}
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
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
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5504
|
+
const toolCalls = [];
|
|
5505
|
+
const toolOutputs = [];
|
|
5506
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
5507
|
+
const toolIndex = index + 1;
|
|
5508
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
5509
|
+
const toolName = call.name;
|
|
5510
|
+
const { value, error: parseError } = call.kind === "custom" ? { value: call.input, error: void 0 } : parseOpenAiToolArguments(call.arguments);
|
|
5511
|
+
const ids = normalizeChatGptToolIds({
|
|
5512
|
+
callKind: call.kind,
|
|
5513
|
+
callId: call.callId,
|
|
5514
|
+
itemId: call.id
|
|
5515
|
+
});
|
|
5516
|
+
return { call, toolName, value, parseError, ids, toolId, turn, toolIndex };
|
|
5517
|
+
});
|
|
5518
|
+
for (const entry of callInputs) {
|
|
5519
|
+
request.onEvent?.({
|
|
5520
|
+
type: "tool_call",
|
|
5521
|
+
phase: "started",
|
|
5522
|
+
turn: entry.turn,
|
|
5523
|
+
toolIndex: entry.toolIndex,
|
|
5524
|
+
toolName: entry.toolName,
|
|
5525
|
+
toolId: entry.toolId,
|
|
5526
|
+
callKind: entry.call.kind,
|
|
5527
|
+
callId: entry.ids.callId,
|
|
5528
|
+
input: entry.value
|
|
5529
|
+
});
|
|
5530
|
+
}
|
|
5531
|
+
const callResults = await Promise.all(
|
|
5532
|
+
callInputs.map(async (entry) => {
|
|
5533
|
+
return await toolCallContextStorage.run(
|
|
5534
|
+
{
|
|
5535
|
+
toolName: entry.toolName,
|
|
5536
|
+
toolId: entry.toolId,
|
|
5537
|
+
turn: entry.turn,
|
|
5538
|
+
toolIndex: entry.toolIndex
|
|
5539
|
+
},
|
|
5540
|
+
async () => {
|
|
5541
|
+
const { result, outputPayload } = await executeToolCall({
|
|
5542
|
+
callKind: entry.call.kind,
|
|
5543
|
+
toolName: entry.toolName,
|
|
5544
|
+
tool: request.tools[entry.toolName],
|
|
5545
|
+
rawInput: entry.value,
|
|
5546
|
+
parseError: entry.parseError
|
|
5547
|
+
});
|
|
5548
|
+
return { entry, result, outputPayload };
|
|
5549
|
+
}
|
|
5550
|
+
);
|
|
5551
|
+
})
|
|
5552
|
+
);
|
|
5553
|
+
let toolExecutionMs = 0;
|
|
5554
|
+
let waitToolMs = 0;
|
|
5555
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
5556
|
+
toolCalls.push({ ...result, callId: entry.ids.callId });
|
|
5557
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5558
|
+
toolExecutionMs += callDurationMs;
|
|
5559
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5560
|
+
waitToolMs += callDurationMs;
|
|
5079
5561
|
}
|
|
5080
|
-
|
|
5081
|
-
|
|
5562
|
+
request.onEvent?.({
|
|
5563
|
+
type: "tool_call",
|
|
5564
|
+
phase: "completed",
|
|
5565
|
+
turn: entry.turn,
|
|
5566
|
+
toolIndex: entry.toolIndex,
|
|
5567
|
+
toolName: entry.toolName,
|
|
5568
|
+
toolId: entry.toolId,
|
|
5569
|
+
callKind: entry.call.kind,
|
|
5570
|
+
callId: entry.ids.callId,
|
|
5571
|
+
input: entry.value,
|
|
5572
|
+
output: result.output,
|
|
5573
|
+
error: result.error,
|
|
5574
|
+
durationMs: result.durationMs
|
|
5575
|
+
});
|
|
5576
|
+
if (entry.call.kind === "custom") {
|
|
5577
|
+
toolOutputs.push({
|
|
5578
|
+
type: "custom_tool_call",
|
|
5579
|
+
id: entry.ids.itemId,
|
|
5580
|
+
call_id: entry.ids.callId,
|
|
5581
|
+
name: entry.toolName,
|
|
5582
|
+
input: entry.call.input,
|
|
5583
|
+
status: "completed"
|
|
5584
|
+
});
|
|
5585
|
+
toolOutputs.push({
|
|
5586
|
+
type: "custom_tool_call_output",
|
|
5587
|
+
call_id: entry.ids.callId,
|
|
5588
|
+
output: mergeToolOutput(outputPayload)
|
|
5589
|
+
});
|
|
5590
|
+
} else {
|
|
5591
|
+
toolOutputs.push({
|
|
5592
|
+
type: "function_call",
|
|
5593
|
+
id: entry.ids.itemId,
|
|
5594
|
+
call_id: entry.ids.callId,
|
|
5595
|
+
name: entry.toolName,
|
|
5596
|
+
arguments: entry.call.arguments,
|
|
5597
|
+
status: "completed"
|
|
5598
|
+
});
|
|
5599
|
+
toolOutputs.push({
|
|
5600
|
+
type: "function_call_output",
|
|
5601
|
+
call_id: entry.ids.callId,
|
|
5602
|
+
output: mergeToolOutput(outputPayload)
|
|
5603
|
+
});
|
|
5082
5604
|
}
|
|
5083
5605
|
}
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
const responseText = (response.text ?? "").trim();
|
|
5094
|
-
const reasoningSummaryText = (response.reasoningSummaryText ?? "").trim();
|
|
5095
|
-
const responseToolCalls = response.toolCalls ?? [];
|
|
5096
|
-
if (responseToolCalls.length === 0) {
|
|
5097
|
-
finalText = responseText;
|
|
5098
|
-
finalThoughts = reasoningSummaryText;
|
|
5606
|
+
const stepCompletedAtMs = Date.now();
|
|
5607
|
+
const timing = buildStepTiming({
|
|
5608
|
+
stepStartedAtMs,
|
|
5609
|
+
stepCompletedAtMs,
|
|
5610
|
+
modelCompletedAtMs,
|
|
5611
|
+
firstModelEventAtMs,
|
|
5612
|
+
toolExecutionMs,
|
|
5613
|
+
waitToolMs
|
|
5614
|
+
});
|
|
5099
5615
|
steps.push({
|
|
5100
5616
|
step: steps.length + 1,
|
|
5101
5617
|
modelVersion,
|
|
5102
5618
|
text: responseText || void 0,
|
|
5103
5619
|
thoughts: reasoningSummaryText || void 0,
|
|
5104
|
-
toolCalls
|
|
5620
|
+
toolCalls,
|
|
5105
5621
|
usage: usageTokens,
|
|
5106
|
-
costUsd: stepCostUsd
|
|
5622
|
+
costUsd: stepCostUsd,
|
|
5623
|
+
timing
|
|
5107
5624
|
});
|
|
5108
|
-
|
|
5625
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
5626
|
+
const steeringItems = steeringInput.length > 0 ? toChatGptInput(steeringInput).input : [];
|
|
5627
|
+
input = steeringItems.length > 0 ? input.concat(toolOutputs, steeringItems) : input.concat(toolOutputs);
|
|
5109
5628
|
}
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
return { entry, result, outputPayload };
|
|
5629
|
+
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5630
|
+
}
|
|
5631
|
+
if (providerInfo.provider === "fireworks") {
|
|
5632
|
+
if (request.modelTools && request.modelTools.length > 0) {
|
|
5633
|
+
throw new Error(
|
|
5634
|
+
"Fireworks provider does not support provider-native modelTools in runToolLoop."
|
|
5635
|
+
);
|
|
5636
|
+
}
|
|
5637
|
+
const fireworksTools = buildFireworksToolsFromToolSet(request.tools);
|
|
5638
|
+
const messages = toFireworksMessages(contents);
|
|
5639
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5640
|
+
const turn = stepIndex + 1;
|
|
5641
|
+
const stepStartedAtMs = Date.now();
|
|
5642
|
+
let schedulerMetrics;
|
|
5643
|
+
const response = await runFireworksCall(
|
|
5644
|
+
async (client) => {
|
|
5645
|
+
return await client.chat.completions.create(
|
|
5646
|
+
{
|
|
5647
|
+
model: providerInfo.model,
|
|
5648
|
+
messages,
|
|
5649
|
+
tools: fireworksTools,
|
|
5650
|
+
tool_choice: "auto",
|
|
5651
|
+
parallel_tool_calls: true
|
|
5652
|
+
},
|
|
5653
|
+
{ signal: request.signal }
|
|
5654
|
+
);
|
|
5655
|
+
},
|
|
5656
|
+
providerInfo.model,
|
|
5657
|
+
{
|
|
5658
|
+
onSettled: (metrics) => {
|
|
5659
|
+
schedulerMetrics = metrics;
|
|
5142
5660
|
}
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5661
|
+
}
|
|
5662
|
+
);
|
|
5663
|
+
const modelCompletedAtMs = Date.now();
|
|
5664
|
+
const modelVersion = typeof response.model === "string" ? response.model : request.model;
|
|
5665
|
+
request.onEvent?.({ type: "model", modelVersion });
|
|
5666
|
+
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
5667
|
+
if (choice?.finish_reason === "content_filter") {
|
|
5668
|
+
request.onEvent?.({ type: "blocked" });
|
|
5669
|
+
}
|
|
5670
|
+
const message = choice?.message;
|
|
5671
|
+
const responseText = extractFireworksMessageText(message).trim();
|
|
5672
|
+
if (responseText.length > 0) {
|
|
5673
|
+
request.onEvent?.({ type: "delta", channel: "response", text: responseText });
|
|
5674
|
+
}
|
|
5675
|
+
const usageTokens = extractFireworksUsageTokens(response.usage);
|
|
5676
|
+
const stepCostUsd = estimateCallCostUsd({
|
|
5677
|
+
modelId: modelVersion,
|
|
5678
|
+
tokens: usageTokens,
|
|
5679
|
+
responseImages: 0
|
|
5680
|
+
});
|
|
5681
|
+
totalCostUsd += stepCostUsd;
|
|
5682
|
+
if (usageTokens) {
|
|
5683
|
+
request.onEvent?.({
|
|
5684
|
+
type: "usage",
|
|
5685
|
+
usage: usageTokens,
|
|
5686
|
+
costUsd: stepCostUsd,
|
|
5687
|
+
modelVersion
|
|
5688
|
+
});
|
|
5689
|
+
}
|
|
5690
|
+
const responseToolCalls = extractFireworksToolCalls(message);
|
|
5691
|
+
if (responseToolCalls.length === 0) {
|
|
5692
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5693
|
+
const steeringMessages = steeringInput2.length > 0 ? toFireworksMessages(steeringInput2) : [];
|
|
5694
|
+
finalText = responseText;
|
|
5695
|
+
finalThoughts = "";
|
|
5696
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5697
|
+
const timing2 = buildStepTiming({
|
|
5698
|
+
stepStartedAtMs,
|
|
5699
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5700
|
+
modelCompletedAtMs,
|
|
5701
|
+
schedulerMetrics,
|
|
5702
|
+
toolExecutionMs: 0,
|
|
5703
|
+
waitToolMs: 0
|
|
5156
5704
|
});
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5705
|
+
steps.push({
|
|
5706
|
+
step: steps.length + 1,
|
|
5707
|
+
modelVersion,
|
|
5708
|
+
text: responseText || void 0,
|
|
5709
|
+
thoughts: void 0,
|
|
5710
|
+
toolCalls: [],
|
|
5711
|
+
usage: usageTokens,
|
|
5712
|
+
costUsd: stepCostUsd,
|
|
5713
|
+
timing: timing2
|
|
5161
5714
|
});
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5715
|
+
if (steeringMessages.length === 0) {
|
|
5716
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5717
|
+
}
|
|
5718
|
+
if (responseText.length > 0) {
|
|
5719
|
+
messages.push({ role: "assistant", content: responseText });
|
|
5720
|
+
}
|
|
5721
|
+
messages.push(...steeringMessages);
|
|
5722
|
+
continue;
|
|
5723
|
+
}
|
|
5724
|
+
const stepToolCalls = [];
|
|
5725
|
+
const callInputs = responseToolCalls.map((call, index) => {
|
|
5726
|
+
const toolIndex = index + 1;
|
|
5727
|
+
const toolId = buildToolLogId(turn, toolIndex);
|
|
5728
|
+
const { value, error: parseError } = parseOpenAiToolArguments(call.arguments);
|
|
5729
|
+
return { call, toolName: call.name, value, parseError, toolId, turn, toolIndex };
|
|
5730
|
+
});
|
|
5731
|
+
for (const entry of callInputs) {
|
|
5732
|
+
request.onEvent?.({
|
|
5733
|
+
type: "tool_call",
|
|
5734
|
+
phase: "started",
|
|
5735
|
+
turn: entry.turn,
|
|
5736
|
+
toolIndex: entry.toolIndex,
|
|
5737
|
+
toolName: entry.toolName,
|
|
5738
|
+
toolId: entry.toolId,
|
|
5739
|
+
callKind: "function",
|
|
5740
|
+
callId: entry.call.id,
|
|
5741
|
+
input: entry.value
|
|
5170
5742
|
});
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5743
|
+
}
|
|
5744
|
+
const callResults = await Promise.all(
|
|
5745
|
+
callInputs.map(async (entry) => {
|
|
5746
|
+
return await toolCallContextStorage.run(
|
|
5747
|
+
{
|
|
5748
|
+
toolName: entry.toolName,
|
|
5749
|
+
toolId: entry.toolId,
|
|
5750
|
+
turn: entry.turn,
|
|
5751
|
+
toolIndex: entry.toolIndex
|
|
5752
|
+
},
|
|
5753
|
+
async () => {
|
|
5754
|
+
const { result, outputPayload } = await executeToolCall({
|
|
5755
|
+
callKind: "function",
|
|
5756
|
+
toolName: entry.toolName,
|
|
5757
|
+
tool: request.tools[entry.toolName],
|
|
5758
|
+
rawInput: entry.value,
|
|
5759
|
+
parseError: entry.parseError
|
|
5760
|
+
});
|
|
5761
|
+
return { entry, result, outputPayload };
|
|
5762
|
+
}
|
|
5763
|
+
);
|
|
5764
|
+
})
|
|
5765
|
+
);
|
|
5766
|
+
const assistantToolCalls = [];
|
|
5767
|
+
const toolMessages = [];
|
|
5768
|
+
let toolExecutionMs = 0;
|
|
5769
|
+
let waitToolMs = 0;
|
|
5770
|
+
for (const { entry, result, outputPayload } of callResults) {
|
|
5771
|
+
stepToolCalls.push({ ...result, callId: entry.call.id });
|
|
5772
|
+
const callDurationMs = toToolResultDuration(result);
|
|
5773
|
+
toolExecutionMs += callDurationMs;
|
|
5774
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5775
|
+
waitToolMs += callDurationMs;
|
|
5776
|
+
}
|
|
5777
|
+
request.onEvent?.({
|
|
5778
|
+
type: "tool_call",
|
|
5779
|
+
phase: "completed",
|
|
5780
|
+
turn: entry.turn,
|
|
5781
|
+
toolIndex: entry.toolIndex,
|
|
5782
|
+
toolName: entry.toolName,
|
|
5783
|
+
toolId: entry.toolId,
|
|
5784
|
+
callKind: "function",
|
|
5785
|
+
callId: entry.call.id,
|
|
5786
|
+
input: entry.value,
|
|
5787
|
+
output: result.output,
|
|
5788
|
+
error: result.error,
|
|
5789
|
+
durationMs: result.durationMs
|
|
5790
|
+
});
|
|
5791
|
+
assistantToolCalls.push({
|
|
5792
|
+
id: entry.call.id,
|
|
5793
|
+
type: "function",
|
|
5794
|
+
function: {
|
|
5795
|
+
name: entry.toolName,
|
|
5796
|
+
arguments: entry.call.arguments
|
|
5797
|
+
}
|
|
5175
5798
|
});
|
|
5799
|
+
toolMessages.push({
|
|
5800
|
+
role: "tool",
|
|
5801
|
+
tool_call_id: entry.call.id,
|
|
5802
|
+
content: mergeToolOutput(outputPayload)
|
|
5803
|
+
});
|
|
5804
|
+
}
|
|
5805
|
+
const stepCompletedAtMs = Date.now();
|
|
5806
|
+
const timing = buildStepTiming({
|
|
5807
|
+
stepStartedAtMs,
|
|
5808
|
+
stepCompletedAtMs,
|
|
5809
|
+
modelCompletedAtMs,
|
|
5810
|
+
schedulerMetrics,
|
|
5811
|
+
toolExecutionMs,
|
|
5812
|
+
waitToolMs
|
|
5813
|
+
});
|
|
5814
|
+
steps.push({
|
|
5815
|
+
step: steps.length + 1,
|
|
5816
|
+
modelVersion,
|
|
5817
|
+
text: responseText || void 0,
|
|
5818
|
+
thoughts: void 0,
|
|
5819
|
+
toolCalls: stepToolCalls,
|
|
5820
|
+
usage: usageTokens,
|
|
5821
|
+
costUsd: stepCostUsd,
|
|
5822
|
+
timing
|
|
5823
|
+
});
|
|
5824
|
+
messages.push({
|
|
5825
|
+
role: "assistant",
|
|
5826
|
+
...responseText.length > 0 ? { content: responseText } : {},
|
|
5827
|
+
tool_calls: assistantToolCalls
|
|
5828
|
+
});
|
|
5829
|
+
messages.push(...toolMessages);
|
|
5830
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
5831
|
+
if (steeringInput.length > 0) {
|
|
5832
|
+
messages.push(...toFireworksMessages(steeringInput));
|
|
5176
5833
|
}
|
|
5177
5834
|
}
|
|
5178
|
-
steps
|
|
5179
|
-
step: steps.length + 1,
|
|
5180
|
-
modelVersion,
|
|
5181
|
-
text: responseText || void 0,
|
|
5182
|
-
thoughts: reasoningSummaryText || void 0,
|
|
5183
|
-
toolCalls,
|
|
5184
|
-
usage: usageTokens,
|
|
5185
|
-
costUsd: stepCostUsd
|
|
5186
|
-
});
|
|
5187
|
-
input = input.concat(toolOutputs);
|
|
5835
|
+
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5188
5836
|
}
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
throw new Error(
|
|
5194
|
-
"Fireworks provider does not support provider-native modelTools in runToolLoop."
|
|
5195
|
-
);
|
|
5196
|
-
}
|
|
5197
|
-
const fireworksTools = buildFireworksToolsFromToolSet(request.tools);
|
|
5198
|
-
const messages = toFireworksMessages(contents);
|
|
5837
|
+
const geminiFunctionTools = buildGeminiFunctionDeclarations(request.tools);
|
|
5838
|
+
const geminiNativeTools = toGeminiTools(request.modelTools);
|
|
5839
|
+
const geminiTools = geminiNativeTools ? geminiNativeTools.concat(geminiFunctionTools) : geminiFunctionTools;
|
|
5840
|
+
const geminiContents = contents.map(convertLlmContentToGeminiContent);
|
|
5199
5841
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5200
|
-
const
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
const
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5842
|
+
const stepStartedAtMs = Date.now();
|
|
5843
|
+
let firstModelEventAtMs;
|
|
5844
|
+
let schedulerMetrics;
|
|
5845
|
+
const markFirstModelEvent = () => {
|
|
5846
|
+
if (firstModelEventAtMs === void 0) {
|
|
5847
|
+
firstModelEventAtMs = Date.now();
|
|
5848
|
+
}
|
|
5849
|
+
};
|
|
5850
|
+
const config = {
|
|
5851
|
+
maxOutputTokens: 32e3,
|
|
5852
|
+
tools: geminiTools,
|
|
5853
|
+
toolConfig: {
|
|
5854
|
+
functionCallingConfig: {
|
|
5855
|
+
mode: import_genai2.FunctionCallingConfigMode.VALIDATED
|
|
5856
|
+
}
|
|
5857
|
+
},
|
|
5858
|
+
thinkingConfig: resolveGeminiThinkingConfig(request.model)
|
|
5859
|
+
};
|
|
5860
|
+
const onEvent = request.onEvent;
|
|
5861
|
+
const response = await runGeminiCall(
|
|
5862
|
+
async (client) => {
|
|
5863
|
+
const stream = await client.models.generateContentStream({
|
|
5864
|
+
model: request.model,
|
|
5865
|
+
contents: geminiContents,
|
|
5866
|
+
config
|
|
5867
|
+
});
|
|
5868
|
+
let responseText = "";
|
|
5869
|
+
let thoughtsText = "";
|
|
5870
|
+
const modelParts = [];
|
|
5871
|
+
const functionCalls = [];
|
|
5872
|
+
const seenFunctionCallIds = /* @__PURE__ */ new Set();
|
|
5873
|
+
const seenFunctionCallKeys = /* @__PURE__ */ new Set();
|
|
5874
|
+
let latestUsageMetadata;
|
|
5875
|
+
let resolvedModelVersion;
|
|
5876
|
+
for await (const chunk of stream) {
|
|
5877
|
+
markFirstModelEvent();
|
|
5878
|
+
if (chunk.modelVersion) {
|
|
5879
|
+
resolvedModelVersion = chunk.modelVersion;
|
|
5880
|
+
onEvent?.({ type: "model", modelVersion: chunk.modelVersion });
|
|
5881
|
+
}
|
|
5882
|
+
if (chunk.usageMetadata) {
|
|
5883
|
+
latestUsageMetadata = chunk.usageMetadata;
|
|
5884
|
+
}
|
|
5885
|
+
const candidates = chunk.candidates;
|
|
5886
|
+
if (!candidates || candidates.length === 0) {
|
|
5887
|
+
continue;
|
|
5888
|
+
}
|
|
5889
|
+
const primary = candidates[0];
|
|
5890
|
+
const parts = primary?.content?.parts;
|
|
5891
|
+
if (!parts || parts.length === 0) {
|
|
5892
|
+
continue;
|
|
5893
|
+
}
|
|
5894
|
+
for (const part of parts) {
|
|
5895
|
+
modelParts.push(part);
|
|
5896
|
+
const call = part.functionCall;
|
|
5897
|
+
if (call) {
|
|
5898
|
+
const id = typeof call.id === "string" ? call.id : "";
|
|
5899
|
+
const shouldAdd = (() => {
|
|
5900
|
+
if (id.length > 0) {
|
|
5901
|
+
if (seenFunctionCallIds.has(id)) {
|
|
5902
|
+
return false;
|
|
5903
|
+
}
|
|
5904
|
+
seenFunctionCallIds.add(id);
|
|
5905
|
+
return true;
|
|
5906
|
+
}
|
|
5907
|
+
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
5908
|
+
if (seenFunctionCallKeys.has(key)) {
|
|
5909
|
+
return false;
|
|
5910
|
+
}
|
|
5911
|
+
seenFunctionCallKeys.add(key);
|
|
5912
|
+
return true;
|
|
5913
|
+
})();
|
|
5914
|
+
if (shouldAdd) {
|
|
5915
|
+
functionCalls.push(call);
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
if (typeof part.text === "string" && part.text.length > 0) {
|
|
5919
|
+
if (part.thought) {
|
|
5920
|
+
thoughtsText += part.text;
|
|
5921
|
+
onEvent?.({ type: "delta", channel: "thought", text: part.text });
|
|
5922
|
+
} else {
|
|
5923
|
+
responseText += part.text;
|
|
5924
|
+
onEvent?.({ type: "delta", channel: "response", text: part.text });
|
|
5925
|
+
}
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
return {
|
|
5930
|
+
responseText,
|
|
5931
|
+
thoughtsText,
|
|
5932
|
+
functionCalls,
|
|
5933
|
+
modelParts,
|
|
5934
|
+
usageMetadata: latestUsageMetadata,
|
|
5935
|
+
modelVersion: resolvedModelVersion ?? request.model
|
|
5936
|
+
};
|
|
5937
|
+
},
|
|
5938
|
+
request.model,
|
|
5939
|
+
{
|
|
5940
|
+
onSettled: (metrics) => {
|
|
5941
|
+
schedulerMetrics = metrics;
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
);
|
|
5945
|
+
const modelCompletedAtMs = Date.now();
|
|
5946
|
+
const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
5947
|
+
const modelVersion = response.modelVersion ?? request.model;
|
|
5225
5948
|
const stepCostUsd = estimateCallCostUsd({
|
|
5226
5949
|
modelId: modelVersion,
|
|
5227
5950
|
tokens: usageTokens,
|
|
5228
5951
|
responseImages: 0
|
|
5229
5952
|
});
|
|
5230
5953
|
totalCostUsd += stepCostUsd;
|
|
5231
|
-
if (
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5954
|
+
if (response.functionCalls.length === 0) {
|
|
5955
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5956
|
+
finalText = response.responseText.trim();
|
|
5957
|
+
finalThoughts = response.thoughtsText.trim();
|
|
5958
|
+
const stepCompletedAtMs2 = Date.now();
|
|
5959
|
+
const timing2 = buildStepTiming({
|
|
5960
|
+
stepStartedAtMs,
|
|
5961
|
+
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5962
|
+
modelCompletedAtMs,
|
|
5963
|
+
firstModelEventAtMs,
|
|
5964
|
+
schedulerMetrics,
|
|
5965
|
+
toolExecutionMs: 0,
|
|
5966
|
+
waitToolMs: 0
|
|
5237
5967
|
});
|
|
5238
|
-
}
|
|
5239
|
-
const responseToolCalls = extractFireworksToolCalls(message);
|
|
5240
|
-
if (responseToolCalls.length === 0) {
|
|
5241
|
-
finalText = responseText;
|
|
5242
|
-
finalThoughts = "";
|
|
5243
5968
|
steps.push({
|
|
5244
5969
|
step: steps.length + 1,
|
|
5245
5970
|
modelVersion,
|
|
5246
|
-
text:
|
|
5247
|
-
thoughts: void 0,
|
|
5971
|
+
text: finalText || void 0,
|
|
5972
|
+
thoughts: finalThoughts || void 0,
|
|
5248
5973
|
toolCalls: [],
|
|
5249
5974
|
usage: usageTokens,
|
|
5250
|
-
costUsd: stepCostUsd
|
|
5975
|
+
costUsd: stepCostUsd,
|
|
5976
|
+
timing: timing2
|
|
5251
5977
|
});
|
|
5252
|
-
|
|
5978
|
+
if (steeringInput2.length === 0) {
|
|
5979
|
+
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
5980
|
+
}
|
|
5981
|
+
const modelPartsForHistory2 = response.modelParts.filter(
|
|
5982
|
+
(part) => !(typeof part.text === "string" && part.thought === true)
|
|
5983
|
+
);
|
|
5984
|
+
if (modelPartsForHistory2.length > 0) {
|
|
5985
|
+
geminiContents.push({ role: "model", parts: modelPartsForHistory2 });
|
|
5986
|
+
} else if (response.responseText.length > 0) {
|
|
5987
|
+
geminiContents.push({ role: "model", parts: [{ text: response.responseText }] });
|
|
5988
|
+
}
|
|
5989
|
+
geminiContents.push(...steeringInput2.map(convertLlmContentToGeminiContent));
|
|
5990
|
+
continue;
|
|
5253
5991
|
}
|
|
5254
|
-
const
|
|
5255
|
-
const
|
|
5992
|
+
const toolCalls = [];
|
|
5993
|
+
const modelPartsForHistory = response.modelParts.filter(
|
|
5994
|
+
(part) => !(typeof part.text === "string" && part.thought === true)
|
|
5995
|
+
);
|
|
5996
|
+
if (modelPartsForHistory.length > 0) {
|
|
5997
|
+
geminiContents.push({ role: "model", parts: modelPartsForHistory });
|
|
5998
|
+
} else {
|
|
5999
|
+
const parts = [];
|
|
6000
|
+
if (response.responseText) {
|
|
6001
|
+
parts.push({ text: response.responseText });
|
|
6002
|
+
}
|
|
6003
|
+
for (const call of response.functionCalls) {
|
|
6004
|
+
parts.push({ functionCall: call });
|
|
6005
|
+
}
|
|
6006
|
+
geminiContents.push({ role: "model", parts });
|
|
6007
|
+
}
|
|
6008
|
+
const responseParts = [];
|
|
6009
|
+
const callInputs = response.functionCalls.map((call, index) => {
|
|
6010
|
+
const turn = stepIndex + 1;
|
|
5256
6011
|
const toolIndex = index + 1;
|
|
5257
6012
|
const toolId = buildToolLogId(turn, toolIndex);
|
|
5258
|
-
const
|
|
5259
|
-
|
|
6013
|
+
const toolName = call.name ?? "unknown";
|
|
6014
|
+
const rawInput = call.args ?? {};
|
|
6015
|
+
return { call, toolName, rawInput, toolId, turn, toolIndex };
|
|
5260
6016
|
});
|
|
6017
|
+
for (const entry of callInputs) {
|
|
6018
|
+
onEvent?.({
|
|
6019
|
+
type: "tool_call",
|
|
6020
|
+
phase: "started",
|
|
6021
|
+
turn: entry.turn,
|
|
6022
|
+
toolIndex: entry.toolIndex,
|
|
6023
|
+
toolName: entry.toolName,
|
|
6024
|
+
toolId: entry.toolId,
|
|
6025
|
+
callKind: "function",
|
|
6026
|
+
callId: entry.call.id,
|
|
6027
|
+
input: entry.rawInput
|
|
6028
|
+
});
|
|
6029
|
+
}
|
|
5261
6030
|
const callResults = await Promise.all(
|
|
5262
6031
|
callInputs.map(async (entry) => {
|
|
5263
6032
|
return await toolCallContextStorage.run(
|
|
@@ -5272,232 +6041,134 @@ async function runToolLoop(request) {
|
|
|
5272
6041
|
callKind: "function",
|
|
5273
6042
|
toolName: entry.toolName,
|
|
5274
6043
|
tool: request.tools[entry.toolName],
|
|
5275
|
-
rawInput: entry.
|
|
5276
|
-
parseError: entry.parseError
|
|
6044
|
+
rawInput: entry.rawInput
|
|
5277
6045
|
});
|
|
5278
6046
|
return { entry, result, outputPayload };
|
|
5279
6047
|
}
|
|
5280
6048
|
);
|
|
5281
6049
|
})
|
|
5282
6050
|
);
|
|
5283
|
-
|
|
5284
|
-
|
|
6051
|
+
let toolExecutionMs = 0;
|
|
6052
|
+
let waitToolMs = 0;
|
|
5285
6053
|
for (const { entry, result, outputPayload } of callResults) {
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
6054
|
+
toolCalls.push({ ...result, callId: entry.call.id });
|
|
6055
|
+
const callDurationMs = toToolResultDuration(result);
|
|
6056
|
+
toolExecutionMs += callDurationMs;
|
|
6057
|
+
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
6058
|
+
waitToolMs += callDurationMs;
|
|
6059
|
+
}
|
|
6060
|
+
onEvent?.({
|
|
6061
|
+
type: "tool_call",
|
|
6062
|
+
phase: "completed",
|
|
6063
|
+
turn: entry.turn,
|
|
6064
|
+
toolIndex: entry.toolIndex,
|
|
6065
|
+
toolName: entry.toolName,
|
|
6066
|
+
toolId: entry.toolId,
|
|
6067
|
+
callKind: "function",
|
|
6068
|
+
callId: entry.call.id,
|
|
6069
|
+
input: entry.rawInput,
|
|
6070
|
+
output: result.output,
|
|
6071
|
+
error: result.error,
|
|
6072
|
+
durationMs: result.durationMs
|
|
6073
|
+
});
|
|
6074
|
+
const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
|
|
6075
|
+
responseParts.push({
|
|
6076
|
+
functionResponse: {
|
|
5291
6077
|
name: entry.toolName,
|
|
5292
|
-
|
|
6078
|
+
response: responsePayload,
|
|
6079
|
+
...entry.call.id ? { id: entry.call.id } : {}
|
|
5293
6080
|
}
|
|
5294
6081
|
});
|
|
5295
|
-
toolMessages.push({
|
|
5296
|
-
role: "tool",
|
|
5297
|
-
tool_call_id: entry.call.id,
|
|
5298
|
-
content: mergeToolOutput(outputPayload)
|
|
5299
|
-
});
|
|
5300
6082
|
}
|
|
6083
|
+
const stepCompletedAtMs = Date.now();
|
|
6084
|
+
const timing = buildStepTiming({
|
|
6085
|
+
stepStartedAtMs,
|
|
6086
|
+
stepCompletedAtMs,
|
|
6087
|
+
modelCompletedAtMs,
|
|
6088
|
+
firstModelEventAtMs,
|
|
6089
|
+
schedulerMetrics,
|
|
6090
|
+
toolExecutionMs,
|
|
6091
|
+
waitToolMs
|
|
6092
|
+
});
|
|
5301
6093
|
steps.push({
|
|
5302
6094
|
step: steps.length + 1,
|
|
5303
6095
|
modelVersion,
|
|
5304
|
-
text: responseText || void 0,
|
|
5305
|
-
thoughts: void 0,
|
|
5306
|
-
toolCalls
|
|
6096
|
+
text: response.responseText.trim() || void 0,
|
|
6097
|
+
thoughts: response.thoughtsText.trim() || void 0,
|
|
6098
|
+
toolCalls,
|
|
5307
6099
|
usage: usageTokens,
|
|
5308
|
-
costUsd: stepCostUsd
|
|
5309
|
-
|
|
5310
|
-
messages.push({
|
|
5311
|
-
role: "assistant",
|
|
5312
|
-
...responseText.length > 0 ? { content: responseText } : {},
|
|
5313
|
-
tool_calls: assistantToolCalls
|
|
6100
|
+
costUsd: stepCostUsd,
|
|
6101
|
+
timing
|
|
5314
6102
|
});
|
|
5315
|
-
|
|
6103
|
+
geminiContents.push({ role: "user", parts: responseParts });
|
|
6104
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6105
|
+
if (steeringInput.length > 0) {
|
|
6106
|
+
geminiContents.push(...steeringInput.map(convertLlmContentToGeminiContent));
|
|
6107
|
+
}
|
|
5316
6108
|
}
|
|
5317
6109
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
6110
|
+
} finally {
|
|
6111
|
+
steeringInternal?.close();
|
|
5318
6112
|
}
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
},
|
|
5332
|
-
thinkingConfig: resolveGeminiThinkingConfig(request.model)
|
|
5333
|
-
};
|
|
5334
|
-
const onEvent = request.onEvent;
|
|
5335
|
-
const response = await runGeminiCall(async (client) => {
|
|
5336
|
-
const stream = await client.models.generateContentStream({
|
|
5337
|
-
model: request.model,
|
|
5338
|
-
contents: geminiContents,
|
|
5339
|
-
config
|
|
5340
|
-
});
|
|
5341
|
-
let responseText = "";
|
|
5342
|
-
let thoughtsText = "";
|
|
5343
|
-
const modelParts = [];
|
|
5344
|
-
const functionCalls = [];
|
|
5345
|
-
const seenFunctionCallIds = /* @__PURE__ */ new Set();
|
|
5346
|
-
const seenFunctionCallKeys = /* @__PURE__ */ new Set();
|
|
5347
|
-
let latestUsageMetadata;
|
|
5348
|
-
let resolvedModelVersion;
|
|
5349
|
-
for await (const chunk of stream) {
|
|
5350
|
-
if (chunk.modelVersion) {
|
|
5351
|
-
resolvedModelVersion = chunk.modelVersion;
|
|
5352
|
-
onEvent?.({ type: "model", modelVersion: chunk.modelVersion });
|
|
5353
|
-
}
|
|
5354
|
-
if (chunk.usageMetadata) {
|
|
5355
|
-
latestUsageMetadata = chunk.usageMetadata;
|
|
5356
|
-
}
|
|
5357
|
-
const candidates = chunk.candidates;
|
|
5358
|
-
if (!candidates || candidates.length === 0) {
|
|
5359
|
-
continue;
|
|
5360
|
-
}
|
|
5361
|
-
const primary = candidates[0];
|
|
5362
|
-
const parts = primary?.content?.parts;
|
|
5363
|
-
if (!parts || parts.length === 0) {
|
|
5364
|
-
continue;
|
|
5365
|
-
}
|
|
5366
|
-
for (const part of parts) {
|
|
5367
|
-
modelParts.push(part);
|
|
5368
|
-
const call = part.functionCall;
|
|
5369
|
-
if (call) {
|
|
5370
|
-
const id = typeof call.id === "string" ? call.id : "";
|
|
5371
|
-
const shouldAdd = (() => {
|
|
5372
|
-
if (id.length > 0) {
|
|
5373
|
-
if (seenFunctionCallIds.has(id)) {
|
|
5374
|
-
return false;
|
|
5375
|
-
}
|
|
5376
|
-
seenFunctionCallIds.add(id);
|
|
5377
|
-
return true;
|
|
5378
|
-
}
|
|
5379
|
-
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
5380
|
-
if (seenFunctionCallKeys.has(key)) {
|
|
5381
|
-
return false;
|
|
5382
|
-
}
|
|
5383
|
-
seenFunctionCallKeys.add(key);
|
|
5384
|
-
return true;
|
|
5385
|
-
})();
|
|
5386
|
-
if (shouldAdd) {
|
|
5387
|
-
functionCalls.push(call);
|
|
5388
|
-
}
|
|
5389
|
-
}
|
|
5390
|
-
if (typeof part.text === "string" && part.text.length > 0) {
|
|
5391
|
-
if (part.thought) {
|
|
5392
|
-
thoughtsText += part.text;
|
|
5393
|
-
onEvent?.({ type: "delta", channel: "thought", text: part.text });
|
|
5394
|
-
} else {
|
|
5395
|
-
responseText += part.text;
|
|
5396
|
-
onEvent?.({ type: "delta", channel: "response", text: part.text });
|
|
5397
|
-
}
|
|
5398
|
-
}
|
|
5399
|
-
}
|
|
5400
|
-
}
|
|
5401
|
-
return {
|
|
5402
|
-
responseText,
|
|
5403
|
-
thoughtsText,
|
|
5404
|
-
functionCalls,
|
|
5405
|
-
modelParts,
|
|
5406
|
-
usageMetadata: latestUsageMetadata,
|
|
5407
|
-
modelVersion: resolvedModelVersion ?? request.model
|
|
5408
|
-
};
|
|
5409
|
-
}, request.model);
|
|
5410
|
-
const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
5411
|
-
const modelVersion = response.modelVersion ?? request.model;
|
|
5412
|
-
const stepCostUsd = estimateCallCostUsd({
|
|
5413
|
-
modelId: modelVersion,
|
|
5414
|
-
tokens: usageTokens,
|
|
5415
|
-
responseImages: 0
|
|
5416
|
-
});
|
|
5417
|
-
totalCostUsd += stepCostUsd;
|
|
5418
|
-
if (response.functionCalls.length === 0) {
|
|
5419
|
-
finalText = response.responseText.trim();
|
|
5420
|
-
finalThoughts = response.thoughtsText.trim();
|
|
5421
|
-
steps.push({
|
|
5422
|
-
step: steps.length + 1,
|
|
5423
|
-
modelVersion,
|
|
5424
|
-
text: finalText || void 0,
|
|
5425
|
-
thoughts: finalThoughts || void 0,
|
|
5426
|
-
toolCalls: [],
|
|
5427
|
-
usage: usageTokens,
|
|
5428
|
-
costUsd: stepCostUsd
|
|
5429
|
-
});
|
|
5430
|
-
return { text: finalText, thoughts: finalThoughts, steps, totalCostUsd };
|
|
6113
|
+
}
|
|
6114
|
+
function mergeAbortSignals(first, second) {
|
|
6115
|
+
if (!first) {
|
|
6116
|
+
return second;
|
|
6117
|
+
}
|
|
6118
|
+
if (!second) {
|
|
6119
|
+
return first;
|
|
6120
|
+
}
|
|
6121
|
+
const controller = new AbortController();
|
|
6122
|
+
const abortFrom = (signal) => {
|
|
6123
|
+
if (!controller.signal.aborted) {
|
|
6124
|
+
controller.abort(signal.reason);
|
|
5431
6125
|
}
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
const
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
{
|
|
5461
|
-
toolName: entry.toolName,
|
|
5462
|
-
toolId: entry.toolId,
|
|
5463
|
-
turn: entry.turn,
|
|
5464
|
-
toolIndex: entry.toolIndex
|
|
5465
|
-
},
|
|
5466
|
-
async () => {
|
|
5467
|
-
const { result, outputPayload } = await executeToolCall({
|
|
5468
|
-
callKind: "function",
|
|
5469
|
-
toolName: entry.toolName,
|
|
5470
|
-
tool: request.tools[entry.toolName],
|
|
5471
|
-
rawInput: entry.rawInput
|
|
5472
|
-
});
|
|
5473
|
-
return { entry, result, outputPayload };
|
|
5474
|
-
}
|
|
5475
|
-
);
|
|
5476
|
-
})
|
|
5477
|
-
);
|
|
5478
|
-
for (const { entry, result, outputPayload } of callResults) {
|
|
5479
|
-
toolCalls.push({ ...result, callId: entry.call.id });
|
|
5480
|
-
const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
|
|
5481
|
-
responseParts.push({
|
|
5482
|
-
functionResponse: {
|
|
5483
|
-
name: entry.toolName,
|
|
5484
|
-
response: responsePayload,
|
|
5485
|
-
...entry.call.id ? { id: entry.call.id } : {}
|
|
6126
|
+
};
|
|
6127
|
+
if (first.aborted) {
|
|
6128
|
+
abortFrom(first);
|
|
6129
|
+
} else {
|
|
6130
|
+
first.addEventListener("abort", () => abortFrom(first), { once: true });
|
|
6131
|
+
}
|
|
6132
|
+
if (second.aborted) {
|
|
6133
|
+
abortFrom(second);
|
|
6134
|
+
} else {
|
|
6135
|
+
second.addEventListener("abort", () => abortFrom(second), { once: true });
|
|
6136
|
+
}
|
|
6137
|
+
return controller.signal;
|
|
6138
|
+
}
|
|
6139
|
+
function streamToolLoop(request) {
|
|
6140
|
+
const queue = createAsyncQueue();
|
|
6141
|
+
const abortController = new AbortController();
|
|
6142
|
+
const steering = request.steering ?? createToolLoopSteeringChannel();
|
|
6143
|
+
const signal = mergeAbortSignals(request.signal, abortController.signal);
|
|
6144
|
+
const sourceOnEvent = request.onEvent;
|
|
6145
|
+
const result = (async () => {
|
|
6146
|
+
try {
|
|
6147
|
+
const output = await runToolLoop({
|
|
6148
|
+
...request,
|
|
6149
|
+
steering,
|
|
6150
|
+
...signal ? { signal } : {},
|
|
6151
|
+
onEvent: (event) => {
|
|
6152
|
+
sourceOnEvent?.(event);
|
|
6153
|
+
queue.push(event);
|
|
5486
6154
|
}
|
|
5487
6155
|
});
|
|
6156
|
+
queue.close();
|
|
6157
|
+
return output;
|
|
6158
|
+
} catch (error) {
|
|
6159
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
6160
|
+
queue.fail(err);
|
|
6161
|
+
throw err;
|
|
5488
6162
|
}
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
geminiContents.push({ role: "user", parts: responseParts });
|
|
5499
|
-
}
|
|
5500
|
-
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
6163
|
+
})();
|
|
6164
|
+
return {
|
|
6165
|
+
events: queue.iterable,
|
|
6166
|
+
result,
|
|
6167
|
+
append: steering.append,
|
|
6168
|
+
steer: steering.steer,
|
|
6169
|
+
pendingSteeringCount: steering.pendingCount,
|
|
6170
|
+
abort: () => abortController.abort()
|
|
6171
|
+
};
|
|
5501
6172
|
}
|
|
5502
6173
|
var IMAGE_GRADE_SCHEMA = import_zod3.z.enum(["pass", "fail"]);
|
|
5503
6174
|
async function gradeGeneratedImage(params) {
|
|
@@ -5744,68 +6415,112 @@ function appendMarkdownSourcesSection(value, sources) {
|
|
|
5744
6415
|
${lines}`;
|
|
5745
6416
|
}
|
|
5746
6417
|
|
|
6418
|
+
// src/agent.ts
|
|
6419
|
+
var import_node_crypto3 = require("crypto");
|
|
6420
|
+
|
|
5747
6421
|
// src/agent/subagents.ts
|
|
5748
6422
|
var import_node_crypto2 = require("crypto");
|
|
5749
6423
|
var import_zod4 = require("zod");
|
|
5750
|
-
var DEFAULT_SUBAGENT_MAX_AGENTS =
|
|
5751
|
-
var DEFAULT_SUBAGENT_MAX_DEPTH =
|
|
5752
|
-
var
|
|
5753
|
-
var
|
|
6424
|
+
var DEFAULT_SUBAGENT_MAX_AGENTS = 6;
|
|
6425
|
+
var DEFAULT_SUBAGENT_MAX_DEPTH = 1;
|
|
6426
|
+
var DEFAULT_SUBAGENT_MIN_WAIT_TIMEOUT_MS = 1e4;
|
|
6427
|
+
var DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS = 3e4;
|
|
6428
|
+
var DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS = 36e5;
|
|
5754
6429
|
var MAX_SUBAGENT_MAX_AGENTS = 64;
|
|
5755
6430
|
var MAX_SUBAGENT_MAX_DEPTH = 12;
|
|
5756
6431
|
var MAX_SUBAGENT_MAX_STEPS = 64;
|
|
5757
|
-
var MAX_SUBAGENT_WAIT_TIMEOUT_MS =
|
|
6432
|
+
var MAX_SUBAGENT_WAIT_TIMEOUT_MS = 36e5;
|
|
5758
6433
|
var SUBAGENT_CONTROL_TOOL_NAMES = ["send_input", "resume_agent", "wait", "close_agent"];
|
|
6434
|
+
var DEFAULT_AGENT_TYPE = "default";
|
|
6435
|
+
var BUILT_IN_AGENT_TYPES = ["default", "researcher", "worker", "reviewer"];
|
|
6436
|
+
var RESEARCHER_ROLE_DESCRIPTION = `Use \`researcher\` for focused discovery and fact-finding work.
|
|
6437
|
+
Researchers are fast and authoritative.
|
|
6438
|
+
They should be used for specific, well-scoped research questions.
|
|
6439
|
+
Rules:
|
|
6440
|
+
- Do not repeat searches they have already completed.
|
|
6441
|
+
- Trust researcher findings unless there is a clear contradiction.
|
|
6442
|
+
- Run researchers in parallel when useful.
|
|
6443
|
+
- Reuse existing researchers for related follow-up questions.`;
|
|
6444
|
+
var WORKER_ROLE_DESCRIPTION = `Use for execution and production work across domains.
|
|
6445
|
+
Typical tasks:
|
|
6446
|
+
- Build part of a deliverable
|
|
6447
|
+
- Implement requested changes
|
|
6448
|
+
- Produce concrete outputs (documents, plans, analyses, artifacts)
|
|
6449
|
+
Rules:
|
|
6450
|
+
- Explicitly assign **ownership** of the task (scope / responsibility).
|
|
6451
|
+
- Always tell workers they are **not alone in the workspace**, and they should ignore edits made by others without touching them unless asked.`;
|
|
6452
|
+
var REVIEWER_ROLE_DESCRIPTION = `Use \`reviewer\` to evaluate completed work and provide feedback.
|
|
6453
|
+
Reviewers focus on quality, correctness, risk, and clarity.
|
|
6454
|
+
Rules:
|
|
6455
|
+
- Review critically and prioritize issues by severity.
|
|
6456
|
+
- Call out gaps, assumptions, and edge cases explicitly.
|
|
6457
|
+
- Provide actionable, concrete feedback to improve the result.
|
|
6458
|
+
- Do not redo the entire task unless explicitly requested; evaluate first.`;
|
|
6459
|
+
var BUILT_IN_AGENT_TYPE_DESCRIPTIONS = {
|
|
6460
|
+
default: "Default agent.",
|
|
6461
|
+
researcher: RESEARCHER_ROLE_DESCRIPTION,
|
|
6462
|
+
worker: WORKER_ROLE_DESCRIPTION,
|
|
6463
|
+
reviewer: REVIEWER_ROLE_DESCRIPTION
|
|
6464
|
+
};
|
|
6465
|
+
var BUILT_IN_AGENT_TYPE_INSTRUCTIONS = {
|
|
6466
|
+
default: void 0,
|
|
6467
|
+
researcher: RESEARCHER_ROLE_DESCRIPTION,
|
|
6468
|
+
worker: WORKER_ROLE_DESCRIPTION,
|
|
6469
|
+
reviewer: REVIEWER_ROLE_DESCRIPTION
|
|
6470
|
+
};
|
|
6471
|
+
var SUBAGENT_NOTIFICATION_OPEN_TAG = "<subagent_notification>";
|
|
6472
|
+
var SUBAGENT_NOTIFICATION_CLOSE_TAG = "</subagent_notification>";
|
|
6473
|
+
var SPAWN_AGENT_TYPE_DESCRIPTION = buildSpawnAgentTypeDescription();
|
|
5759
6474
|
var subagentInputItemSchema = import_zod4.z.object({
|
|
5760
|
-
text: import_zod4.z.string().
|
|
5761
|
-
image_url: import_zod4.z.string().
|
|
5762
|
-
name: import_zod4.z.string().
|
|
5763
|
-
path: import_zod4.z.string().
|
|
5764
|
-
type: import_zod4.z.string().
|
|
6475
|
+
text: import_zod4.z.string().nullish(),
|
|
6476
|
+
image_url: import_zod4.z.string().nullish(),
|
|
6477
|
+
name: import_zod4.z.string().nullish(),
|
|
6478
|
+
path: import_zod4.z.string().nullish(),
|
|
6479
|
+
type: import_zod4.z.string().nullish()
|
|
5765
6480
|
}).passthrough();
|
|
5766
6481
|
var spawnAgentInputSchema = import_zod4.z.object({
|
|
5767
|
-
prompt: import_zod4.z.string().
|
|
5768
|
-
message: import_zod4.z.string().
|
|
5769
|
-
items: import_zod4.z.array(subagentInputItemSchema).
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
6482
|
+
prompt: import_zod4.z.string().nullish().describe("Alias for message. Initial plain-text task for the new agent."),
|
|
6483
|
+
message: import_zod4.z.string().nullish().describe("Initial plain-text task for the new agent. Use either message or items."),
|
|
6484
|
+
items: import_zod4.z.array(subagentInputItemSchema).nullish().describe(
|
|
6485
|
+
"Structured input items. Use this to pass explicit mentions (for example app:// connector paths)."
|
|
6486
|
+
),
|
|
6487
|
+
agent_type: import_zod4.z.string().nullish().describe(SPAWN_AGENT_TYPE_DESCRIPTION),
|
|
6488
|
+
fork_context: import_zod4.z.boolean().nullish().describe(
|
|
6489
|
+
"When true, fork the current thread history into the new agent before sending the initial prompt. This must be used when you want the new agent to have exactly the same context as you."
|
|
6490
|
+
),
|
|
6491
|
+
instructions: import_zod4.z.string().nullish().describe("Optional extra instructions for this subagent instance."),
|
|
6492
|
+
model: import_zod4.z.string().nullish().describe("Optional model override. Must be one of this package's supported text model ids."),
|
|
6493
|
+
max_steps: import_zod4.z.number().int().min(1).max(MAX_SUBAGENT_MAX_STEPS).nullish().describe("Optional max step budget for each subagent run.")
|
|
5776
6494
|
});
|
|
5777
6495
|
var sendInputSchema = import_zod4.z.object({
|
|
5778
|
-
agent_id: import_zod4.z.string().
|
|
5779
|
-
id: import_zod4.z.string().
|
|
5780
|
-
input: import_zod4.z.string().
|
|
5781
|
-
message: import_zod4.z.string().
|
|
5782
|
-
items: import_zod4.z.array(subagentInputItemSchema).
|
|
5783
|
-
|
|
6496
|
+
agent_id: import_zod4.z.string().nullish().describe("Target subagent id."),
|
|
6497
|
+
id: import_zod4.z.string().nullish().describe("Agent id to message (from spawn_agent)."),
|
|
6498
|
+
input: import_zod4.z.string().nullish().describe("New user input queued for the subagent."),
|
|
6499
|
+
message: import_zod4.z.string().nullish().describe("Legacy plain-text message to send to the agent. Use either message or items."),
|
|
6500
|
+
items: import_zod4.z.array(subagentInputItemSchema).nullish().describe(
|
|
6501
|
+
"Structured input items. Use this to pass explicit mentions (for example app:// connector paths)."
|
|
6502
|
+
),
|
|
6503
|
+
interrupt: import_zod4.z.boolean().nullish().describe("If true and currently running, aborts active run before queuing input.")
|
|
5784
6504
|
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
5785
6505
|
message: "agent_id (or id) is required."
|
|
5786
|
-
}).refine((value) => Boolean(resolvePromptValue(value.input, value.message, value.items)), {
|
|
5787
|
-
message: "input (or message/items) is required."
|
|
5788
6506
|
});
|
|
5789
6507
|
var resumeAgentSchema = import_zod4.z.object({
|
|
5790
|
-
agent_id: import_zod4.z.string().
|
|
5791
|
-
id: import_zod4.z.string().
|
|
6508
|
+
agent_id: import_zod4.z.string().nullish().describe("Target subagent id."),
|
|
6509
|
+
id: import_zod4.z.string().nullish().describe("Agent id to resume.")
|
|
5792
6510
|
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
5793
6511
|
message: "agent_id (or id) is required."
|
|
5794
6512
|
});
|
|
5795
6513
|
var waitSchema = import_zod4.z.object({
|
|
5796
|
-
agent_id: import_zod4.z.string().
|
|
5797
|
-
id: import_zod4.z.string().
|
|
5798
|
-
ids: import_zod4.z.array(import_zod4.z.string().min(1)).
|
|
5799
|
-
timeout_ms: import_zod4.z.number().int().
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
message: "agent_id/id or ids is required."
|
|
5804
|
-
}
|
|
5805
|
-
);
|
|
6514
|
+
agent_id: import_zod4.z.string().nullish().describe("Target subagent id."),
|
|
6515
|
+
id: import_zod4.z.string().nullish().describe("Codex-style alias for agent_id."),
|
|
6516
|
+
ids: import_zod4.z.array(import_zod4.z.string().min(1)).nullish().describe("Agent ids to wait on. Pass multiple ids to wait for whichever finishes first."),
|
|
6517
|
+
timeout_ms: import_zod4.z.number().int().nullish().describe(
|
|
6518
|
+
"Optional timeout in milliseconds. Defaults to 30000, min 10000, max 3600000. Prefer longer waits (minutes) to avoid busy polling."
|
|
6519
|
+
)
|
|
6520
|
+
});
|
|
5806
6521
|
var closeSchema = import_zod4.z.object({
|
|
5807
|
-
agent_id: import_zod4.z.string().
|
|
5808
|
-
id: import_zod4.z.string().
|
|
6522
|
+
agent_id: import_zod4.z.string().nullish().describe("Target subagent id."),
|
|
6523
|
+
id: import_zod4.z.string().nullish().describe("Agent id to close (from spawn_agent).")
|
|
5809
6524
|
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
5810
6525
|
message: "agent_id (or id) is required."
|
|
5811
6526
|
});
|
|
@@ -5813,6 +6528,7 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
5813
6528
|
const defaults = {
|
|
5814
6529
|
maxAgents: DEFAULT_SUBAGENT_MAX_AGENTS,
|
|
5815
6530
|
maxDepth: DEFAULT_SUBAGENT_MAX_DEPTH,
|
|
6531
|
+
minWaitTimeoutMs: DEFAULT_SUBAGENT_MIN_WAIT_TIMEOUT_MS,
|
|
5816
6532
|
defaultWaitTimeoutMs: DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS,
|
|
5817
6533
|
maxWaitTimeoutMs: DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS,
|
|
5818
6534
|
promptPattern: "codex",
|
|
@@ -5833,10 +6549,16 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
5833
6549
|
MAX_SUBAGENT_MAX_AGENTS
|
|
5834
6550
|
);
|
|
5835
6551
|
const maxDepth = normalizeInteger(config.maxDepth, defaults.maxDepth, 1, MAX_SUBAGENT_MAX_DEPTH);
|
|
6552
|
+
const minWaitTimeoutMs = normalizeInteger(
|
|
6553
|
+
config.minWaitTimeoutMs,
|
|
6554
|
+
defaults.minWaitTimeoutMs,
|
|
6555
|
+
1,
|
|
6556
|
+
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
6557
|
+
);
|
|
5836
6558
|
const defaultWaitTimeoutMs = normalizeInteger(
|
|
5837
6559
|
config.defaultWaitTimeoutMs,
|
|
5838
6560
|
defaults.defaultWaitTimeoutMs,
|
|
5839
|
-
|
|
6561
|
+
minWaitTimeoutMs,
|
|
5840
6562
|
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
5841
6563
|
);
|
|
5842
6564
|
const maxWaitTimeoutMs = normalizeInteger(
|
|
@@ -5853,6 +6575,7 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
5853
6575
|
enabled,
|
|
5854
6576
|
maxAgents,
|
|
5855
6577
|
maxDepth,
|
|
6578
|
+
minWaitTimeoutMs,
|
|
5856
6579
|
defaultWaitTimeoutMs,
|
|
5857
6580
|
maxWaitTimeoutMs,
|
|
5858
6581
|
promptPattern,
|
|
@@ -5866,9 +6589,11 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
5866
6589
|
function buildCodexSubagentOrchestratorInstructions(params) {
|
|
5867
6590
|
return [
|
|
5868
6591
|
"Subagent orchestration tools are available: spawn_agent, send_input, resume_agent, wait, close_agent.",
|
|
6592
|
+
"Background updates may appear as <subagent_notification>{...}</subagent_notification>; treat them as status updates, not new user intent.",
|
|
6593
|
+
"Available spawn_agent agent_type values: default, researcher, worker, reviewer.",
|
|
5869
6594
|
"Use this control pattern:",
|
|
5870
6595
|
"1. spawn_agent with a focused prompt.",
|
|
5871
|
-
"2. wait
|
|
6596
|
+
"2. wait with ids=[agent_id] until the agent reaches a non-running state. Prefer long waits (minutes).",
|
|
5872
6597
|
"3. For follow-up turns, send_input then resume_agent.",
|
|
5873
6598
|
"4. close_agent when delegation is complete.",
|
|
5874
6599
|
`Limits: max active subagents ${params.maxAgents}, max depth ${params.maxDepth}, current depth ${params.currentDepth}.`
|
|
@@ -5890,9 +6615,10 @@ function createSubagentToolController(options) {
|
|
|
5890
6615
|
};
|
|
5891
6616
|
}
|
|
5892
6617
|
const agents = /* @__PURE__ */ new Map();
|
|
6618
|
+
const roleNicknameCounts = /* @__PURE__ */ new Map();
|
|
5893
6619
|
const tools = {
|
|
5894
6620
|
spawn_agent: tool({
|
|
5895
|
-
description: "
|
|
6621
|
+
description: "Spawn a sub-agent for a well-scoped task. Returns the agent id (and user-facing nickname when available) to use to communicate with this agent.",
|
|
5896
6622
|
inputSchema: spawnAgentInputSchema,
|
|
5897
6623
|
execute: async (input) => {
|
|
5898
6624
|
if (countActiveAgents(agents) >= options.config.maxAgents) {
|
|
@@ -5915,24 +6641,36 @@ function createSubagentToolController(options) {
|
|
|
5915
6641
|
}
|
|
5916
6642
|
const id = `agent_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}`;
|
|
5917
6643
|
const now = Date.now();
|
|
5918
|
-
const
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
6644
|
+
const { roleName, roleInstructions } = resolveAgentType(input.agent_type);
|
|
6645
|
+
const nickname = reserveAgentNickname(roleName, roleNicknameCounts);
|
|
6646
|
+
const perSpawnInstructions = joinInstructionBlocks(
|
|
6647
|
+
roleInstructions,
|
|
6648
|
+
trimToUndefined(input.instructions)
|
|
6649
|
+
);
|
|
6650
|
+
const initialPrompt = resolveCollabInputText({
|
|
6651
|
+
textCandidates: [{ value: input.prompt }, { value: input.message }],
|
|
6652
|
+
items: input.items,
|
|
6653
|
+
bothError: "Provide either prompt/message or items, but not both.",
|
|
6654
|
+
missingError: "Provide one of: prompt/message or items.",
|
|
6655
|
+
emptyTextError: "Empty message can't be sent to an agent.",
|
|
6656
|
+
emptyItemsError: "Items can't be empty."
|
|
6657
|
+
});
|
|
5922
6658
|
const agent = {
|
|
5923
6659
|
id,
|
|
5924
6660
|
depth: childDepth,
|
|
5925
6661
|
model,
|
|
6662
|
+
...nickname ? { nickname } : {},
|
|
6663
|
+
agentRole: roleName,
|
|
5926
6664
|
status: "idle",
|
|
5927
6665
|
createdAtMs: now,
|
|
5928
6666
|
updatedAtMs: now,
|
|
5929
6667
|
pendingInputs: [initialPrompt],
|
|
5930
|
-
history: [],
|
|
6668
|
+
history: input.fork_context && options.forkContextMessages ? [...options.forkContextMessages] : [],
|
|
5931
6669
|
...options.buildChildInstructions ? {
|
|
5932
6670
|
instructions: trimToUndefined(
|
|
5933
|
-
options.buildChildInstructions(
|
|
6671
|
+
options.buildChildInstructions(perSpawnInstructions, childDepth)
|
|
5934
6672
|
)
|
|
5935
|
-
} :
|
|
6673
|
+
} : perSpawnInstructions ? { instructions: perSpawnInstructions } : {},
|
|
5936
6674
|
...input.max_steps ? { maxSteps: input.max_steps } : options.config.maxSteps ? { maxSteps: options.config.maxSteps } : {},
|
|
5937
6675
|
turns: 0,
|
|
5938
6676
|
notification: "spawned",
|
|
@@ -5942,41 +6680,50 @@ function createSubagentToolController(options) {
|
|
|
5942
6680
|
};
|
|
5943
6681
|
agents.set(id, agent);
|
|
5944
6682
|
startRun(agent, options);
|
|
5945
|
-
return buildToolResponse(
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
6683
|
+
return buildToolResponse(
|
|
6684
|
+
agent,
|
|
6685
|
+
{
|
|
6686
|
+
notification: "spawned",
|
|
6687
|
+
message: `Spawned subagent ${id}.`
|
|
6688
|
+
},
|
|
6689
|
+
{ nickname: agent.nickname }
|
|
6690
|
+
);
|
|
5949
6691
|
}
|
|
5950
6692
|
}),
|
|
5951
6693
|
send_input: tool({
|
|
5952
|
-
description: "
|
|
6694
|
+
description: "Send a message to an existing agent. Use interrupt=true to redirect work immediately.",
|
|
5953
6695
|
inputSchema: sendInputSchema,
|
|
5954
6696
|
execute: async (input) => {
|
|
6697
|
+
const submissionId = randomSubmissionId();
|
|
5955
6698
|
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
5956
6699
|
if (!agentId) {
|
|
5957
6700
|
throw new Error("send_input requires agent_id or id.");
|
|
5958
6701
|
}
|
|
5959
6702
|
const agent = requireAgent(agents, agentId);
|
|
5960
|
-
const nextInput =
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
6703
|
+
const nextInput = resolveCollabInputText({
|
|
6704
|
+
textCandidates: [{ value: input.input }, { value: input.message }],
|
|
6705
|
+
items: input.items,
|
|
6706
|
+
bothError: "Provide either input/message or items, but not both.",
|
|
6707
|
+
missingError: "Provide one of: input/message or items.",
|
|
6708
|
+
emptyTextError: "Empty message can't be sent to an agent.",
|
|
6709
|
+
emptyItemsError: "Items can't be empty."
|
|
6710
|
+
});
|
|
5964
6711
|
if (agent.status === "closed") {
|
|
5965
|
-
throw new Error(`
|
|
6712
|
+
throw new Error(`agent with id ${agent.id} is closed`);
|
|
5966
6713
|
}
|
|
5967
6714
|
if (input.interrupt && agent.abortController) {
|
|
5968
6715
|
agent.abortController.abort("send_input_interrupt");
|
|
5969
6716
|
agent.pendingInputs.unshift(nextInput);
|
|
5970
6717
|
setNotification(agent, "input_queued", `Interrupted ${agent.id} and queued new input.`);
|
|
5971
|
-
return buildToolResponse(agent);
|
|
6718
|
+
return buildToolResponse(agent, void 0, { submission_id: submissionId });
|
|
5972
6719
|
}
|
|
5973
6720
|
agent.pendingInputs.push(nextInput);
|
|
5974
6721
|
setNotification(agent, "input_queued", `Queued input for ${agent.id}.`);
|
|
5975
|
-
return buildToolResponse(agent);
|
|
6722
|
+
return buildToolResponse(agent, void 0, { submission_id: submissionId });
|
|
5976
6723
|
}
|
|
5977
6724
|
}),
|
|
5978
6725
|
resume_agent: tool({
|
|
5979
|
-
description: "
|
|
6726
|
+
description: "Resume a previously closed agent by id so it can receive send_input and wait calls.",
|
|
5980
6727
|
inputSchema: resumeAgentSchema,
|
|
5981
6728
|
execute: async (input) => {
|
|
5982
6729
|
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
@@ -5985,10 +6732,11 @@ function createSubagentToolController(options) {
|
|
|
5985
6732
|
}
|
|
5986
6733
|
const agent = requireAgent(agents, agentId);
|
|
5987
6734
|
if (agent.status === "closed") {
|
|
5988
|
-
|
|
6735
|
+
agent.status = "idle";
|
|
6736
|
+
setNotification(agent, "resumed", `Resumed subagent ${agent.id}.`);
|
|
5989
6737
|
return buildToolResponse(agent, {
|
|
5990
|
-
notification: "
|
|
5991
|
-
message: `
|
|
6738
|
+
notification: "resumed",
|
|
6739
|
+
message: `Resumed subagent ${agent.id}.`
|
|
5992
6740
|
});
|
|
5993
6741
|
}
|
|
5994
6742
|
const outcome = startRun(agent, options);
|
|
@@ -6007,41 +6755,42 @@ function createSubagentToolController(options) {
|
|
|
6007
6755
|
}
|
|
6008
6756
|
}),
|
|
6009
6757
|
wait: tool({
|
|
6010
|
-
description: "
|
|
6758
|
+
description: "Wait for agents to reach a final status. Completed statuses may include the agent's final message. Returns empty status when timed out. Once the agent reaches a final status, a notification message will be received containing the same completed status.",
|
|
6011
6759
|
inputSchema: waitSchema,
|
|
6012
6760
|
execute: async (input) => {
|
|
6013
|
-
const usesIdsArray = Array.isArray(input.ids) && input.ids.length > 0;
|
|
6014
6761
|
const ids = resolveAgentIdList(input.agent_id, input.id, input.ids);
|
|
6015
6762
|
if (ids.length === 0) {
|
|
6016
|
-
throw new Error("
|
|
6763
|
+
throw new Error("ids must be non-empty");
|
|
6764
|
+
}
|
|
6765
|
+
if (typeof input.timeout_ms === "number" && input.timeout_ms <= 0) {
|
|
6766
|
+
throw new Error("timeout_ms must be greater than zero");
|
|
6017
6767
|
}
|
|
6018
6768
|
const timeoutMs = normalizeInteger(
|
|
6019
6769
|
input.timeout_ms,
|
|
6020
6770
|
options.config.defaultWaitTimeoutMs,
|
|
6021
|
-
|
|
6771
|
+
options.config.minWaitTimeoutMs,
|
|
6022
6772
|
options.config.maxWaitTimeoutMs
|
|
6023
6773
|
);
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
agent,
|
|
6034
|
-
"timeout",
|
|
6035
|
-
`Timed out after ${timeoutMs}ms while waiting for ${agent.id}.`
|
|
6036
|
-
);
|
|
6037
|
-
return buildToolResponse(agent, void 0, { timed_out: true, timeout_ms: timeoutMs });
|
|
6038
|
-
}
|
|
6774
|
+
const status = await waitForAnyAgentStatus(agents, ids, timeoutMs);
|
|
6775
|
+
const timedOut = Object.keys(status).length === 0;
|
|
6776
|
+
if (timedOut && ids.length === 1) {
|
|
6777
|
+
const agent = requireAgent(agents, ids[0]);
|
|
6778
|
+
setNotification(
|
|
6779
|
+
agent,
|
|
6780
|
+
"timeout",
|
|
6781
|
+
`Timed out after ${timeoutMs}ms while waiting for ${agent.id}.`
|
|
6782
|
+
);
|
|
6039
6783
|
}
|
|
6040
|
-
return
|
|
6784
|
+
return {
|
|
6785
|
+
status,
|
|
6786
|
+
status_summary: summarizeAgentStatuses(status),
|
|
6787
|
+
timed_out: timedOut,
|
|
6788
|
+
timeout_ms: timeoutMs
|
|
6789
|
+
};
|
|
6041
6790
|
}
|
|
6042
6791
|
}),
|
|
6043
6792
|
close_agent: tool({
|
|
6044
|
-
description: "
|
|
6793
|
+
description: "Close an agent when it is no longer needed and return its last known status.",
|
|
6045
6794
|
inputSchema: closeSchema,
|
|
6046
6795
|
execute: async (input) => {
|
|
6047
6796
|
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
@@ -6053,7 +6802,7 @@ function createSubagentToolController(options) {
|
|
|
6053
6802
|
setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
|
|
6054
6803
|
return buildToolResponse(agent, void 0, { cancelled: false });
|
|
6055
6804
|
}
|
|
6056
|
-
const cancelled = closeSubagent(agent, `Closed ${agent.id}
|
|
6805
|
+
const cancelled = closeSubagent(agent, `Closed ${agent.id}.`, options);
|
|
6057
6806
|
return buildToolResponse(
|
|
6058
6807
|
agent,
|
|
6059
6808
|
{ notification: "closed", message: `Closed ${agent.id}.` },
|
|
@@ -6068,7 +6817,7 @@ function createSubagentToolController(options) {
|
|
|
6068
6817
|
const running = [];
|
|
6069
6818
|
for (const agent of agents.values()) {
|
|
6070
6819
|
if (agent.status !== "closed") {
|
|
6071
|
-
closeSubagent(agent, `Parent agent loop closed ${agent.id}
|
|
6820
|
+
closeSubagent(agent, `Parent agent loop closed ${agent.id}.`, options);
|
|
6072
6821
|
}
|
|
6073
6822
|
if (agent.runningPromise) {
|
|
6074
6823
|
running.push(agent.runningPromise);
|
|
@@ -6083,7 +6832,7 @@ function createSubagentToolController(options) {
|
|
|
6083
6832
|
function requireAgent(agents, id) {
|
|
6084
6833
|
const agent = agents.get(id);
|
|
6085
6834
|
if (!agent) {
|
|
6086
|
-
throw new Error(`
|
|
6835
|
+
throw new Error(`agent with id ${id} not found`);
|
|
6087
6836
|
}
|
|
6088
6837
|
return agent;
|
|
6089
6838
|
}
|
|
@@ -6102,17 +6851,33 @@ function resolveAgentIdList(agentId, idAlias, ids) {
|
|
|
6102
6851
|
const single = resolveAgentIdValue(agentId, idAlias);
|
|
6103
6852
|
return single ? [single] : [];
|
|
6104
6853
|
}
|
|
6105
|
-
function
|
|
6106
|
-
const
|
|
6107
|
-
|
|
6108
|
-
|
|
6854
|
+
function resolveCollabInputText(params) {
|
|
6855
|
+
const textCandidate = params.textCandidates.find(
|
|
6856
|
+
(candidate) => candidate.value !== void 0 && candidate.value !== null
|
|
6857
|
+
);
|
|
6858
|
+
const hasText = Boolean(textCandidate);
|
|
6859
|
+
const hasItems = params.items !== void 0 && params.items !== null;
|
|
6860
|
+
if (hasText && hasItems) {
|
|
6861
|
+
throw new Error(params.bothError);
|
|
6109
6862
|
}
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
return messageValue;
|
|
6863
|
+
if (!hasText && !hasItems) {
|
|
6864
|
+
throw new Error(params.missingError);
|
|
6113
6865
|
}
|
|
6114
|
-
|
|
6115
|
-
|
|
6866
|
+
if (hasText) {
|
|
6867
|
+
const value = textCandidate?.value?.trim();
|
|
6868
|
+
if (!value) {
|
|
6869
|
+
throw new Error(params.emptyTextError);
|
|
6870
|
+
}
|
|
6871
|
+
return value;
|
|
6872
|
+
}
|
|
6873
|
+
if (!params.items || params.items.length === 0) {
|
|
6874
|
+
throw new Error(params.emptyItemsError);
|
|
6875
|
+
}
|
|
6876
|
+
const itemText = resolveInputItemsText(params.items);
|
|
6877
|
+
if (!itemText) {
|
|
6878
|
+
throw new Error(params.emptyItemsError);
|
|
6879
|
+
}
|
|
6880
|
+
return itemText;
|
|
6116
6881
|
}
|
|
6117
6882
|
function resolveInputItemsText(items) {
|
|
6118
6883
|
if (!items || items.length === 0) {
|
|
@@ -6128,9 +6893,28 @@ function resolveInputItemsText(items) {
|
|
|
6128
6893
|
const name = typeof item.name === "string" ? item.name.trim() : "";
|
|
6129
6894
|
const path6 = typeof item.path === "string" ? item.path.trim() : "";
|
|
6130
6895
|
const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6896
|
+
if (itemType === "image") {
|
|
6897
|
+
lines.push("[image]");
|
|
6898
|
+
continue;
|
|
6899
|
+
}
|
|
6900
|
+
if (itemType === "local_image" && path6) {
|
|
6901
|
+
lines.push(`[local_image:${path6}]`);
|
|
6902
|
+
continue;
|
|
6903
|
+
}
|
|
6904
|
+
if (itemType === "skill" && name && path6) {
|
|
6905
|
+
lines.push(`[skill:$${name}](${path6})`);
|
|
6906
|
+
continue;
|
|
6907
|
+
}
|
|
6908
|
+
if (itemType === "mention" && name && path6) {
|
|
6909
|
+
lines.push(`[mention:$${name}](${path6})`);
|
|
6910
|
+
continue;
|
|
6911
|
+
}
|
|
6912
|
+
if (path6 || imageUrl) {
|
|
6913
|
+
lines.push(`[${itemType || "input"}:${path6 || imageUrl}]`);
|
|
6914
|
+
continue;
|
|
6915
|
+
}
|
|
6916
|
+
if (name) {
|
|
6917
|
+
lines.push(`[${itemType || "input"}:${name}]`);
|
|
6134
6918
|
}
|
|
6135
6919
|
}
|
|
6136
6920
|
if (lines.length === 0) {
|
|
@@ -6205,7 +6989,12 @@ function startRun(agent, options) {
|
|
|
6205
6989
|
}
|
|
6206
6990
|
const input = [...agent.history, { role: "user", content: nextInput }];
|
|
6207
6991
|
const abortController = new AbortController();
|
|
6992
|
+
const runStartedAtMs = Date.now();
|
|
6208
6993
|
agent.abortController = abortController;
|
|
6994
|
+
if (agent.firstRunStartedAtMs === void 0) {
|
|
6995
|
+
agent.firstRunStartedAtMs = runStartedAtMs;
|
|
6996
|
+
}
|
|
6997
|
+
agent.lastRunStartedAtMs = runStartedAtMs;
|
|
6209
6998
|
agent.lastError = void 0;
|
|
6210
6999
|
setLifecycle(
|
|
6211
7000
|
agent,
|
|
@@ -6237,6 +7026,7 @@ function startRun(agent, options) {
|
|
|
6237
7026
|
"run_completed",
|
|
6238
7027
|
`Subagent ${agent.id} completed run ${agent.turns}.`
|
|
6239
7028
|
);
|
|
7029
|
+
emitBackgroundNotification(agent, options);
|
|
6240
7030
|
} catch (error) {
|
|
6241
7031
|
if (agent.status === "closed") {
|
|
6242
7032
|
return;
|
|
@@ -6248,7 +7038,11 @@ function startRun(agent, options) {
|
|
|
6248
7038
|
const message = toErrorMessage(error);
|
|
6249
7039
|
agent.lastError = message;
|
|
6250
7040
|
setLifecycle(agent, "failed", "run_failed", `Subagent ${agent.id} failed: ${message}`);
|
|
7041
|
+
emitBackgroundNotification(agent, options);
|
|
6251
7042
|
} finally {
|
|
7043
|
+
const runCompletedAtMs = Date.now();
|
|
7044
|
+
agent.lastRunCompletedAtMs = runCompletedAtMs;
|
|
7045
|
+
agent.lastRunDurationMs = Math.max(0, runCompletedAtMs - runStartedAtMs);
|
|
6252
7046
|
agent.runningPromise = void 0;
|
|
6253
7047
|
agent.abortController = void 0;
|
|
6254
7048
|
}
|
|
@@ -6256,30 +7050,16 @@ function startRun(agent, options) {
|
|
|
6256
7050
|
agent.runningPromise = runPromise;
|
|
6257
7051
|
return "started";
|
|
6258
7052
|
}
|
|
6259
|
-
function closeSubagent(agent, message) {
|
|
7053
|
+
function closeSubagent(agent, message, options) {
|
|
6260
7054
|
const cancelled = Boolean(agent.runningPromise);
|
|
6261
7055
|
agent.pendingInputs = [];
|
|
6262
7056
|
if (agent.abortController) {
|
|
6263
7057
|
agent.abortController.abort("close_agent");
|
|
6264
7058
|
}
|
|
6265
7059
|
setLifecycle(agent, "closed", "closed", message);
|
|
7060
|
+
emitBackgroundNotification(agent, options);
|
|
6266
7061
|
return cancelled;
|
|
6267
7062
|
}
|
|
6268
|
-
async function waitUntilNotRunning(agent, timeoutMs) {
|
|
6269
|
-
const deadline = Date.now() + timeoutMs;
|
|
6270
|
-
while (agent.status === "running") {
|
|
6271
|
-
const remaining = deadline - Date.now();
|
|
6272
|
-
if (remaining <= 0) {
|
|
6273
|
-
return false;
|
|
6274
|
-
}
|
|
6275
|
-
const currentVersion = agent.version;
|
|
6276
|
-
const changed = await waitForVersionChange(agent, currentVersion, remaining);
|
|
6277
|
-
if (!changed) {
|
|
6278
|
-
return false;
|
|
6279
|
-
}
|
|
6280
|
-
}
|
|
6281
|
-
return true;
|
|
6282
|
-
}
|
|
6283
7063
|
async function waitForVersionChange(agent, version, timeoutMs) {
|
|
6284
7064
|
if (agent.version !== version) {
|
|
6285
7065
|
return true;
|
|
@@ -6317,6 +7097,8 @@ function buildToolResponse(agent, override, extra = {}) {
|
|
|
6317
7097
|
function buildSnapshot(agent) {
|
|
6318
7098
|
return {
|
|
6319
7099
|
agent_id: agent.id,
|
|
7100
|
+
...agent.nickname ? { nickname: agent.nickname } : {},
|
|
7101
|
+
agent_role: agent.agentRole,
|
|
6320
7102
|
status: agent.status,
|
|
6321
7103
|
depth: agent.depth,
|
|
6322
7104
|
model: agent.model,
|
|
@@ -6324,6 +7106,13 @@ function buildSnapshot(agent) {
|
|
|
6324
7106
|
turns: agent.turns,
|
|
6325
7107
|
created_at: new Date(agent.createdAtMs).toISOString(),
|
|
6326
7108
|
updated_at: new Date(agent.updatedAtMs).toISOString(),
|
|
7109
|
+
...agent.firstRunStartedAtMs ? {
|
|
7110
|
+
first_run_started_at: new Date(agent.firstRunStartedAtMs).toISOString(),
|
|
7111
|
+
spawn_startup_latency_ms: Math.max(0, agent.firstRunStartedAtMs - agent.createdAtMs)
|
|
7112
|
+
} : {},
|
|
7113
|
+
...agent.lastRunStartedAtMs ? { last_run_started_at: new Date(agent.lastRunStartedAtMs).toISOString() } : {},
|
|
7114
|
+
...agent.lastRunCompletedAtMs ? { last_run_completed_at: new Date(agent.lastRunCompletedAtMs).toISOString() } : {},
|
|
7115
|
+
...typeof agent.lastRunDurationMs === "number" ? { last_run_duration_ms: Math.max(0, agent.lastRunDurationMs) } : {},
|
|
6327
7116
|
...agent.lastError ? { last_error: agent.lastError } : {},
|
|
6328
7117
|
...agent.lastResult ? {
|
|
6329
7118
|
last_result: {
|
|
@@ -6335,6 +7124,83 @@ function buildSnapshot(agent) {
|
|
|
6335
7124
|
} : {}
|
|
6336
7125
|
};
|
|
6337
7126
|
}
|
|
7127
|
+
function emitBackgroundNotification(agent, options) {
|
|
7128
|
+
if (!options?.onBackgroundMessage) {
|
|
7129
|
+
return;
|
|
7130
|
+
}
|
|
7131
|
+
if (!isBackgroundNotification(agent.notification)) {
|
|
7132
|
+
return;
|
|
7133
|
+
}
|
|
7134
|
+
const payload = {
|
|
7135
|
+
agent_id: agent.id,
|
|
7136
|
+
status: buildSnapshot(agent)
|
|
7137
|
+
};
|
|
7138
|
+
const body = JSON.stringify(payload);
|
|
7139
|
+
try {
|
|
7140
|
+
options.onBackgroundMessage(
|
|
7141
|
+
`${SUBAGENT_NOTIFICATION_OPEN_TAG}${body}${SUBAGENT_NOTIFICATION_CLOSE_TAG}`
|
|
7142
|
+
);
|
|
7143
|
+
} catch {
|
|
7144
|
+
}
|
|
7145
|
+
}
|
|
7146
|
+
function isBackgroundNotification(notification) {
|
|
7147
|
+
return notification === "run_completed" || notification === "run_failed" || notification === "closed";
|
|
7148
|
+
}
|
|
7149
|
+
function summarizeAgentStatuses(status) {
|
|
7150
|
+
const summary = {};
|
|
7151
|
+
for (const [agentId, snapshot] of Object.entries(status)) {
|
|
7152
|
+
const value = snapshot.status;
|
|
7153
|
+
summary[agentId] = typeof value === "string" ? value : "unknown";
|
|
7154
|
+
}
|
|
7155
|
+
return summary;
|
|
7156
|
+
}
|
|
7157
|
+
function buildSpawnAgentTypeDescription() {
|
|
7158
|
+
const sections = BUILT_IN_AGENT_TYPES.map((name) => {
|
|
7159
|
+
const description = BUILT_IN_AGENT_TYPE_DESCRIPTIONS[name];
|
|
7160
|
+
return `${name}: {
|
|
7161
|
+
${description}
|
|
7162
|
+
}`;
|
|
7163
|
+
});
|
|
7164
|
+
return [
|
|
7165
|
+
`Optional type name for the new agent. If omitted, \`${DEFAULT_AGENT_TYPE}\` is used.`,
|
|
7166
|
+
"Available roles:",
|
|
7167
|
+
...sections
|
|
7168
|
+
].join("\n");
|
|
7169
|
+
}
|
|
7170
|
+
function resolveAgentType(agentType) {
|
|
7171
|
+
const requestedRoleName = trimToUndefined(agentType) ?? DEFAULT_AGENT_TYPE;
|
|
7172
|
+
const roleName = requestedRoleName;
|
|
7173
|
+
const description = BUILT_IN_AGENT_TYPE_DESCRIPTIONS[roleName];
|
|
7174
|
+
if (!description) {
|
|
7175
|
+
throw new Error(`unknown agent_type '${requestedRoleName}'`);
|
|
7176
|
+
}
|
|
7177
|
+
return {
|
|
7178
|
+
roleName,
|
|
7179
|
+
roleInstructions: BUILT_IN_AGENT_TYPE_INSTRUCTIONS[roleName]
|
|
7180
|
+
};
|
|
7181
|
+
}
|
|
7182
|
+
function reserveAgentNickname(roleName, counts) {
|
|
7183
|
+
const prefixByRole = {
|
|
7184
|
+
default: "Agent",
|
|
7185
|
+
researcher: "Researcher",
|
|
7186
|
+
worker: "Worker",
|
|
7187
|
+
reviewer: "Reviewer"
|
|
7188
|
+
};
|
|
7189
|
+
const prefix = prefixByRole[roleName] ?? "Agent";
|
|
7190
|
+
const next = (counts.get(prefix) ?? 0) + 1;
|
|
7191
|
+
counts.set(prefix, next);
|
|
7192
|
+
return `${prefix}_${next}`;
|
|
7193
|
+
}
|
|
7194
|
+
function joinInstructionBlocks(...blocks) {
|
|
7195
|
+
const parts = blocks.map(trimToUndefined).filter((value) => Boolean(value));
|
|
7196
|
+
if (parts.length === 0) {
|
|
7197
|
+
return void 0;
|
|
7198
|
+
}
|
|
7199
|
+
return parts.join("\n\n");
|
|
7200
|
+
}
|
|
7201
|
+
function randomSubmissionId() {
|
|
7202
|
+
return `sub_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}`;
|
|
7203
|
+
}
|
|
6338
7204
|
function normalizeInteger(value, fallback, min, max) {
|
|
6339
7205
|
const parsed = Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
6340
7206
|
return Math.max(min, Math.min(max, parsed));
|
|
@@ -7073,29 +7939,33 @@ var DEFAULT_MAX_LINE_LENGTH = 500;
|
|
|
7073
7939
|
var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
|
|
7074
7940
|
var DEFAULT_TAB_WIDTH = 4;
|
|
7075
7941
|
var codexReadFileInputSchema = import_zod6.z.object({
|
|
7076
|
-
file_path: import_zod6.z.string().min(1).describe(
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7942
|
+
file_path: import_zod6.z.string().min(1).describe(
|
|
7943
|
+
"Path to the file (relative to cwd, or absolute. In sandbox mode, / maps to the sandbox root)."
|
|
7944
|
+
),
|
|
7945
|
+
offset: import_zod6.z.number().int().min(1).nullish().describe("The line number to start reading from. Must be 1 or greater."),
|
|
7946
|
+
limit: import_zod6.z.number().int().min(1).nullish().describe("The maximum number of lines to return."),
|
|
7947
|
+
mode: import_zod6.z.enum(["slice", "indentation"]).nullish().describe('Optional mode selector: "slice" (default) or "indentation".'),
|
|
7080
7948
|
indentation: import_zod6.z.object({
|
|
7081
|
-
anchor_line: import_zod6.z.number().int().min(1).
|
|
7082
|
-
max_levels: import_zod6.z.number().int().min(0).
|
|
7083
|
-
include_siblings: import_zod6.z.boolean().
|
|
7084
|
-
include_header: import_zod6.z.boolean().
|
|
7085
|
-
max_lines: import_zod6.z.number().int().min(1).
|
|
7086
|
-
}).
|
|
7949
|
+
anchor_line: import_zod6.z.number().int().min(1).nullish(),
|
|
7950
|
+
max_levels: import_zod6.z.number().int().min(0).nullish(),
|
|
7951
|
+
include_siblings: import_zod6.z.boolean().nullish(),
|
|
7952
|
+
include_header: import_zod6.z.boolean().nullish(),
|
|
7953
|
+
max_lines: import_zod6.z.number().int().min(1).nullish()
|
|
7954
|
+
}).nullish()
|
|
7087
7955
|
});
|
|
7088
7956
|
var codexListDirInputSchema = import_zod6.z.object({
|
|
7089
|
-
dir_path: import_zod6.z.string().min(1).describe(
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7957
|
+
dir_path: import_zod6.z.string().min(1).describe(
|
|
7958
|
+
"Path to the directory to list (relative to cwd, or absolute. In sandbox mode, / maps to the sandbox root)."
|
|
7959
|
+
),
|
|
7960
|
+
offset: import_zod6.z.number().int().min(1).nullish().describe("The entry number to start listing from. Must be 1 or greater."),
|
|
7961
|
+
limit: import_zod6.z.number().int().min(1).nullish().describe("The maximum number of entries to return."),
|
|
7962
|
+
depth: import_zod6.z.number().int().min(1).nullish().describe("The maximum directory depth to traverse. Must be 1 or greater.")
|
|
7093
7963
|
});
|
|
7094
7964
|
var codexGrepFilesInputSchema = import_zod6.z.object({
|
|
7095
7965
|
pattern: import_zod6.z.string().min(1).describe("Regular expression pattern to search for."),
|
|
7096
|
-
include: import_zod6.z.string().
|
|
7097
|
-
path: import_zod6.z.string().
|
|
7098
|
-
limit: import_zod6.z.number().int().min(1).
|
|
7966
|
+
include: import_zod6.z.string().nullish().describe('Optional glob limiting searched files (for example "*.rs").'),
|
|
7967
|
+
path: import_zod6.z.string().nullish().describe("Directory or file path to search. Defaults to cwd."),
|
|
7968
|
+
limit: import_zod6.z.number().int().min(1).nullish().describe("Maximum number of file paths to return (defaults to 100).")
|
|
7099
7969
|
});
|
|
7100
7970
|
var applyPatchInputSchema = import_zod6.z.object({
|
|
7101
7971
|
input: import_zod6.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
@@ -7330,9 +8200,6 @@ function createGlobTool(options = {}) {
|
|
|
7330
8200
|
}
|
|
7331
8201
|
async function readFileCodex(input, options) {
|
|
7332
8202
|
const runtime = resolveRuntime(options);
|
|
7333
|
-
if (!import_node_path5.default.isAbsolute(input.file_path)) {
|
|
7334
|
-
throw new Error("file_path must be an absolute path");
|
|
7335
|
-
}
|
|
7336
8203
|
const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
7337
8204
|
await runAccessHook2(runtime, {
|
|
7338
8205
|
cwd: runtime.cwd,
|
|
@@ -7375,15 +8242,12 @@ async function readFileCodex(input, options) {
|
|
|
7375
8242
|
maxLevels: indentation.max_levels ?? 0,
|
|
7376
8243
|
includeSiblings: indentation.include_siblings ?? false,
|
|
7377
8244
|
includeHeader: indentation.include_header ?? true,
|
|
7378
|
-
maxLines: indentation.max_lines
|
|
8245
|
+
maxLines: indentation.max_lines ?? void 0
|
|
7379
8246
|
});
|
|
7380
8247
|
return selected.map((record) => `L${record.number}: ${record.display}`).join("\n");
|
|
7381
8248
|
}
|
|
7382
8249
|
async function listDirectoryCodex(input, options) {
|
|
7383
8250
|
const runtime = resolveRuntime(options);
|
|
7384
|
-
if (!import_node_path5.default.isAbsolute(input.dir_path)) {
|
|
7385
|
-
throw new Error("dir_path must be an absolute path");
|
|
7386
|
-
}
|
|
7387
8251
|
const dirPath = resolvePathWithPolicy(input.dir_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
7388
8252
|
await runAccessHook2(runtime, {
|
|
7389
8253
|
cwd: runtime.cwd,
|
|
@@ -7411,7 +8275,7 @@ async function listDirectoryCodex(input, options) {
|
|
|
7411
8275
|
const remaining = entries.length - startIndex;
|
|
7412
8276
|
const cappedLimit = Math.min(limit, remaining);
|
|
7413
8277
|
const selected = entries.slice(startIndex, startIndex + cappedLimit);
|
|
7414
|
-
const output = [`Absolute path: ${dirPath}`];
|
|
8278
|
+
const output = [`Absolute path: ${toSandboxDisplayPath(dirPath, runtime.cwd)}`];
|
|
7415
8279
|
for (const entry of selected) {
|
|
7416
8280
|
output.push(formatListEntry(entry));
|
|
7417
8281
|
}
|
|
@@ -7797,10 +8661,17 @@ function mapApplyPatchAction(action) {
|
|
|
7797
8661
|
}
|
|
7798
8662
|
function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
7799
8663
|
const absolutePath = import_node_path5.default.isAbsolute(inputPath) ? import_node_path5.default.resolve(inputPath) : import_node_path5.default.resolve(cwd, inputPath);
|
|
7800
|
-
if (
|
|
7801
|
-
|
|
8664
|
+
if (allowOutsideCwd || isPathInsideCwd2(absolutePath, cwd)) {
|
|
8665
|
+
return absolutePath;
|
|
7802
8666
|
}
|
|
7803
|
-
|
|
8667
|
+
if (import_node_path5.default.isAbsolute(inputPath)) {
|
|
8668
|
+
const sandboxRelativePath = inputPath.replace(/^[/\\]+/, "");
|
|
8669
|
+
const sandboxRootedPath = import_node_path5.default.resolve(cwd, sandboxRelativePath);
|
|
8670
|
+
if (isPathInsideCwd2(sandboxRootedPath, cwd)) {
|
|
8671
|
+
return sandboxRootedPath;
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
throw new Error(`path "${inputPath}" resolves outside cwd "${cwd}"`);
|
|
7804
8675
|
}
|
|
7805
8676
|
function isPathInsideCwd2(candidatePath, cwd) {
|
|
7806
8677
|
const relative = import_node_path5.default.relative(cwd, candidatePath);
|
|
@@ -7816,6 +8687,16 @@ function toDisplayPath2(absolutePath, cwd) {
|
|
|
7816
8687
|
}
|
|
7817
8688
|
return absolutePath;
|
|
7818
8689
|
}
|
|
8690
|
+
function toSandboxDisplayPath(absolutePath, cwd) {
|
|
8691
|
+
const relative = import_node_path5.default.relative(cwd, absolutePath);
|
|
8692
|
+
if (relative === "") {
|
|
8693
|
+
return "/";
|
|
8694
|
+
}
|
|
8695
|
+
if (!relative.startsWith("..") && !import_node_path5.default.isAbsolute(relative)) {
|
|
8696
|
+
return `/${normalizeSlashes(relative)}`;
|
|
8697
|
+
}
|
|
8698
|
+
return normalizeSlashes(absolutePath);
|
|
8699
|
+
}
|
|
7819
8700
|
function splitLines(content) {
|
|
7820
8701
|
const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
7821
8702
|
const lines = normalized.split("\n");
|
|
@@ -8153,7 +9034,71 @@ function isNoEntError(error) {
|
|
|
8153
9034
|
|
|
8154
9035
|
// src/agent.ts
|
|
8155
9036
|
async function runAgentLoop(request) {
|
|
8156
|
-
|
|
9037
|
+
const telemetry = createAgentTelemetrySession(request.telemetry);
|
|
9038
|
+
try {
|
|
9039
|
+
return await runAgentLoopInternal(request, { depth: 0, telemetry });
|
|
9040
|
+
} finally {
|
|
9041
|
+
await telemetry?.flush();
|
|
9042
|
+
}
|
|
9043
|
+
}
|
|
9044
|
+
function mergeAbortSignals2(first, second) {
|
|
9045
|
+
if (!first) {
|
|
9046
|
+
return second;
|
|
9047
|
+
}
|
|
9048
|
+
if (!second) {
|
|
9049
|
+
return first;
|
|
9050
|
+
}
|
|
9051
|
+
const controller = new AbortController();
|
|
9052
|
+
const abortFrom = (signal) => {
|
|
9053
|
+
if (!controller.signal.aborted) {
|
|
9054
|
+
controller.abort(signal.reason);
|
|
9055
|
+
}
|
|
9056
|
+
};
|
|
9057
|
+
if (first.aborted) {
|
|
9058
|
+
abortFrom(first);
|
|
9059
|
+
} else {
|
|
9060
|
+
first.addEventListener("abort", () => abortFrom(first), { once: true });
|
|
9061
|
+
}
|
|
9062
|
+
if (second.aborted) {
|
|
9063
|
+
abortFrom(second);
|
|
9064
|
+
} else {
|
|
9065
|
+
second.addEventListener("abort", () => abortFrom(second), { once: true });
|
|
9066
|
+
}
|
|
9067
|
+
return controller.signal;
|
|
9068
|
+
}
|
|
9069
|
+
function streamAgentLoop(request) {
|
|
9070
|
+
const queue = createAsyncQueue();
|
|
9071
|
+
const abortController = new AbortController();
|
|
9072
|
+
const steering = request.steering ?? createToolLoopSteeringChannel();
|
|
9073
|
+
const signal = mergeAbortSignals2(request.signal, abortController.signal);
|
|
9074
|
+
const sourceOnEvent = request.onEvent;
|
|
9075
|
+
const result = (async () => {
|
|
9076
|
+
try {
|
|
9077
|
+
const output = await runAgentLoop({
|
|
9078
|
+
...request,
|
|
9079
|
+
steering,
|
|
9080
|
+
...signal ? { signal } : {},
|
|
9081
|
+
onEvent: (event) => {
|
|
9082
|
+
sourceOnEvent?.(event);
|
|
9083
|
+
queue.push(event);
|
|
9084
|
+
}
|
|
9085
|
+
});
|
|
9086
|
+
queue.close();
|
|
9087
|
+
return output;
|
|
9088
|
+
} catch (error) {
|
|
9089
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
9090
|
+
queue.fail(err);
|
|
9091
|
+
throw err;
|
|
9092
|
+
}
|
|
9093
|
+
})();
|
|
9094
|
+
return {
|
|
9095
|
+
events: queue.iterable,
|
|
9096
|
+
result,
|
|
9097
|
+
append: steering.append,
|
|
9098
|
+
steer: steering.steer,
|
|
9099
|
+
pendingSteeringCount: steering.pendingCount,
|
|
9100
|
+
abort: () => abortController.abort()
|
|
9101
|
+
};
|
|
8157
9102
|
}
|
|
8158
9103
|
async function runAgentLoopInternal(request, context) {
|
|
8159
9104
|
const {
|
|
@@ -8163,19 +9108,28 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8163
9108
|
subagentTool,
|
|
8164
9109
|
subagent_tool,
|
|
8165
9110
|
subagents,
|
|
9111
|
+
telemetry,
|
|
8166
9112
|
...toolLoopRequest
|
|
8167
9113
|
} = request;
|
|
9114
|
+
const telemetrySession = context.telemetry ?? createAgentTelemetrySession(telemetry);
|
|
9115
|
+
const runId = randomRunId();
|
|
9116
|
+
const startedAtMs = Date.now();
|
|
9117
|
+
const steeringChannel = toolLoopRequest.steering ?? createToolLoopSteeringChannel();
|
|
9118
|
+
const toolLoopRequestWithSteering = toolLoopRequest.steering === steeringChannel ? toolLoopRequest : { ...toolLoopRequest, steering: steeringChannel };
|
|
8168
9119
|
const filesystemSelection = filesystemTool ?? filesystem_tool;
|
|
8169
9120
|
const subagentSelection = subagentTool ?? subagent_tool ?? subagents;
|
|
8170
9121
|
const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
|
|
8171
9122
|
const resolvedSubagentConfig = resolveSubagentToolConfig(subagentSelection, context.depth);
|
|
8172
9123
|
const subagentController = createSubagentController({
|
|
9124
|
+
runId,
|
|
8173
9125
|
model: request.model,
|
|
8174
9126
|
depth: context.depth,
|
|
9127
|
+
telemetry: telemetrySession,
|
|
8175
9128
|
customTools: customTools ?? {},
|
|
8176
9129
|
filesystemSelection,
|
|
8177
9130
|
subagentSelection,
|
|
8178
|
-
toolLoopRequest,
|
|
9131
|
+
toolLoopRequest: toolLoopRequestWithSteering,
|
|
9132
|
+
steering: steeringChannel,
|
|
8179
9133
|
resolvedSubagentConfig
|
|
8180
9134
|
});
|
|
8181
9135
|
const mergedTools = mergeToolSets(
|
|
@@ -8188,16 +9142,58 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8188
9142
|
);
|
|
8189
9143
|
}
|
|
8190
9144
|
const instructions = buildLoopInstructions(
|
|
8191
|
-
|
|
9145
|
+
toolLoopRequestWithSteering.instructions,
|
|
8192
9146
|
resolvedSubagentConfig,
|
|
8193
9147
|
context.depth
|
|
8194
9148
|
);
|
|
9149
|
+
const emitTelemetry = createAgentTelemetryEmitter({
|
|
9150
|
+
session: telemetrySession,
|
|
9151
|
+
runId,
|
|
9152
|
+
parentRunId: context.parentRunId,
|
|
9153
|
+
depth: context.depth,
|
|
9154
|
+
model: request.model
|
|
9155
|
+
});
|
|
9156
|
+
emitTelemetry({
|
|
9157
|
+
type: "agent.run.started",
|
|
9158
|
+
inputMode: typeof request.input === "string" ? "string" : "messages",
|
|
9159
|
+
customToolCount: Object.keys(customTools ?? {}).length,
|
|
9160
|
+
mergedToolCount: Object.keys(mergedTools).length,
|
|
9161
|
+
filesystemToolsEnabled: Object.keys(filesystemTools).length > 0,
|
|
9162
|
+
subagentToolsEnabled: resolvedSubagentConfig.enabled
|
|
9163
|
+
});
|
|
9164
|
+
const sourceOnEvent = toolLoopRequestWithSteering.onEvent;
|
|
9165
|
+
const includeLlmStreamEvents = telemetrySession?.includeLlmStreamEvents === true;
|
|
9166
|
+
const wrappedOnEvent = sourceOnEvent || includeLlmStreamEvents ? (event) => {
|
|
9167
|
+
sourceOnEvent?.(event);
|
|
9168
|
+
if (includeLlmStreamEvents) {
|
|
9169
|
+
emitTelemetry({ type: "agent.run.stream", event });
|
|
9170
|
+
}
|
|
9171
|
+
} : void 0;
|
|
8195
9172
|
try {
|
|
8196
|
-
|
|
8197
|
-
...
|
|
9173
|
+
const result = await runToolLoop({
|
|
9174
|
+
...toolLoopRequestWithSteering,
|
|
8198
9175
|
...instructions ? { instructions } : {},
|
|
9176
|
+
...wrappedOnEvent ? { onEvent: wrappedOnEvent } : {},
|
|
8199
9177
|
tools: mergedTools
|
|
8200
9178
|
});
|
|
9179
|
+
emitTelemetry({
|
|
9180
|
+
type: "agent.run.completed",
|
|
9181
|
+
success: true,
|
|
9182
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9183
|
+
stepCount: result.steps.length,
|
|
9184
|
+
toolCallCount: countToolCalls(result),
|
|
9185
|
+
totalCostUsd: result.totalCostUsd,
|
|
9186
|
+
usage: summarizeResultUsage(result)
|
|
9187
|
+
});
|
|
9188
|
+
return result;
|
|
9189
|
+
} catch (error) {
|
|
9190
|
+
emitTelemetry({
|
|
9191
|
+
type: "agent.run.completed",
|
|
9192
|
+
success: false,
|
|
9193
|
+
durationMs: Math.max(0, Date.now() - startedAtMs),
|
|
9194
|
+
error: toErrorMessage2(error)
|
|
9195
|
+
});
|
|
9196
|
+
throw error;
|
|
8201
9197
|
} finally {
|
|
8202
9198
|
await subagentController?.closeAll();
|
|
8203
9199
|
}
|
|
@@ -8243,6 +9239,10 @@ function createSubagentController(params) {
|
|
|
8243
9239
|
config: params.resolvedSubagentConfig,
|
|
8244
9240
|
parentDepth: params.depth,
|
|
8245
9241
|
parentModel: params.resolvedSubagentConfig.model ?? params.model,
|
|
9242
|
+
forkContextMessages: normalizeForkContextMessages(params.toolLoopRequest.input),
|
|
9243
|
+
onBackgroundMessage: (message) => {
|
|
9244
|
+
params.steering?.append({ role: "user", content: message });
|
|
9245
|
+
},
|
|
8246
9246
|
buildChildInstructions: (spawnInstructions, childDepth) => buildChildInstructions(spawnInstructions, params.resolvedSubagentConfig, childDepth),
|
|
8247
9247
|
runSubagent: async (subagentRequest) => {
|
|
8248
9248
|
const childCustomTools = params.resolvedSubagentConfig.inheritTools ? params.customTools : {};
|
|
@@ -8260,7 +9260,11 @@ function createSubagentController(params) {
|
|
|
8260
9260
|
openAiReasoningEffort: params.toolLoopRequest.openAiReasoningEffort,
|
|
8261
9261
|
signal: subagentRequest.signal
|
|
8262
9262
|
},
|
|
8263
|
-
{
|
|
9263
|
+
{
|
|
9264
|
+
depth: params.depth + 1,
|
|
9265
|
+
parentRunId: params.runId,
|
|
9266
|
+
telemetry: params.telemetry
|
|
9267
|
+
}
|
|
8264
9268
|
);
|
|
8265
9269
|
}
|
|
8266
9270
|
});
|
|
@@ -8307,10 +9311,142 @@ function buildChildInstructions(spawnInstructions, config, childDepth) {
|
|
|
8307
9311
|
}
|
|
8308
9312
|
return blocks.length > 0 ? blocks.join("\n\n") : void 0;
|
|
8309
9313
|
}
|
|
9314
|
+
function normalizeForkContextMessages(input) {
|
|
9315
|
+
if (typeof input === "string") {
|
|
9316
|
+
return [{ role: "user", content: input }];
|
|
9317
|
+
}
|
|
9318
|
+
return input.map((message) => ({
|
|
9319
|
+
role: message.role,
|
|
9320
|
+
content: Array.isArray(message.content) ? [...message.content] : message.content
|
|
9321
|
+
}));
|
|
9322
|
+
}
|
|
8310
9323
|
function trimToUndefined2(value) {
|
|
8311
9324
|
const trimmed = value?.trim();
|
|
8312
9325
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
8313
9326
|
}
|
|
9327
|
+
function randomRunId() {
|
|
9328
|
+
return (0, import_node_crypto3.randomBytes)(8).toString("hex");
|
|
9329
|
+
}
|
|
9330
|
+
function toIsoNow() {
|
|
9331
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9332
|
+
}
|
|
9333
|
+
function toErrorMessage2(error) {
|
|
9334
|
+
if (error instanceof Error && error.message) {
|
|
9335
|
+
return error.message;
|
|
9336
|
+
}
|
|
9337
|
+
if (typeof error === "string") {
|
|
9338
|
+
return error;
|
|
9339
|
+
}
|
|
9340
|
+
return "Unknown error";
|
|
9341
|
+
}
|
|
9342
|
+
function countToolCalls(result) {
|
|
9343
|
+
let count = 0;
|
|
9344
|
+
for (const step of result.steps) {
|
|
9345
|
+
count += step.toolCalls.length;
|
|
9346
|
+
}
|
|
9347
|
+
return count;
|
|
9348
|
+
}
|
|
9349
|
+
function sumUsageValue(current, next) {
|
|
9350
|
+
if (typeof next !== "number" || !Number.isFinite(next)) {
|
|
9351
|
+
return current;
|
|
9352
|
+
}
|
|
9353
|
+
const normalizedNext = Math.max(0, next);
|
|
9354
|
+
if (typeof current !== "number" || !Number.isFinite(current)) {
|
|
9355
|
+
return normalizedNext;
|
|
9356
|
+
}
|
|
9357
|
+
return Math.max(0, current) + normalizedNext;
|
|
9358
|
+
}
|
|
9359
|
+
function summarizeResultUsage(result) {
|
|
9360
|
+
let summary;
|
|
9361
|
+
for (const step of result.steps) {
|
|
9362
|
+
const usage = step.usage;
|
|
9363
|
+
if (!usage) {
|
|
9364
|
+
continue;
|
|
9365
|
+
}
|
|
9366
|
+
summary = {
|
|
9367
|
+
promptTokens: sumUsageValue(summary?.promptTokens, usage.promptTokens),
|
|
9368
|
+
cachedTokens: sumUsageValue(summary?.cachedTokens, usage.cachedTokens),
|
|
9369
|
+
responseTokens: sumUsageValue(summary?.responseTokens, usage.responseTokens),
|
|
9370
|
+
responseImageTokens: sumUsageValue(summary?.responseImageTokens, usage.responseImageTokens),
|
|
9371
|
+
thinkingTokens: sumUsageValue(summary?.thinkingTokens, usage.thinkingTokens),
|
|
9372
|
+
totalTokens: sumUsageValue(summary?.totalTokens, usage.totalTokens),
|
|
9373
|
+
toolUsePromptTokens: sumUsageValue(summary?.toolUsePromptTokens, usage.toolUsePromptTokens)
|
|
9374
|
+
};
|
|
9375
|
+
}
|
|
9376
|
+
return summary;
|
|
9377
|
+
}
|
|
9378
|
+
function isPromiseLike(value) {
|
|
9379
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
9380
|
+
}
|
|
9381
|
+
function isAgentTelemetrySink(value) {
|
|
9382
|
+
return typeof value === "object" && value !== null && typeof value.emit === "function";
|
|
9383
|
+
}
|
|
9384
|
+
function resolveTelemetrySelection(telemetry) {
|
|
9385
|
+
if (!telemetry) {
|
|
9386
|
+
return void 0;
|
|
9387
|
+
}
|
|
9388
|
+
if (isAgentTelemetrySink(telemetry)) {
|
|
9389
|
+
return { sink: telemetry };
|
|
9390
|
+
}
|
|
9391
|
+
if (isAgentTelemetrySink(telemetry.sink)) {
|
|
9392
|
+
return telemetry;
|
|
9393
|
+
}
|
|
9394
|
+
throw new Error("Invalid runAgentLoop telemetry config: expected a sink with emit(event).");
|
|
9395
|
+
}
|
|
9396
|
+
function createAgentTelemetrySession(telemetry) {
|
|
9397
|
+
const config = resolveTelemetrySelection(telemetry);
|
|
9398
|
+
if (!config) {
|
|
9399
|
+
return void 0;
|
|
9400
|
+
}
|
|
9401
|
+
const pending = /* @__PURE__ */ new Set();
|
|
9402
|
+
const trackPromise = (promise) => {
|
|
9403
|
+
pending.add(promise);
|
|
9404
|
+
promise.finally(() => {
|
|
9405
|
+
pending.delete(promise);
|
|
9406
|
+
});
|
|
9407
|
+
};
|
|
9408
|
+
const emit = (event) => {
|
|
9409
|
+
try {
|
|
9410
|
+
const output = config.sink.emit(event);
|
|
9411
|
+
if (isPromiseLike(output)) {
|
|
9412
|
+
const task = Promise.resolve(output).then(() => void 0).catch(() => void 0);
|
|
9413
|
+
trackPromise(task);
|
|
9414
|
+
}
|
|
9415
|
+
} catch {
|
|
9416
|
+
}
|
|
9417
|
+
};
|
|
9418
|
+
const flush = async () => {
|
|
9419
|
+
while (pending.size > 0) {
|
|
9420
|
+
await Promise.allSettled([...pending]);
|
|
9421
|
+
}
|
|
9422
|
+
if (typeof config.sink.flush === "function") {
|
|
9423
|
+
try {
|
|
9424
|
+
await config.sink.flush();
|
|
9425
|
+
} catch {
|
|
9426
|
+
}
|
|
9427
|
+
}
|
|
9428
|
+
};
|
|
9429
|
+
return {
|
|
9430
|
+
includeLlmStreamEvents: config.includeLlmStreamEvents === true,
|
|
9431
|
+
emit,
|
|
9432
|
+
flush
|
|
9433
|
+
};
|
|
9434
|
+
}
|
|
9435
|
+
function createAgentTelemetryEmitter(params) {
|
|
9436
|
+
return (event) => {
|
|
9437
|
+
if (!params.session) {
|
|
9438
|
+
return;
|
|
9439
|
+
}
|
|
9440
|
+
params.session.emit({
|
|
9441
|
+
...event,
|
|
9442
|
+
timestamp: toIsoNow(),
|
|
9443
|
+
runId: params.runId,
|
|
9444
|
+
...params.parentRunId ? { parentRunId: params.parentRunId } : {},
|
|
9445
|
+
depth: params.depth,
|
|
9446
|
+
model: params.model
|
|
9447
|
+
});
|
|
9448
|
+
};
|
|
9449
|
+
}
|
|
8314
9450
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8315
9451
|
0 && (module.exports = {
|
|
8316
9452
|
CHATGPT_MODEL_IDS,
|
|
@@ -8334,6 +9470,7 @@ function trimToUndefined2(value) {
|
|
|
8334
9470
|
appendMarkdownSourcesSection,
|
|
8335
9471
|
applyPatch,
|
|
8336
9472
|
configureGemini,
|
|
9473
|
+
configureModelConcurrency,
|
|
8337
9474
|
convertGooglePartsToLlmParts,
|
|
8338
9475
|
createApplyPatchTool,
|
|
8339
9476
|
createCodexApplyPatchTool,
|
|
@@ -8353,6 +9490,7 @@ function trimToUndefined2(value) {
|
|
|
8353
9490
|
createReadFilesTool,
|
|
8354
9491
|
createReplaceTool,
|
|
8355
9492
|
createRgSearchTool,
|
|
9493
|
+
createToolLoopSteeringChannel,
|
|
8356
9494
|
createWriteFileTool,
|
|
8357
9495
|
customTool,
|
|
8358
9496
|
encodeChatGptAuthJson,
|
|
@@ -8378,13 +9516,16 @@ function trimToUndefined2(value) {
|
|
|
8378
9516
|
loadLocalEnv,
|
|
8379
9517
|
parseJsonFromLlmText,
|
|
8380
9518
|
refreshChatGptOauthToken,
|
|
9519
|
+
resetModelConcurrencyConfig,
|
|
8381
9520
|
resolveFilesystemToolProfile,
|
|
8382
9521
|
resolveFireworksModelId,
|
|
8383
9522
|
runAgentLoop,
|
|
8384
9523
|
runToolLoop,
|
|
8385
9524
|
sanitisePartForLogging,
|
|
9525
|
+
streamAgentLoop,
|
|
8386
9526
|
streamJson,
|
|
8387
9527
|
streamText,
|
|
9528
|
+
streamToolLoop,
|
|
8388
9529
|
stripCodexCitationMarkers,
|
|
8389
9530
|
toGeminiJsonSchema,
|
|
8390
9531
|
tool
|