@ljoukov/llm 3.0.6 → 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 +70 -0
- package/dist/index.cjs +1415 -816
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -19
- package/dist/index.d.ts +81 -19
- package/dist/index.js +1412 -816
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -71,6 +71,7 @@ __export(index_exports, {
|
|
|
71
71
|
createReadFilesTool: () => createReadFilesTool,
|
|
72
72
|
createReplaceTool: () => createReplaceTool,
|
|
73
73
|
createRgSearchTool: () => createRgSearchTool,
|
|
74
|
+
createToolLoopSteeringChannel: () => createToolLoopSteeringChannel,
|
|
74
75
|
createWriteFileTool: () => createWriteFileTool,
|
|
75
76
|
customTool: () => customTool,
|
|
76
77
|
encodeChatGptAuthJson: () => encodeChatGptAuthJson,
|
|
@@ -102,8 +103,10 @@ __export(index_exports, {
|
|
|
102
103
|
runAgentLoop: () => runAgentLoop,
|
|
103
104
|
runToolLoop: () => runToolLoop,
|
|
104
105
|
sanitisePartForLogging: () => sanitisePartForLogging,
|
|
106
|
+
streamAgentLoop: () => streamAgentLoop,
|
|
105
107
|
streamJson: () => streamJson,
|
|
106
108
|
streamText: () => streamText,
|
|
109
|
+
streamToolLoop: () => streamToolLoop,
|
|
107
110
|
stripCodexCitationMarkers: () => stripCodexCitationMarkers,
|
|
108
111
|
toGeminiJsonSchema: () => toGeminiJsonSchema,
|
|
109
112
|
tool: () => tool
|
|
@@ -4908,6 +4911,102 @@ var DEFAULT_TOOL_LOOP_MAX_STEPS = 8;
|
|
|
4908
4911
|
function resolveToolLoopContents(input) {
|
|
4909
4912
|
return resolveTextContents(input);
|
|
4910
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
|
+
}
|
|
4911
5010
|
function isCustomTool(toolDef) {
|
|
4912
5011
|
return toolDef.type === "custom";
|
|
4913
5012
|
}
|
|
@@ -5026,451 +5125,817 @@ async function runToolLoop(request) {
|
|
|
5026
5125
|
}
|
|
5027
5126
|
const maxSteps = Math.max(1, Math.floor(request.maxSteps ?? DEFAULT_TOOL_LOOP_MAX_STEPS));
|
|
5028
5127
|
const providerInfo = resolveProvider(request.model);
|
|
5128
|
+
const steeringInternal = resolveToolLoopSteeringInternal(request.steering);
|
|
5029
5129
|
const steps = [];
|
|
5030
5130
|
let totalCostUsd = 0;
|
|
5031
5131
|
let finalText = "";
|
|
5032
5132
|
let finalThoughts = "";
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
const reasoning = {
|
|
5046
|
-
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5047
|
-
summary: "detailed"
|
|
5048
|
-
};
|
|
5049
|
-
let previousResponseId;
|
|
5050
|
-
let input = toOpenAiInput(contents);
|
|
5051
|
-
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5052
|
-
const turn = stepIndex + 1;
|
|
5053
|
-
const stepStartedAtMs = Date.now();
|
|
5054
|
-
let firstModelEventAtMs;
|
|
5055
|
-
let schedulerMetrics;
|
|
5056
|
-
const abortController = new AbortController();
|
|
5057
|
-
if (request.signal) {
|
|
5058
|
-
if (request.signal.aborted) {
|
|
5059
|
-
abortController.abort(request.signal.reason);
|
|
5060
|
-
} else {
|
|
5061
|
-
request.signal.addEventListener(
|
|
5062
|
-
"abort",
|
|
5063
|
-
() => abortController.abort(request.signal?.reason),
|
|
5064
|
-
{ once: true }
|
|
5065
|
-
);
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
|
-
const onEvent = request.onEvent;
|
|
5069
|
-
let modelVersion = request.model;
|
|
5070
|
-
let usageTokens;
|
|
5071
|
-
const emitEvent = (ev) => {
|
|
5072
|
-
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)
|
|
5073
5145
|
};
|
|
5074
|
-
const
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
}
|
|
5146
|
+
const reasoning = {
|
|
5147
|
+
effort: toOpenAiReasoningEffort(reasoningEffort),
|
|
5148
|
+
summary: "detailed"
|
|
5078
5149
|
};
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
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();
|
|
5222
|
+
},
|
|
5223
|
+
providerInfo.model,
|
|
5224
|
+
{
|
|
5225
|
+
onSettled: (metrics) => {
|
|
5226
|
+
schedulerMetrics = metrics;
|
|
5116
5227
|
}
|
|
5117
5228
|
}
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
{
|
|
5122
|
-
|
|
5123
|
-
|
|
5229
|
+
);
|
|
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 };
|
|
5124
5281
|
}
|
|
5282
|
+
previousResponseId = finalResponse.id;
|
|
5283
|
+
input = steeringItems2;
|
|
5284
|
+
continue;
|
|
5125
5285
|
}
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
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({
|
|
5153
5379
|
stepStartedAtMs,
|
|
5154
|
-
stepCompletedAtMs
|
|
5380
|
+
stepCompletedAtMs,
|
|
5155
5381
|
modelCompletedAtMs,
|
|
5156
5382
|
firstModelEventAtMs,
|
|
5157
5383
|
schedulerMetrics,
|
|
5158
|
-
toolExecutionMs
|
|
5159
|
-
waitToolMs
|
|
5384
|
+
toolExecutionMs,
|
|
5385
|
+
waitToolMs
|
|
5160
5386
|
});
|
|
5161
5387
|
steps.push({
|
|
5162
5388
|
step: steps.length + 1,
|
|
5163
5389
|
modelVersion,
|
|
5164
5390
|
text: responseText || void 0,
|
|
5165
5391
|
thoughts: reasoningSummary || void 0,
|
|
5166
|
-
toolCalls:
|
|
5392
|
+
toolCalls: stepToolCalls,
|
|
5167
5393
|
usage: usageTokens,
|
|
5168
5394
|
costUsd: stepCostUsd,
|
|
5169
|
-
timing
|
|
5395
|
+
timing
|
|
5170
5396
|
});
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
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"
|
|
5199
5442
|
},
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
});
|
|
5208
|
-
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 });
|
|
5209
5451
|
}
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
const
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
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 });
|
|
5222
5471
|
}
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
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
|
|
5228
5486
|
});
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
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
|
|
5234
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;
|
|
5235
5503
|
}
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
if (providerInfo.provider === "chatgpt") {
|
|
5263
|
-
const openAiAgentTools = buildOpenAiToolsFromToolSet(request.tools);
|
|
5264
|
-
const openAiNativeTools = toOpenAiTools(request.modelTools);
|
|
5265
|
-
const openAiTools = openAiNativeTools ? [...openAiNativeTools, ...openAiAgentTools] : [...openAiAgentTools];
|
|
5266
|
-
const reasoningEffort = resolveOpenAiReasoningEffort(
|
|
5267
|
-
request.model,
|
|
5268
|
-
request.openAiReasoningEffort
|
|
5269
|
-
);
|
|
5270
|
-
const toolLoopInput = toChatGptInput(contents);
|
|
5271
|
-
const conversationId = `tool-loop-${(0, import_node_crypto.randomBytes)(8).toString("hex")}`;
|
|
5272
|
-
const promptCacheKey = conversationId;
|
|
5273
|
-
let input = [...toolLoopInput.input];
|
|
5274
|
-
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5275
|
-
const turn = stepIndex + 1;
|
|
5276
|
-
const stepStartedAtMs = Date.now();
|
|
5277
|
-
let firstModelEventAtMs;
|
|
5278
|
-
const markFirstModelEvent = () => {
|
|
5279
|
-
if (firstModelEventAtMs === void 0) {
|
|
5280
|
-
firstModelEventAtMs = Date.now();
|
|
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
|
+
});
|
|
5281
5530
|
}
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
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;
|
|
5307
5561
|
}
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
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
|
+
});
|
|
5311
5604
|
}
|
|
5312
5605
|
}
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
const modelVersion = response.model ? `chatgpt-${response.model}` : request.model;
|
|
5316
|
-
const usageTokens = extractChatGptUsageTokens(response.usage);
|
|
5317
|
-
const stepCostUsd = estimateCallCostUsd({
|
|
5318
|
-
modelId: modelVersion,
|
|
5319
|
-
tokens: usageTokens,
|
|
5320
|
-
responseImages: 0
|
|
5321
|
-
});
|
|
5322
|
-
totalCostUsd += stepCostUsd;
|
|
5323
|
-
const responseText = (response.text ?? "").trim();
|
|
5324
|
-
const reasoningSummaryText = (response.reasoningSummaryText ?? "").trim();
|
|
5325
|
-
const responseToolCalls = response.toolCalls ?? [];
|
|
5326
|
-
if (responseToolCalls.length === 0) {
|
|
5327
|
-
finalText = responseText;
|
|
5328
|
-
finalThoughts = reasoningSummaryText;
|
|
5329
|
-
const stepCompletedAtMs2 = Date.now();
|
|
5330
|
-
const timing2 = buildStepTiming({
|
|
5606
|
+
const stepCompletedAtMs = Date.now();
|
|
5607
|
+
const timing = buildStepTiming({
|
|
5331
5608
|
stepStartedAtMs,
|
|
5332
|
-
stepCompletedAtMs
|
|
5609
|
+
stepCompletedAtMs,
|
|
5333
5610
|
modelCompletedAtMs,
|
|
5334
5611
|
firstModelEventAtMs,
|
|
5335
|
-
toolExecutionMs
|
|
5336
|
-
waitToolMs
|
|
5612
|
+
toolExecutionMs,
|
|
5613
|
+
waitToolMs
|
|
5337
5614
|
});
|
|
5338
5615
|
steps.push({
|
|
5339
5616
|
step: steps.length + 1,
|
|
5340
5617
|
modelVersion,
|
|
5341
5618
|
text: responseText || void 0,
|
|
5342
5619
|
thoughts: reasoningSummaryText || void 0,
|
|
5343
|
-
toolCalls
|
|
5620
|
+
toolCalls,
|
|
5344
5621
|
usage: usageTokens,
|
|
5345
5622
|
costUsd: stepCostUsd,
|
|
5346
|
-
timing
|
|
5623
|
+
timing
|
|
5347
5624
|
});
|
|
5348
|
-
|
|
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);
|
|
5349
5628
|
}
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
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;
|
|
5382
5660
|
}
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
toolExecutionMs += callDurationMs;
|
|
5392
|
-
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5393
|
-
waitToolMs += callDurationMs;
|
|
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" });
|
|
5394
5669
|
}
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
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
|
|
5403
5688
|
});
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
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
|
|
5408
5704
|
});
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
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
|
|
5417
5714
|
});
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
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
|
|
5422
5742
|
});
|
|
5423
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
|
+
}
|
|
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));
|
|
5833
|
+
}
|
|
5424
5834
|
}
|
|
5425
|
-
|
|
5426
|
-
const timing = buildStepTiming({
|
|
5427
|
-
stepStartedAtMs,
|
|
5428
|
-
stepCompletedAtMs,
|
|
5429
|
-
modelCompletedAtMs,
|
|
5430
|
-
firstModelEventAtMs,
|
|
5431
|
-
toolExecutionMs,
|
|
5432
|
-
waitToolMs
|
|
5433
|
-
});
|
|
5434
|
-
steps.push({
|
|
5435
|
-
step: steps.length + 1,
|
|
5436
|
-
modelVersion,
|
|
5437
|
-
text: responseText || void 0,
|
|
5438
|
-
thoughts: reasoningSummaryText || void 0,
|
|
5439
|
-
toolCalls,
|
|
5440
|
-
usage: usageTokens,
|
|
5441
|
-
costUsd: stepCostUsd,
|
|
5442
|
-
timing
|
|
5443
|
-
});
|
|
5444
|
-
input = input.concat(toolOutputs);
|
|
5445
|
-
}
|
|
5446
|
-
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5447
|
-
}
|
|
5448
|
-
if (providerInfo.provider === "fireworks") {
|
|
5449
|
-
if (request.modelTools && request.modelTools.length > 0) {
|
|
5450
|
-
throw new Error(
|
|
5451
|
-
"Fireworks provider does not support provider-native modelTools in runToolLoop."
|
|
5452
|
-
);
|
|
5835
|
+
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
5453
5836
|
}
|
|
5454
|
-
const
|
|
5455
|
-
const
|
|
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);
|
|
5456
5841
|
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
5457
|
-
const turn = stepIndex + 1;
|
|
5458
5842
|
const stepStartedAtMs = Date.now();
|
|
5843
|
+
let firstModelEventAtMs;
|
|
5459
5844
|
let schedulerMetrics;
|
|
5460
|
-
const
|
|
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(
|
|
5461
5862
|
async (client) => {
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
);
|
|
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
|
+
};
|
|
5472
5937
|
},
|
|
5473
|
-
|
|
5938
|
+
request.model,
|
|
5474
5939
|
{
|
|
5475
5940
|
onSettled: (metrics) => {
|
|
5476
5941
|
schedulerMetrics = metrics;
|
|
@@ -5478,41 +5943,24 @@ async function runToolLoop(request) {
|
|
|
5478
5943
|
}
|
|
5479
5944
|
);
|
|
5480
5945
|
const modelCompletedAtMs = Date.now();
|
|
5481
|
-
const
|
|
5482
|
-
|
|
5483
|
-
const choice = Array.isArray(response.choices) ? response.choices[0] : void 0;
|
|
5484
|
-
if (choice?.finish_reason === "content_filter") {
|
|
5485
|
-
request.onEvent?.({ type: "blocked" });
|
|
5486
|
-
}
|
|
5487
|
-
const message = choice?.message;
|
|
5488
|
-
const responseText = extractFireworksMessageText(message).trim();
|
|
5489
|
-
if (responseText.length > 0) {
|
|
5490
|
-
request.onEvent?.({ type: "delta", channel: "response", text: responseText });
|
|
5491
|
-
}
|
|
5492
|
-
const usageTokens = extractFireworksUsageTokens(response.usage);
|
|
5946
|
+
const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
5947
|
+
const modelVersion = response.modelVersion ?? request.model;
|
|
5493
5948
|
const stepCostUsd = estimateCallCostUsd({
|
|
5494
5949
|
modelId: modelVersion,
|
|
5495
5950
|
tokens: usageTokens,
|
|
5496
5951
|
responseImages: 0
|
|
5497
5952
|
});
|
|
5498
5953
|
totalCostUsd += stepCostUsd;
|
|
5499
|
-
if (
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
costUsd: stepCostUsd,
|
|
5504
|
-
modelVersion
|
|
5505
|
-
});
|
|
5506
|
-
}
|
|
5507
|
-
const responseToolCalls = extractFireworksToolCalls(message);
|
|
5508
|
-
if (responseToolCalls.length === 0) {
|
|
5509
|
-
finalText = responseText;
|
|
5510
|
-
finalThoughts = "";
|
|
5954
|
+
if (response.functionCalls.length === 0) {
|
|
5955
|
+
const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
|
|
5956
|
+
finalText = response.responseText.trim();
|
|
5957
|
+
finalThoughts = response.thoughtsText.trim();
|
|
5511
5958
|
const stepCompletedAtMs2 = Date.now();
|
|
5512
5959
|
const timing2 = buildStepTiming({
|
|
5513
5960
|
stepStartedAtMs,
|
|
5514
5961
|
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5515
5962
|
modelCompletedAtMs,
|
|
5963
|
+
firstModelEventAtMs,
|
|
5516
5964
|
schedulerMetrics,
|
|
5517
5965
|
toolExecutionMs: 0,
|
|
5518
5966
|
waitToolMs: 0
|
|
@@ -5520,22 +5968,65 @@ async function runToolLoop(request) {
|
|
|
5520
5968
|
steps.push({
|
|
5521
5969
|
step: steps.length + 1,
|
|
5522
5970
|
modelVersion,
|
|
5523
|
-
text:
|
|
5524
|
-
thoughts: void 0,
|
|
5971
|
+
text: finalText || void 0,
|
|
5972
|
+
thoughts: finalThoughts || void 0,
|
|
5525
5973
|
toolCalls: [],
|
|
5526
5974
|
usage: usageTokens,
|
|
5527
5975
|
costUsd: stepCostUsd,
|
|
5528
5976
|
timing: timing2
|
|
5529
5977
|
});
|
|
5530
|
-
|
|
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;
|
|
5991
|
+
}
|
|
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 });
|
|
5531
6007
|
}
|
|
5532
|
-
const
|
|
5533
|
-
const callInputs =
|
|
6008
|
+
const responseParts = [];
|
|
6009
|
+
const callInputs = response.functionCalls.map((call, index) => {
|
|
6010
|
+
const turn = stepIndex + 1;
|
|
5534
6011
|
const toolIndex = index + 1;
|
|
5535
6012
|
const toolId = buildToolLogId(turn, toolIndex);
|
|
5536
|
-
const
|
|
5537
|
-
|
|
6013
|
+
const toolName = call.name ?? "unknown";
|
|
6014
|
+
const rawInput = call.args ?? {};
|
|
6015
|
+
return { call, toolName, rawInput, toolId, turn, toolIndex };
|
|
5538
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
|
+
}
|
|
5539
6030
|
const callResults = await Promise.all(
|
|
5540
6031
|
callInputs.map(async (entry) => {
|
|
5541
6032
|
return await toolCallContextStorage.run(
|
|
@@ -5550,44 +6041,51 @@ async function runToolLoop(request) {
|
|
|
5550
6041
|
callKind: "function",
|
|
5551
6042
|
toolName: entry.toolName,
|
|
5552
6043
|
tool: request.tools[entry.toolName],
|
|
5553
|
-
rawInput: entry.
|
|
5554
|
-
parseError: entry.parseError
|
|
6044
|
+
rawInput: entry.rawInput
|
|
5555
6045
|
});
|
|
5556
6046
|
return { entry, result, outputPayload };
|
|
5557
6047
|
}
|
|
5558
6048
|
);
|
|
5559
6049
|
})
|
|
5560
6050
|
);
|
|
5561
|
-
const assistantToolCalls = [];
|
|
5562
|
-
const toolMessages = [];
|
|
5563
6051
|
let toolExecutionMs = 0;
|
|
5564
6052
|
let waitToolMs = 0;
|
|
5565
6053
|
for (const { entry, result, outputPayload } of callResults) {
|
|
5566
|
-
|
|
6054
|
+
toolCalls.push({ ...result, callId: entry.call.id });
|
|
5567
6055
|
const callDurationMs = toToolResultDuration(result);
|
|
5568
6056
|
toolExecutionMs += callDurationMs;
|
|
5569
6057
|
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5570
6058
|
waitToolMs += callDurationMs;
|
|
5571
6059
|
}
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
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: {
|
|
5576
6077
|
name: entry.toolName,
|
|
5577
|
-
|
|
6078
|
+
response: responsePayload,
|
|
6079
|
+
...entry.call.id ? { id: entry.call.id } : {}
|
|
5578
6080
|
}
|
|
5579
6081
|
});
|
|
5580
|
-
toolMessages.push({
|
|
5581
|
-
role: "tool",
|
|
5582
|
-
tool_call_id: entry.call.id,
|
|
5583
|
-
content: mergeToolOutput(outputPayload)
|
|
5584
|
-
});
|
|
5585
6082
|
}
|
|
5586
6083
|
const stepCompletedAtMs = Date.now();
|
|
5587
6084
|
const timing = buildStepTiming({
|
|
5588
6085
|
stepStartedAtMs,
|
|
5589
6086
|
stepCompletedAtMs,
|
|
5590
6087
|
modelCompletedAtMs,
|
|
6088
|
+
firstModelEventAtMs,
|
|
5591
6089
|
schedulerMetrics,
|
|
5592
6090
|
toolExecutionMs,
|
|
5593
6091
|
waitToolMs
|
|
@@ -5595,251 +6093,82 @@ async function runToolLoop(request) {
|
|
|
5595
6093
|
steps.push({
|
|
5596
6094
|
step: steps.length + 1,
|
|
5597
6095
|
modelVersion,
|
|
5598
|
-
text: responseText || void 0,
|
|
5599
|
-
thoughts: void 0,
|
|
5600
|
-
toolCalls
|
|
6096
|
+
text: response.responseText.trim() || void 0,
|
|
6097
|
+
thoughts: response.thoughtsText.trim() || void 0,
|
|
6098
|
+
toolCalls,
|
|
5601
6099
|
usage: usageTokens,
|
|
5602
6100
|
costUsd: stepCostUsd,
|
|
5603
6101
|
timing
|
|
5604
6102
|
});
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
}
|
|
5610
|
-
messages.push(...toolMessages);
|
|
6103
|
+
geminiContents.push({ role: "user", parts: responseParts });
|
|
6104
|
+
const steeringInput = steeringInternal?.drainPendingContents() ?? [];
|
|
6105
|
+
if (steeringInput.length > 0) {
|
|
6106
|
+
geminiContents.push(...steeringInput.map(convertLlmContentToGeminiContent));
|
|
6107
|
+
}
|
|
5611
6108
|
}
|
|
5612
6109
|
throw new Error(`Tool loop exceeded max steps (${maxSteps}) without final response.`);
|
|
6110
|
+
} finally {
|
|
6111
|
+
steeringInternal?.close();
|
|
5613
6112
|
}
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
};
|
|
5627
|
-
const config = {
|
|
5628
|
-
maxOutputTokens: 32e3,
|
|
5629
|
-
tools: geminiTools,
|
|
5630
|
-
toolConfig: {
|
|
5631
|
-
functionCallingConfig: {
|
|
5632
|
-
mode: import_genai2.FunctionCallingConfigMode.VALIDATED
|
|
5633
|
-
}
|
|
5634
|
-
},
|
|
5635
|
-
thinkingConfig: resolveGeminiThinkingConfig(request.model)
|
|
5636
|
-
};
|
|
5637
|
-
const onEvent = request.onEvent;
|
|
5638
|
-
const response = await runGeminiCall(
|
|
5639
|
-
async (client) => {
|
|
5640
|
-
const stream = await client.models.generateContentStream({
|
|
5641
|
-
model: request.model,
|
|
5642
|
-
contents: geminiContents,
|
|
5643
|
-
config
|
|
5644
|
-
});
|
|
5645
|
-
let responseText = "";
|
|
5646
|
-
let thoughtsText = "";
|
|
5647
|
-
const modelParts = [];
|
|
5648
|
-
const functionCalls = [];
|
|
5649
|
-
const seenFunctionCallIds = /* @__PURE__ */ new Set();
|
|
5650
|
-
const seenFunctionCallKeys = /* @__PURE__ */ new Set();
|
|
5651
|
-
let latestUsageMetadata;
|
|
5652
|
-
let resolvedModelVersion;
|
|
5653
|
-
for await (const chunk of stream) {
|
|
5654
|
-
markFirstModelEvent();
|
|
5655
|
-
if (chunk.modelVersion) {
|
|
5656
|
-
resolvedModelVersion = chunk.modelVersion;
|
|
5657
|
-
onEvent?.({ type: "model", modelVersion: chunk.modelVersion });
|
|
5658
|
-
}
|
|
5659
|
-
if (chunk.usageMetadata) {
|
|
5660
|
-
latestUsageMetadata = chunk.usageMetadata;
|
|
5661
|
-
}
|
|
5662
|
-
const candidates = chunk.candidates;
|
|
5663
|
-
if (!candidates || candidates.length === 0) {
|
|
5664
|
-
continue;
|
|
5665
|
-
}
|
|
5666
|
-
const primary = candidates[0];
|
|
5667
|
-
const parts = primary?.content?.parts;
|
|
5668
|
-
if (!parts || parts.length === 0) {
|
|
5669
|
-
continue;
|
|
5670
|
-
}
|
|
5671
|
-
for (const part of parts) {
|
|
5672
|
-
modelParts.push(part);
|
|
5673
|
-
const call = part.functionCall;
|
|
5674
|
-
if (call) {
|
|
5675
|
-
const id = typeof call.id === "string" ? call.id : "";
|
|
5676
|
-
const shouldAdd = (() => {
|
|
5677
|
-
if (id.length > 0) {
|
|
5678
|
-
if (seenFunctionCallIds.has(id)) {
|
|
5679
|
-
return false;
|
|
5680
|
-
}
|
|
5681
|
-
seenFunctionCallIds.add(id);
|
|
5682
|
-
return true;
|
|
5683
|
-
}
|
|
5684
|
-
const key = JSON.stringify({ name: call.name ?? "", args: call.args ?? null });
|
|
5685
|
-
if (seenFunctionCallKeys.has(key)) {
|
|
5686
|
-
return false;
|
|
5687
|
-
}
|
|
5688
|
-
seenFunctionCallKeys.add(key);
|
|
5689
|
-
return true;
|
|
5690
|
-
})();
|
|
5691
|
-
if (shouldAdd) {
|
|
5692
|
-
functionCalls.push(call);
|
|
5693
|
-
}
|
|
5694
|
-
}
|
|
5695
|
-
if (typeof part.text === "string" && part.text.length > 0) {
|
|
5696
|
-
if (part.thought) {
|
|
5697
|
-
thoughtsText += part.text;
|
|
5698
|
-
onEvent?.({ type: "delta", channel: "thought", text: part.text });
|
|
5699
|
-
} else {
|
|
5700
|
-
responseText += part.text;
|
|
5701
|
-
onEvent?.({ type: "delta", channel: "response", text: part.text });
|
|
5702
|
-
}
|
|
5703
|
-
}
|
|
5704
|
-
}
|
|
5705
|
-
}
|
|
5706
|
-
return {
|
|
5707
|
-
responseText,
|
|
5708
|
-
thoughtsText,
|
|
5709
|
-
functionCalls,
|
|
5710
|
-
modelParts,
|
|
5711
|
-
usageMetadata: latestUsageMetadata,
|
|
5712
|
-
modelVersion: resolvedModelVersion ?? request.model
|
|
5713
|
-
};
|
|
5714
|
-
},
|
|
5715
|
-
request.model,
|
|
5716
|
-
{
|
|
5717
|
-
onSettled: (metrics) => {
|
|
5718
|
-
schedulerMetrics = metrics;
|
|
5719
|
-
}
|
|
5720
|
-
}
|
|
5721
|
-
);
|
|
5722
|
-
const modelCompletedAtMs = Date.now();
|
|
5723
|
-
const usageTokens = extractGeminiUsageTokens(response.usageMetadata);
|
|
5724
|
-
const modelVersion = response.modelVersion ?? request.model;
|
|
5725
|
-
const stepCostUsd = estimateCallCostUsd({
|
|
5726
|
-
modelId: modelVersion,
|
|
5727
|
-
tokens: usageTokens,
|
|
5728
|
-
responseImages: 0
|
|
5729
|
-
});
|
|
5730
|
-
totalCostUsd += stepCostUsd;
|
|
5731
|
-
if (response.functionCalls.length === 0) {
|
|
5732
|
-
finalText = response.responseText.trim();
|
|
5733
|
-
finalThoughts = response.thoughtsText.trim();
|
|
5734
|
-
const stepCompletedAtMs2 = Date.now();
|
|
5735
|
-
const timing2 = buildStepTiming({
|
|
5736
|
-
stepStartedAtMs,
|
|
5737
|
-
stepCompletedAtMs: stepCompletedAtMs2,
|
|
5738
|
-
modelCompletedAtMs,
|
|
5739
|
-
firstModelEventAtMs,
|
|
5740
|
-
schedulerMetrics,
|
|
5741
|
-
toolExecutionMs: 0,
|
|
5742
|
-
waitToolMs: 0
|
|
5743
|
-
});
|
|
5744
|
-
steps.push({
|
|
5745
|
-
step: steps.length + 1,
|
|
5746
|
-
modelVersion,
|
|
5747
|
-
text: finalText || void 0,
|
|
5748
|
-
thoughts: finalThoughts || void 0,
|
|
5749
|
-
toolCalls: [],
|
|
5750
|
-
usage: usageTokens,
|
|
5751
|
-
costUsd: stepCostUsd,
|
|
5752
|
-
timing: timing2
|
|
5753
|
-
});
|
|
5754
|
-
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);
|
|
5755
6125
|
}
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
const
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
{
|
|
5785
|
-
toolName: entry.toolName,
|
|
5786
|
-
toolId: entry.toolId,
|
|
5787
|
-
turn: entry.turn,
|
|
5788
|
-
toolIndex: entry.toolIndex
|
|
5789
|
-
},
|
|
5790
|
-
async () => {
|
|
5791
|
-
const { result, outputPayload } = await executeToolCall({
|
|
5792
|
-
callKind: "function",
|
|
5793
|
-
toolName: entry.toolName,
|
|
5794
|
-
tool: request.tools[entry.toolName],
|
|
5795
|
-
rawInput: entry.rawInput
|
|
5796
|
-
});
|
|
5797
|
-
return { entry, result, outputPayload };
|
|
5798
|
-
}
|
|
5799
|
-
);
|
|
5800
|
-
})
|
|
5801
|
-
);
|
|
5802
|
-
let toolExecutionMs = 0;
|
|
5803
|
-
let waitToolMs = 0;
|
|
5804
|
-
for (const { entry, result, outputPayload } of callResults) {
|
|
5805
|
-
toolCalls.push({ ...result, callId: entry.call.id });
|
|
5806
|
-
const callDurationMs = toToolResultDuration(result);
|
|
5807
|
-
toolExecutionMs += callDurationMs;
|
|
5808
|
-
if (entry.toolName.toLowerCase() === SUBAGENT_WAIT_TOOL_NAME) {
|
|
5809
|
-
waitToolMs += callDurationMs;
|
|
5810
|
-
}
|
|
5811
|
-
const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
|
|
5812
|
-
responseParts.push({
|
|
5813
|
-
functionResponse: {
|
|
5814
|
-
name: entry.toolName,
|
|
5815
|
-
response: responsePayload,
|
|
5816
|
-
...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);
|
|
5817
6154
|
}
|
|
5818
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;
|
|
5819
6162
|
}
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
});
|
|
5830
|
-
steps.push({
|
|
5831
|
-
step: steps.length + 1,
|
|
5832
|
-
modelVersion,
|
|
5833
|
-
text: response.responseText.trim() || void 0,
|
|
5834
|
-
thoughts: response.thoughtsText.trim() || void 0,
|
|
5835
|
-
toolCalls,
|
|
5836
|
-
usage: usageTokens,
|
|
5837
|
-
costUsd: stepCostUsd,
|
|
5838
|
-
timing
|
|
5839
|
-
});
|
|
5840
|
-
geminiContents.push({ role: "user", parts: responseParts });
|
|
5841
|
-
}
|
|
5842
|
-
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
|
+
};
|
|
5843
6172
|
}
|
|
5844
6173
|
var IMAGE_GRADE_SCHEMA = import_zod3.z.enum(["pass", "fail"]);
|
|
5845
6174
|
async function gradeGeneratedImage(params) {
|
|
@@ -6092,65 +6421,106 @@ var import_node_crypto3 = require("crypto");
|
|
|
6092
6421
|
// src/agent/subagents.ts
|
|
6093
6422
|
var import_node_crypto2 = require("crypto");
|
|
6094
6423
|
var import_zod4 = require("zod");
|
|
6095
|
-
var DEFAULT_SUBAGENT_MAX_AGENTS =
|
|
6096
|
-
var DEFAULT_SUBAGENT_MAX_DEPTH =
|
|
6097
|
-
var
|
|
6098
|
-
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;
|
|
6099
6429
|
var MAX_SUBAGENT_MAX_AGENTS = 64;
|
|
6100
6430
|
var MAX_SUBAGENT_MAX_DEPTH = 12;
|
|
6101
6431
|
var MAX_SUBAGENT_MAX_STEPS = 64;
|
|
6102
|
-
var MAX_SUBAGENT_WAIT_TIMEOUT_MS =
|
|
6432
|
+
var MAX_SUBAGENT_WAIT_TIMEOUT_MS = 36e5;
|
|
6103
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();
|
|
6104
6474
|
var subagentInputItemSchema = import_zod4.z.object({
|
|
6105
|
-
text: import_zod4.z.string().
|
|
6106
|
-
image_url: import_zod4.z.string().
|
|
6107
|
-
name: import_zod4.z.string().
|
|
6108
|
-
path: import_zod4.z.string().
|
|
6109
|
-
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()
|
|
6110
6480
|
}).passthrough();
|
|
6111
6481
|
var spawnAgentInputSchema = import_zod4.z.object({
|
|
6112
|
-
prompt: import_zod4.z.string().
|
|
6113
|
-
message: import_zod4.z.string().
|
|
6114
|
-
items: import_zod4.z.array(subagentInputItemSchema).
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
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.")
|
|
6121
6494
|
});
|
|
6122
6495
|
var sendInputSchema = import_zod4.z.object({
|
|
6123
|
-
agent_id: import_zod4.z.string().
|
|
6124
|
-
id: import_zod4.z.string().
|
|
6125
|
-
input: import_zod4.z.string().
|
|
6126
|
-
message: import_zod4.z.string().
|
|
6127
|
-
items: import_zod4.z.array(subagentInputItemSchema).
|
|
6128
|
-
|
|
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.")
|
|
6129
6504
|
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
6130
6505
|
message: "agent_id (or id) is required."
|
|
6131
|
-
}).refine((value) => Boolean(resolvePromptValue(value.input, value.message, value.items)), {
|
|
6132
|
-
message: "input (or message/items) is required."
|
|
6133
6506
|
});
|
|
6134
6507
|
var resumeAgentSchema = import_zod4.z.object({
|
|
6135
|
-
agent_id: import_zod4.z.string().
|
|
6136
|
-
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.")
|
|
6137
6510
|
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
6138
6511
|
message: "agent_id (or id) is required."
|
|
6139
6512
|
});
|
|
6140
6513
|
var waitSchema = import_zod4.z.object({
|
|
6141
|
-
agent_id: import_zod4.z.string().
|
|
6142
|
-
id: import_zod4.z.string().
|
|
6143
|
-
ids: import_zod4.z.array(import_zod4.z.string().min(1)).
|
|
6144
|
-
timeout_ms: import_zod4.z.number().int().
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
message: "agent_id/id or ids is required."
|
|
6149
|
-
}
|
|
6150
|
-
);
|
|
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
|
+
});
|
|
6151
6521
|
var closeSchema = import_zod4.z.object({
|
|
6152
|
-
agent_id: import_zod4.z.string().
|
|
6153
|
-
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).")
|
|
6154
6524
|
}).refine((value) => Boolean(resolveAgentIdValue(value.agent_id, value.id)), {
|
|
6155
6525
|
message: "agent_id (or id) is required."
|
|
6156
6526
|
});
|
|
@@ -6158,6 +6528,7 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
6158
6528
|
const defaults = {
|
|
6159
6529
|
maxAgents: DEFAULT_SUBAGENT_MAX_AGENTS,
|
|
6160
6530
|
maxDepth: DEFAULT_SUBAGENT_MAX_DEPTH,
|
|
6531
|
+
minWaitTimeoutMs: DEFAULT_SUBAGENT_MIN_WAIT_TIMEOUT_MS,
|
|
6161
6532
|
defaultWaitTimeoutMs: DEFAULT_SUBAGENT_WAIT_TIMEOUT_MS,
|
|
6162
6533
|
maxWaitTimeoutMs: DEFAULT_SUBAGENT_MAX_WAIT_TIMEOUT_MS,
|
|
6163
6534
|
promptPattern: "codex",
|
|
@@ -6178,10 +6549,16 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
6178
6549
|
MAX_SUBAGENT_MAX_AGENTS
|
|
6179
6550
|
);
|
|
6180
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
|
+
);
|
|
6181
6558
|
const defaultWaitTimeoutMs = normalizeInteger(
|
|
6182
6559
|
config.defaultWaitTimeoutMs,
|
|
6183
6560
|
defaults.defaultWaitTimeoutMs,
|
|
6184
|
-
|
|
6561
|
+
minWaitTimeoutMs,
|
|
6185
6562
|
MAX_SUBAGENT_WAIT_TIMEOUT_MS
|
|
6186
6563
|
);
|
|
6187
6564
|
const maxWaitTimeoutMs = normalizeInteger(
|
|
@@ -6198,6 +6575,7 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
6198
6575
|
enabled,
|
|
6199
6576
|
maxAgents,
|
|
6200
6577
|
maxDepth,
|
|
6578
|
+
minWaitTimeoutMs,
|
|
6201
6579
|
defaultWaitTimeoutMs,
|
|
6202
6580
|
maxWaitTimeoutMs,
|
|
6203
6581
|
promptPattern,
|
|
@@ -6211,9 +6589,11 @@ function resolveSubagentToolConfig(selection, currentDepth) {
|
|
|
6211
6589
|
function buildCodexSubagentOrchestratorInstructions(params) {
|
|
6212
6590
|
return [
|
|
6213
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.",
|
|
6214
6594
|
"Use this control pattern:",
|
|
6215
6595
|
"1. spawn_agent with a focused prompt.",
|
|
6216
|
-
"2. wait
|
|
6596
|
+
"2. wait with ids=[agent_id] until the agent reaches a non-running state. Prefer long waits (minutes).",
|
|
6217
6597
|
"3. For follow-up turns, send_input then resume_agent.",
|
|
6218
6598
|
"4. close_agent when delegation is complete.",
|
|
6219
6599
|
`Limits: max active subagents ${params.maxAgents}, max depth ${params.maxDepth}, current depth ${params.currentDepth}.`
|
|
@@ -6235,9 +6615,10 @@ function createSubagentToolController(options) {
|
|
|
6235
6615
|
};
|
|
6236
6616
|
}
|
|
6237
6617
|
const agents = /* @__PURE__ */ new Map();
|
|
6618
|
+
const roleNicknameCounts = /* @__PURE__ */ new Map();
|
|
6238
6619
|
const tools = {
|
|
6239
6620
|
spawn_agent: tool({
|
|
6240
|
-
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.",
|
|
6241
6622
|
inputSchema: spawnAgentInputSchema,
|
|
6242
6623
|
execute: async (input) => {
|
|
6243
6624
|
if (countActiveAgents(agents) >= options.config.maxAgents) {
|
|
@@ -6260,24 +6641,36 @@ function createSubagentToolController(options) {
|
|
|
6260
6641
|
}
|
|
6261
6642
|
const id = `agent_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}`;
|
|
6262
6643
|
const now = Date.now();
|
|
6263
|
-
const
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
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
|
+
});
|
|
6267
6658
|
const agent = {
|
|
6268
6659
|
id,
|
|
6269
6660
|
depth: childDepth,
|
|
6270
6661
|
model,
|
|
6662
|
+
...nickname ? { nickname } : {},
|
|
6663
|
+
agentRole: roleName,
|
|
6271
6664
|
status: "idle",
|
|
6272
6665
|
createdAtMs: now,
|
|
6273
6666
|
updatedAtMs: now,
|
|
6274
6667
|
pendingInputs: [initialPrompt],
|
|
6275
|
-
history: [],
|
|
6668
|
+
history: input.fork_context && options.forkContextMessages ? [...options.forkContextMessages] : [],
|
|
6276
6669
|
...options.buildChildInstructions ? {
|
|
6277
6670
|
instructions: trimToUndefined(
|
|
6278
|
-
options.buildChildInstructions(
|
|
6671
|
+
options.buildChildInstructions(perSpawnInstructions, childDepth)
|
|
6279
6672
|
)
|
|
6280
|
-
} :
|
|
6673
|
+
} : perSpawnInstructions ? { instructions: perSpawnInstructions } : {},
|
|
6281
6674
|
...input.max_steps ? { maxSteps: input.max_steps } : options.config.maxSteps ? { maxSteps: options.config.maxSteps } : {},
|
|
6282
6675
|
turns: 0,
|
|
6283
6676
|
notification: "spawned",
|
|
@@ -6287,41 +6680,50 @@ function createSubagentToolController(options) {
|
|
|
6287
6680
|
};
|
|
6288
6681
|
agents.set(id, agent);
|
|
6289
6682
|
startRun(agent, options);
|
|
6290
|
-
return buildToolResponse(
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6683
|
+
return buildToolResponse(
|
|
6684
|
+
agent,
|
|
6685
|
+
{
|
|
6686
|
+
notification: "spawned",
|
|
6687
|
+
message: `Spawned subagent ${id}.`
|
|
6688
|
+
},
|
|
6689
|
+
{ nickname: agent.nickname }
|
|
6690
|
+
);
|
|
6294
6691
|
}
|
|
6295
6692
|
}),
|
|
6296
6693
|
send_input: tool({
|
|
6297
|
-
description: "
|
|
6694
|
+
description: "Send a message to an existing agent. Use interrupt=true to redirect work immediately.",
|
|
6298
6695
|
inputSchema: sendInputSchema,
|
|
6299
6696
|
execute: async (input) => {
|
|
6697
|
+
const submissionId = randomSubmissionId();
|
|
6300
6698
|
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
6301
6699
|
if (!agentId) {
|
|
6302
6700
|
throw new Error("send_input requires agent_id or id.");
|
|
6303
6701
|
}
|
|
6304
6702
|
const agent = requireAgent(agents, agentId);
|
|
6305
|
-
const nextInput =
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
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
|
+
});
|
|
6309
6711
|
if (agent.status === "closed") {
|
|
6310
|
-
throw new Error(`
|
|
6712
|
+
throw new Error(`agent with id ${agent.id} is closed`);
|
|
6311
6713
|
}
|
|
6312
6714
|
if (input.interrupt && agent.abortController) {
|
|
6313
6715
|
agent.abortController.abort("send_input_interrupt");
|
|
6314
6716
|
agent.pendingInputs.unshift(nextInput);
|
|
6315
6717
|
setNotification(agent, "input_queued", `Interrupted ${agent.id} and queued new input.`);
|
|
6316
|
-
return buildToolResponse(agent);
|
|
6718
|
+
return buildToolResponse(agent, void 0, { submission_id: submissionId });
|
|
6317
6719
|
}
|
|
6318
6720
|
agent.pendingInputs.push(nextInput);
|
|
6319
6721
|
setNotification(agent, "input_queued", `Queued input for ${agent.id}.`);
|
|
6320
|
-
return buildToolResponse(agent);
|
|
6722
|
+
return buildToolResponse(agent, void 0, { submission_id: submissionId });
|
|
6321
6723
|
}
|
|
6322
6724
|
}),
|
|
6323
6725
|
resume_agent: tool({
|
|
6324
|
-
description: "
|
|
6726
|
+
description: "Resume a previously closed agent by id so it can receive send_input and wait calls.",
|
|
6325
6727
|
inputSchema: resumeAgentSchema,
|
|
6326
6728
|
execute: async (input) => {
|
|
6327
6729
|
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
@@ -6330,10 +6732,11 @@ function createSubagentToolController(options) {
|
|
|
6330
6732
|
}
|
|
6331
6733
|
const agent = requireAgent(agents, agentId);
|
|
6332
6734
|
if (agent.status === "closed") {
|
|
6333
|
-
|
|
6735
|
+
agent.status = "idle";
|
|
6736
|
+
setNotification(agent, "resumed", `Resumed subagent ${agent.id}.`);
|
|
6334
6737
|
return buildToolResponse(agent, {
|
|
6335
|
-
notification: "
|
|
6336
|
-
message: `
|
|
6738
|
+
notification: "resumed",
|
|
6739
|
+
message: `Resumed subagent ${agent.id}.`
|
|
6337
6740
|
});
|
|
6338
6741
|
}
|
|
6339
6742
|
const outcome = startRun(agent, options);
|
|
@@ -6352,41 +6755,42 @@ function createSubagentToolController(options) {
|
|
|
6352
6755
|
}
|
|
6353
6756
|
}),
|
|
6354
6757
|
wait: tool({
|
|
6355
|
-
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.",
|
|
6356
6759
|
inputSchema: waitSchema,
|
|
6357
6760
|
execute: async (input) => {
|
|
6358
|
-
const usesIdsArray = Array.isArray(input.ids) && input.ids.length > 0;
|
|
6359
6761
|
const ids = resolveAgentIdList(input.agent_id, input.id, input.ids);
|
|
6360
6762
|
if (ids.length === 0) {
|
|
6361
|
-
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");
|
|
6362
6767
|
}
|
|
6363
6768
|
const timeoutMs = normalizeInteger(
|
|
6364
6769
|
input.timeout_ms,
|
|
6365
6770
|
options.config.defaultWaitTimeoutMs,
|
|
6366
|
-
|
|
6771
|
+
options.config.minWaitTimeoutMs,
|
|
6367
6772
|
options.config.maxWaitTimeoutMs
|
|
6368
6773
|
);
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
agent,
|
|
6379
|
-
"timeout",
|
|
6380
|
-
`Timed out after ${timeoutMs}ms while waiting for ${agent.id}.`
|
|
6381
|
-
);
|
|
6382
|
-
return buildToolResponse(agent, void 0, { timed_out: true, timeout_ms: timeoutMs });
|
|
6383
|
-
}
|
|
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
|
+
);
|
|
6384
6783
|
}
|
|
6385
|
-
return
|
|
6784
|
+
return {
|
|
6785
|
+
status,
|
|
6786
|
+
status_summary: summarizeAgentStatuses(status),
|
|
6787
|
+
timed_out: timedOut,
|
|
6788
|
+
timeout_ms: timeoutMs
|
|
6789
|
+
};
|
|
6386
6790
|
}
|
|
6387
6791
|
}),
|
|
6388
6792
|
close_agent: tool({
|
|
6389
|
-
description: "
|
|
6793
|
+
description: "Close an agent when it is no longer needed and return its last known status.",
|
|
6390
6794
|
inputSchema: closeSchema,
|
|
6391
6795
|
execute: async (input) => {
|
|
6392
6796
|
const agentId = resolveAgentIdValue(input.agent_id, input.id);
|
|
@@ -6398,7 +6802,7 @@ function createSubagentToolController(options) {
|
|
|
6398
6802
|
setNotification(agent, "already_closed", `Subagent ${agent.id} is already closed.`);
|
|
6399
6803
|
return buildToolResponse(agent, void 0, { cancelled: false });
|
|
6400
6804
|
}
|
|
6401
|
-
const cancelled = closeSubagent(agent, `Closed ${agent.id}
|
|
6805
|
+
const cancelled = closeSubagent(agent, `Closed ${agent.id}.`, options);
|
|
6402
6806
|
return buildToolResponse(
|
|
6403
6807
|
agent,
|
|
6404
6808
|
{ notification: "closed", message: `Closed ${agent.id}.` },
|
|
@@ -6413,7 +6817,7 @@ function createSubagentToolController(options) {
|
|
|
6413
6817
|
const running = [];
|
|
6414
6818
|
for (const agent of agents.values()) {
|
|
6415
6819
|
if (agent.status !== "closed") {
|
|
6416
|
-
closeSubagent(agent, `Parent agent loop closed ${agent.id}
|
|
6820
|
+
closeSubagent(agent, `Parent agent loop closed ${agent.id}.`, options);
|
|
6417
6821
|
}
|
|
6418
6822
|
if (agent.runningPromise) {
|
|
6419
6823
|
running.push(agent.runningPromise);
|
|
@@ -6428,7 +6832,7 @@ function createSubagentToolController(options) {
|
|
|
6428
6832
|
function requireAgent(agents, id) {
|
|
6429
6833
|
const agent = agents.get(id);
|
|
6430
6834
|
if (!agent) {
|
|
6431
|
-
throw new Error(`
|
|
6835
|
+
throw new Error(`agent with id ${id} not found`);
|
|
6432
6836
|
}
|
|
6433
6837
|
return agent;
|
|
6434
6838
|
}
|
|
@@ -6447,17 +6851,33 @@ function resolveAgentIdList(agentId, idAlias, ids) {
|
|
|
6447
6851
|
const single = resolveAgentIdValue(agentId, idAlias);
|
|
6448
6852
|
return single ? [single] : [];
|
|
6449
6853
|
}
|
|
6450
|
-
function
|
|
6451
|
-
const
|
|
6452
|
-
|
|
6453
|
-
|
|
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);
|
|
6862
|
+
}
|
|
6863
|
+
if (!hasText && !hasItems) {
|
|
6864
|
+
throw new Error(params.missingError);
|
|
6865
|
+
}
|
|
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);
|
|
6454
6875
|
}
|
|
6455
|
-
const
|
|
6456
|
-
if (
|
|
6457
|
-
|
|
6876
|
+
const itemText = resolveInputItemsText(params.items);
|
|
6877
|
+
if (!itemText) {
|
|
6878
|
+
throw new Error(params.emptyItemsError);
|
|
6458
6879
|
}
|
|
6459
|
-
|
|
6460
|
-
return itemText ?? "";
|
|
6880
|
+
return itemText;
|
|
6461
6881
|
}
|
|
6462
6882
|
function resolveInputItemsText(items) {
|
|
6463
6883
|
if (!items || items.length === 0) {
|
|
@@ -6473,9 +6893,28 @@ function resolveInputItemsText(items) {
|
|
|
6473
6893
|
const name = typeof item.name === "string" ? item.name.trim() : "";
|
|
6474
6894
|
const path6 = typeof item.path === "string" ? item.path.trim() : "";
|
|
6475
6895
|
const imageUrl = typeof item.image_url === "string" ? item.image_url.trim() : "";
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
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}]`);
|
|
6479
6918
|
}
|
|
6480
6919
|
}
|
|
6481
6920
|
if (lines.length === 0) {
|
|
@@ -6587,6 +7026,7 @@ function startRun(agent, options) {
|
|
|
6587
7026
|
"run_completed",
|
|
6588
7027
|
`Subagent ${agent.id} completed run ${agent.turns}.`
|
|
6589
7028
|
);
|
|
7029
|
+
emitBackgroundNotification(agent, options);
|
|
6590
7030
|
} catch (error) {
|
|
6591
7031
|
if (agent.status === "closed") {
|
|
6592
7032
|
return;
|
|
@@ -6598,6 +7038,7 @@ function startRun(agent, options) {
|
|
|
6598
7038
|
const message = toErrorMessage(error);
|
|
6599
7039
|
agent.lastError = message;
|
|
6600
7040
|
setLifecycle(agent, "failed", "run_failed", `Subagent ${agent.id} failed: ${message}`);
|
|
7041
|
+
emitBackgroundNotification(agent, options);
|
|
6601
7042
|
} finally {
|
|
6602
7043
|
const runCompletedAtMs = Date.now();
|
|
6603
7044
|
agent.lastRunCompletedAtMs = runCompletedAtMs;
|
|
@@ -6609,30 +7050,16 @@ function startRun(agent, options) {
|
|
|
6609
7050
|
agent.runningPromise = runPromise;
|
|
6610
7051
|
return "started";
|
|
6611
7052
|
}
|
|
6612
|
-
function closeSubagent(agent, message) {
|
|
7053
|
+
function closeSubagent(agent, message, options) {
|
|
6613
7054
|
const cancelled = Boolean(agent.runningPromise);
|
|
6614
7055
|
agent.pendingInputs = [];
|
|
6615
7056
|
if (agent.abortController) {
|
|
6616
7057
|
agent.abortController.abort("close_agent");
|
|
6617
7058
|
}
|
|
6618
7059
|
setLifecycle(agent, "closed", "closed", message);
|
|
7060
|
+
emitBackgroundNotification(agent, options);
|
|
6619
7061
|
return cancelled;
|
|
6620
7062
|
}
|
|
6621
|
-
async function waitUntilNotRunning(agent, timeoutMs) {
|
|
6622
|
-
const deadline = Date.now() + timeoutMs;
|
|
6623
|
-
while (agent.status === "running") {
|
|
6624
|
-
const remaining = deadline - Date.now();
|
|
6625
|
-
if (remaining <= 0) {
|
|
6626
|
-
return false;
|
|
6627
|
-
}
|
|
6628
|
-
const currentVersion = agent.version;
|
|
6629
|
-
const changed = await waitForVersionChange(agent, currentVersion, remaining);
|
|
6630
|
-
if (!changed) {
|
|
6631
|
-
return false;
|
|
6632
|
-
}
|
|
6633
|
-
}
|
|
6634
|
-
return true;
|
|
6635
|
-
}
|
|
6636
7063
|
async function waitForVersionChange(agent, version, timeoutMs) {
|
|
6637
7064
|
if (agent.version !== version) {
|
|
6638
7065
|
return true;
|
|
@@ -6670,6 +7097,8 @@ function buildToolResponse(agent, override, extra = {}) {
|
|
|
6670
7097
|
function buildSnapshot(agent) {
|
|
6671
7098
|
return {
|
|
6672
7099
|
agent_id: agent.id,
|
|
7100
|
+
...agent.nickname ? { nickname: agent.nickname } : {},
|
|
7101
|
+
agent_role: agent.agentRole,
|
|
6673
7102
|
status: agent.status,
|
|
6674
7103
|
depth: agent.depth,
|
|
6675
7104
|
model: agent.model,
|
|
@@ -6695,6 +7124,83 @@ function buildSnapshot(agent) {
|
|
|
6695
7124
|
} : {}
|
|
6696
7125
|
};
|
|
6697
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
|
+
}
|
|
6698
7204
|
function normalizeInteger(value, fallback, min, max) {
|
|
6699
7205
|
const parsed = Number.isFinite(value) ? Math.floor(value) : fallback;
|
|
6700
7206
|
return Math.max(min, Math.min(max, parsed));
|
|
@@ -7433,29 +7939,33 @@ var DEFAULT_MAX_LINE_LENGTH = 500;
|
|
|
7433
7939
|
var DEFAULT_GREP_MAX_SCANNED_FILES = 2e4;
|
|
7434
7940
|
var DEFAULT_TAB_WIDTH = 4;
|
|
7435
7941
|
var codexReadFileInputSchema = import_zod6.z.object({
|
|
7436
|
-
file_path: import_zod6.z.string().min(1).describe(
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
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".'),
|
|
7440
7948
|
indentation: import_zod6.z.object({
|
|
7441
|
-
anchor_line: import_zod6.z.number().int().min(1).
|
|
7442
|
-
max_levels: import_zod6.z.number().int().min(0).
|
|
7443
|
-
include_siblings: import_zod6.z.boolean().
|
|
7444
|
-
include_header: import_zod6.z.boolean().
|
|
7445
|
-
max_lines: import_zod6.z.number().int().min(1).
|
|
7446
|
-
}).
|
|
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()
|
|
7447
7955
|
});
|
|
7448
7956
|
var codexListDirInputSchema = import_zod6.z.object({
|
|
7449
|
-
dir_path: import_zod6.z.string().min(1).describe(
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
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.")
|
|
7453
7963
|
});
|
|
7454
7964
|
var codexGrepFilesInputSchema = import_zod6.z.object({
|
|
7455
7965
|
pattern: import_zod6.z.string().min(1).describe("Regular expression pattern to search for."),
|
|
7456
|
-
include: import_zod6.z.string().
|
|
7457
|
-
path: import_zod6.z.string().
|
|
7458
|
-
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).")
|
|
7459
7969
|
});
|
|
7460
7970
|
var applyPatchInputSchema = import_zod6.z.object({
|
|
7461
7971
|
input: import_zod6.z.string().min(1).describe(CODEX_APPLY_PATCH_INPUT_DESCRIPTION)
|
|
@@ -7690,9 +8200,6 @@ function createGlobTool(options = {}) {
|
|
|
7690
8200
|
}
|
|
7691
8201
|
async function readFileCodex(input, options) {
|
|
7692
8202
|
const runtime = resolveRuntime(options);
|
|
7693
|
-
if (!import_node_path5.default.isAbsolute(input.file_path)) {
|
|
7694
|
-
throw new Error("file_path must be an absolute path");
|
|
7695
|
-
}
|
|
7696
8203
|
const filePath = resolvePathWithPolicy(input.file_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
7697
8204
|
await runAccessHook2(runtime, {
|
|
7698
8205
|
cwd: runtime.cwd,
|
|
@@ -7735,15 +8242,12 @@ async function readFileCodex(input, options) {
|
|
|
7735
8242
|
maxLevels: indentation.max_levels ?? 0,
|
|
7736
8243
|
includeSiblings: indentation.include_siblings ?? false,
|
|
7737
8244
|
includeHeader: indentation.include_header ?? true,
|
|
7738
|
-
maxLines: indentation.max_lines
|
|
8245
|
+
maxLines: indentation.max_lines ?? void 0
|
|
7739
8246
|
});
|
|
7740
8247
|
return selected.map((record) => `L${record.number}: ${record.display}`).join("\n");
|
|
7741
8248
|
}
|
|
7742
8249
|
async function listDirectoryCodex(input, options) {
|
|
7743
8250
|
const runtime = resolveRuntime(options);
|
|
7744
|
-
if (!import_node_path5.default.isAbsolute(input.dir_path)) {
|
|
7745
|
-
throw new Error("dir_path must be an absolute path");
|
|
7746
|
-
}
|
|
7747
8251
|
const dirPath = resolvePathWithPolicy(input.dir_path, runtime.cwd, runtime.allowOutsideCwd);
|
|
7748
8252
|
await runAccessHook2(runtime, {
|
|
7749
8253
|
cwd: runtime.cwd,
|
|
@@ -7771,7 +8275,7 @@ async function listDirectoryCodex(input, options) {
|
|
|
7771
8275
|
const remaining = entries.length - startIndex;
|
|
7772
8276
|
const cappedLimit = Math.min(limit, remaining);
|
|
7773
8277
|
const selected = entries.slice(startIndex, startIndex + cappedLimit);
|
|
7774
|
-
const output = [`Absolute path: ${dirPath}`];
|
|
8278
|
+
const output = [`Absolute path: ${toSandboxDisplayPath(dirPath, runtime.cwd)}`];
|
|
7775
8279
|
for (const entry of selected) {
|
|
7776
8280
|
output.push(formatListEntry(entry));
|
|
7777
8281
|
}
|
|
@@ -8157,10 +8661,17 @@ function mapApplyPatchAction(action) {
|
|
|
8157
8661
|
}
|
|
8158
8662
|
function resolvePathWithPolicy(inputPath, cwd, allowOutsideCwd) {
|
|
8159
8663
|
const absolutePath = import_node_path5.default.isAbsolute(inputPath) ? import_node_path5.default.resolve(inputPath) : import_node_path5.default.resolve(cwd, inputPath);
|
|
8160
|
-
if (
|
|
8161
|
-
|
|
8664
|
+
if (allowOutsideCwd || isPathInsideCwd2(absolutePath, cwd)) {
|
|
8665
|
+
return absolutePath;
|
|
8162
8666
|
}
|
|
8163
|
-
|
|
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}"`);
|
|
8164
8675
|
}
|
|
8165
8676
|
function isPathInsideCwd2(candidatePath, cwd) {
|
|
8166
8677
|
const relative = import_node_path5.default.relative(cwd, candidatePath);
|
|
@@ -8176,6 +8687,16 @@ function toDisplayPath2(absolutePath, cwd) {
|
|
|
8176
8687
|
}
|
|
8177
8688
|
return absolutePath;
|
|
8178
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
|
+
}
|
|
8179
8700
|
function splitLines(content) {
|
|
8180
8701
|
const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
8181
8702
|
const lines = normalized.split("\n");
|
|
@@ -8520,6 +9041,65 @@ async function runAgentLoop(request) {
|
|
|
8520
9041
|
await telemetry?.flush();
|
|
8521
9042
|
}
|
|
8522
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
|
+
};
|
|
9102
|
+
}
|
|
8523
9103
|
async function runAgentLoopInternal(request, context) {
|
|
8524
9104
|
const {
|
|
8525
9105
|
tools: customTools,
|
|
@@ -8534,6 +9114,8 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8534
9114
|
const telemetrySession = context.telemetry ?? createAgentTelemetrySession(telemetry);
|
|
8535
9115
|
const runId = randomRunId();
|
|
8536
9116
|
const startedAtMs = Date.now();
|
|
9117
|
+
const steeringChannel = toolLoopRequest.steering ?? createToolLoopSteeringChannel();
|
|
9118
|
+
const toolLoopRequestWithSteering = toolLoopRequest.steering === steeringChannel ? toolLoopRequest : { ...toolLoopRequest, steering: steeringChannel };
|
|
8537
9119
|
const filesystemSelection = filesystemTool ?? filesystem_tool;
|
|
8538
9120
|
const subagentSelection = subagentTool ?? subagent_tool ?? subagents;
|
|
8539
9121
|
const filesystemTools = resolveFilesystemTools(request.model, filesystemSelection);
|
|
@@ -8546,7 +9128,8 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8546
9128
|
customTools: customTools ?? {},
|
|
8547
9129
|
filesystemSelection,
|
|
8548
9130
|
subagentSelection,
|
|
8549
|
-
toolLoopRequest,
|
|
9131
|
+
toolLoopRequest: toolLoopRequestWithSteering,
|
|
9132
|
+
steering: steeringChannel,
|
|
8550
9133
|
resolvedSubagentConfig
|
|
8551
9134
|
});
|
|
8552
9135
|
const mergedTools = mergeToolSets(
|
|
@@ -8559,7 +9142,7 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8559
9142
|
);
|
|
8560
9143
|
}
|
|
8561
9144
|
const instructions = buildLoopInstructions(
|
|
8562
|
-
|
|
9145
|
+
toolLoopRequestWithSteering.instructions,
|
|
8563
9146
|
resolvedSubagentConfig,
|
|
8564
9147
|
context.depth
|
|
8565
9148
|
);
|
|
@@ -8578,7 +9161,7 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8578
9161
|
filesystemToolsEnabled: Object.keys(filesystemTools).length > 0,
|
|
8579
9162
|
subagentToolsEnabled: resolvedSubagentConfig.enabled
|
|
8580
9163
|
});
|
|
8581
|
-
const sourceOnEvent =
|
|
9164
|
+
const sourceOnEvent = toolLoopRequestWithSteering.onEvent;
|
|
8582
9165
|
const includeLlmStreamEvents = telemetrySession?.includeLlmStreamEvents === true;
|
|
8583
9166
|
const wrappedOnEvent = sourceOnEvent || includeLlmStreamEvents ? (event) => {
|
|
8584
9167
|
sourceOnEvent?.(event);
|
|
@@ -8588,7 +9171,7 @@ async function runAgentLoopInternal(request, context) {
|
|
|
8588
9171
|
} : void 0;
|
|
8589
9172
|
try {
|
|
8590
9173
|
const result = await runToolLoop({
|
|
8591
|
-
...
|
|
9174
|
+
...toolLoopRequestWithSteering,
|
|
8592
9175
|
...instructions ? { instructions } : {},
|
|
8593
9176
|
...wrappedOnEvent ? { onEvent: wrappedOnEvent } : {},
|
|
8594
9177
|
tools: mergedTools
|
|
@@ -8656,6 +9239,10 @@ function createSubagentController(params) {
|
|
|
8656
9239
|
config: params.resolvedSubagentConfig,
|
|
8657
9240
|
parentDepth: params.depth,
|
|
8658
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
|
+
},
|
|
8659
9246
|
buildChildInstructions: (spawnInstructions, childDepth) => buildChildInstructions(spawnInstructions, params.resolvedSubagentConfig, childDepth),
|
|
8660
9247
|
runSubagent: async (subagentRequest) => {
|
|
8661
9248
|
const childCustomTools = params.resolvedSubagentConfig.inheritTools ? params.customTools : {};
|
|
@@ -8724,6 +9311,15 @@ function buildChildInstructions(spawnInstructions, config, childDepth) {
|
|
|
8724
9311
|
}
|
|
8725
9312
|
return blocks.length > 0 ? blocks.join("\n\n") : void 0;
|
|
8726
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
|
+
}
|
|
8727
9323
|
function trimToUndefined2(value) {
|
|
8728
9324
|
const trimmed = value?.trim();
|
|
8729
9325
|
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
@@ -8894,6 +9490,7 @@ function createAgentTelemetryEmitter(params) {
|
|
|
8894
9490
|
createReadFilesTool,
|
|
8895
9491
|
createReplaceTool,
|
|
8896
9492
|
createRgSearchTool,
|
|
9493
|
+
createToolLoopSteeringChannel,
|
|
8897
9494
|
createWriteFileTool,
|
|
8898
9495
|
customTool,
|
|
8899
9496
|
encodeChatGptAuthJson,
|
|
@@ -8925,8 +9522,10 @@ function createAgentTelemetryEmitter(params) {
|
|
|
8925
9522
|
runAgentLoop,
|
|
8926
9523
|
runToolLoop,
|
|
8927
9524
|
sanitisePartForLogging,
|
|
9525
|
+
streamAgentLoop,
|
|
8928
9526
|
streamJson,
|
|
8929
9527
|
streamText,
|
|
9528
|
+
streamToolLoop,
|
|
8930
9529
|
stripCodexCitationMarkers,
|
|
8931
9530
|
toGeminiJsonSchema,
|
|
8932
9531
|
tool
|