fourmis-agents-sdk 0.3.1 → 0.4.1
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 +126 -198
- package/dist/agent-loop.d.ts +21 -3
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +279 -90
- package/dist/agents/index.js +1079 -124
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +1079 -124
- package/dist/agents/types.d.ts +4 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/api.d.ts +8 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1663 -430
- package/dist/hooks.d.ts +19 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +27 -2
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1671 -431
- package/dist/mcp/client.d.ts +8 -1
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +134 -13
- package/dist/mcp/index.js +134 -13
- package/dist/mcp/types.d.ts +21 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +7 -3
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +41 -2
- package/dist/providers/openai.d.ts +6 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +36 -6
- package/dist/providers/registry.js +76 -8
- package/dist/providers/types.d.ts +4 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/query.d.ts +21 -2
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +69 -1
- package/dist/skills/index.js +23 -1
- package/dist/skills/skills.d.ts +16 -0
- package/dist/skills/skills.d.ts.map +1 -1
- package/dist/skills/skills.js +23 -1
- package/dist/tools/ask-user-question.d.ts +7 -0
- package/dist/tools/ask-user-question.d.ts.map +1 -0
- package/dist/tools/ask-user-question.js +48 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +47 -2
- package/dist/tools/config.d.ts +7 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +114 -0
- package/dist/tools/exit-plan-mode.d.ts +7 -0
- package/dist/tools/exit-plan-mode.d.ts.map +1 -0
- package/dist/tools/exit-plan-mode.js +34 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +506 -9
- package/dist/tools/notebook-edit.d.ts +7 -0
- package/dist/tools/notebook-edit.d.ts.map +1 -0
- package/dist/tools/notebook-edit.js +83 -0
- package/dist/tools/presets.d.ts +2 -1
- package/dist/tools/presets.d.ts.map +1 -1
- package/dist/tools/presets.js +22 -4
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +12 -1
- package/dist/tools/registry.d.ts +2 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +10 -0
- package/dist/tools/todo-write.d.ts +7 -0
- package/dist/tools/todo-write.d.ts.map +1 -0
- package/dist/tools/todo-write.js +69 -0
- package/dist/tools/web-fetch.d.ts +6 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +85 -0
- package/dist/tools/web-search.d.ts +7 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +78 -0
- package/dist/types.d.ts +344 -42
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/session-store.d.ts +1 -1
- package/dist/utils/session-store.d.ts.map +1 -1
- package/dist/utils/session-store.js +49 -2
- package/dist/utils/system-prompt.d.ts +2 -0
- package/dist/utils/system-prompt.d.ts.map +1 -1
- package/dist/utils/system-prompt.js +33 -4
- package/package.json +3 -2
package/dist/agent-loop.js
CHANGED
|
@@ -104,10 +104,57 @@ function mergeUsage(a, b) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// src/agent-loop.ts
|
|
107
|
+
function makeModelUsageEntry() {
|
|
108
|
+
return {
|
|
109
|
+
inputTokens: 0,
|
|
110
|
+
outputTokens: 0,
|
|
111
|
+
cacheReadInputTokens: 0,
|
|
112
|
+
cacheCreationInputTokens: 0,
|
|
113
|
+
totalCostUsd: 0
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function makeErrorResult(params) {
|
|
117
|
+
return {
|
|
118
|
+
type: "result",
|
|
119
|
+
subtype: params.subtype,
|
|
120
|
+
duration_ms: Date.now() - params.startTime,
|
|
121
|
+
duration_api_ms: params.apiTimeMs,
|
|
122
|
+
is_error: true,
|
|
123
|
+
num_turns: params.turns,
|
|
124
|
+
stop_reason: null,
|
|
125
|
+
total_cost_usd: params.costUsd,
|
|
126
|
+
usage: params.usage,
|
|
127
|
+
modelUsage: params.modelUsage,
|
|
128
|
+
permission_denials: params.permissionDenials,
|
|
129
|
+
errors: params.errors,
|
|
130
|
+
uuid: uuid(),
|
|
131
|
+
session_id: params.sessionId
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function extractStructuredJson(text) {
|
|
135
|
+
const trimmed = text.trim();
|
|
136
|
+
if (!trimmed) {
|
|
137
|
+
return { ok: false, error: "Empty result text; expected JSON output." };
|
|
138
|
+
}
|
|
139
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
140
|
+
const candidate = fenced ? fenced[1].trim() : trimmed;
|
|
141
|
+
try {
|
|
142
|
+
return { ok: true, value: JSON.parse(candidate) };
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
145
|
+
return { ok: false, error: `Invalid JSON output: ${message}` };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
107
148
|
async function* agentLoop(prompt, options) {
|
|
108
149
|
const {
|
|
109
150
|
provider,
|
|
110
151
|
model,
|
|
152
|
+
fallbackModel,
|
|
153
|
+
modelState,
|
|
154
|
+
maxThinkingTokensState,
|
|
155
|
+
thinking,
|
|
156
|
+
effort,
|
|
157
|
+
outputFormat,
|
|
111
158
|
systemPrompt,
|
|
112
159
|
tools,
|
|
113
160
|
permissions,
|
|
@@ -115,7 +162,7 @@ async function* agentLoop(prompt, options) {
|
|
|
115
162
|
sessionId,
|
|
116
163
|
maxTurns,
|
|
117
164
|
maxBudgetUsd,
|
|
118
|
-
|
|
165
|
+
includePartialMessages,
|
|
119
166
|
signal,
|
|
120
167
|
env,
|
|
121
168
|
debug,
|
|
@@ -123,14 +170,17 @@ async function* agentLoop(prompt, options) {
|
|
|
123
170
|
mcpClient,
|
|
124
171
|
previousMessages,
|
|
125
172
|
sessionLogger,
|
|
126
|
-
nativeMemoryTool
|
|
173
|
+
nativeMemoryTool,
|
|
174
|
+
initMeta
|
|
127
175
|
} = options;
|
|
176
|
+
const effectiveModelState = modelState ?? { current: model };
|
|
128
177
|
const startTime = Date.now();
|
|
129
178
|
let apiTimeMs = 0;
|
|
130
179
|
let turns = 0;
|
|
131
180
|
let totalUsage = emptyTokenUsage();
|
|
132
181
|
let costUsd = 0;
|
|
133
182
|
const modelUsage = {};
|
|
183
|
+
const permissionDenials = [];
|
|
134
184
|
if (mcpClient) {
|
|
135
185
|
await mcpClient.connectAll();
|
|
136
186
|
for (const tool of mcpClient.getTools()) {
|
|
@@ -148,56 +198,135 @@ async function* agentLoop(prompt, options) {
|
|
|
148
198
|
sessionLogger("user", prompt, null);
|
|
149
199
|
}
|
|
150
200
|
yield {
|
|
151
|
-
type: "
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
201
|
+
type: "system",
|
|
202
|
+
subtype: "init",
|
|
203
|
+
apiKeySource: "user",
|
|
204
|
+
claude_code_version: "fourmis-agent-sdk",
|
|
205
|
+
session_id: sessionId,
|
|
206
|
+
model: effectiveModelState.current,
|
|
155
207
|
tools: tools.list(),
|
|
156
208
|
cwd,
|
|
209
|
+
mcp_servers: (mcpClient?.status() ?? []).map((s) => ({ name: s.name, status: s.status })),
|
|
210
|
+
permissionMode: permissions.getMode(),
|
|
211
|
+
agents: initMeta?.agents,
|
|
212
|
+
betas: initMeta?.betas,
|
|
213
|
+
slash_commands: initMeta?.slashCommands ?? [],
|
|
214
|
+
output_style: initMeta?.outputStyle ?? "default",
|
|
215
|
+
skills: initMeta?.skills ?? [],
|
|
216
|
+
plugins: (initMeta?.plugins ?? []).map((p) => ({ name: p.path.split("/").pop() ?? p.path, path: p.path })),
|
|
157
217
|
uuid: uuid()
|
|
158
218
|
};
|
|
159
219
|
if (hooks) {
|
|
160
|
-
await hooks.fire("
|
|
220
|
+
await hooks.fire("Setup", {
|
|
221
|
+
event: "Setup",
|
|
222
|
+
hook_event_name: "Setup",
|
|
223
|
+
trigger: "init",
|
|
224
|
+
session_id: sessionId,
|
|
225
|
+
cwd,
|
|
226
|
+
permission_mode: permissions.getMode()
|
|
227
|
+
}, undefined, { signal });
|
|
228
|
+
}
|
|
229
|
+
if (hooks) {
|
|
230
|
+
await hooks.fire("SessionStart", {
|
|
231
|
+
event: "SessionStart",
|
|
232
|
+
hook_event_name: "SessionStart",
|
|
233
|
+
session_id: sessionId,
|
|
234
|
+
source: "startup",
|
|
235
|
+
model: effectiveModelState.current,
|
|
236
|
+
cwd,
|
|
237
|
+
permission_mode: permissions.getMode()
|
|
238
|
+
}, undefined, { signal });
|
|
161
239
|
}
|
|
162
240
|
while (true) {
|
|
163
241
|
if (signal.aborted) {
|
|
164
|
-
yield
|
|
242
|
+
yield makeErrorResult({
|
|
243
|
+
subtype: "error_during_execution",
|
|
244
|
+
errors: ["Aborted"],
|
|
245
|
+
turns,
|
|
246
|
+
costUsd,
|
|
247
|
+
sessionId,
|
|
248
|
+
startTime,
|
|
249
|
+
apiTimeMs,
|
|
250
|
+
usage: totalUsage,
|
|
251
|
+
modelUsage,
|
|
252
|
+
permissionDenials
|
|
253
|
+
});
|
|
165
254
|
return;
|
|
166
255
|
}
|
|
167
256
|
if (turns >= maxTurns) {
|
|
168
|
-
yield
|
|
257
|
+
yield makeErrorResult({
|
|
258
|
+
subtype: "error_max_turns",
|
|
259
|
+
errors: [`Reached maximum turns (${maxTurns})`],
|
|
260
|
+
turns,
|
|
261
|
+
costUsd,
|
|
262
|
+
sessionId,
|
|
263
|
+
startTime,
|
|
264
|
+
apiTimeMs,
|
|
265
|
+
usage: totalUsage,
|
|
266
|
+
modelUsage,
|
|
267
|
+
permissionDenials
|
|
268
|
+
});
|
|
169
269
|
return;
|
|
170
270
|
}
|
|
171
271
|
if (maxBudgetUsd > 0 && costUsd >= maxBudgetUsd) {
|
|
172
|
-
yield
|
|
272
|
+
yield makeErrorResult({
|
|
273
|
+
subtype: "error_max_budget_usd",
|
|
274
|
+
errors: [],
|
|
275
|
+
turns,
|
|
276
|
+
costUsd,
|
|
277
|
+
sessionId,
|
|
278
|
+
startTime,
|
|
279
|
+
apiTimeMs,
|
|
280
|
+
usage: totalUsage,
|
|
281
|
+
modelUsage,
|
|
282
|
+
permissionDenials
|
|
283
|
+
});
|
|
173
284
|
return;
|
|
174
285
|
}
|
|
286
|
+
const activeModel = effectiveModelState.current;
|
|
175
287
|
const toolDefs = tools.getDefinitions();
|
|
176
288
|
const apiStart = Date.now();
|
|
177
289
|
let assistantTextParts = [];
|
|
178
|
-
|
|
290
|
+
const toolCalls = [];
|
|
179
291
|
let turnUsage = emptyTokenUsage();
|
|
292
|
+
let turnStopReason = null;
|
|
180
293
|
const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
|
|
181
294
|
try {
|
|
182
295
|
const chunks = provider.chat({
|
|
183
|
-
model,
|
|
296
|
+
model: activeModel,
|
|
184
297
|
messages,
|
|
185
298
|
tools: toolDefs.length > 0 ? toolDefs : undefined,
|
|
186
299
|
systemPrompt,
|
|
187
300
|
signal,
|
|
188
|
-
nativeTools
|
|
301
|
+
nativeTools,
|
|
302
|
+
thinkingBudget: maxThinkingTokensState?.current,
|
|
303
|
+
thinking,
|
|
304
|
+
effort,
|
|
305
|
+
outputFormat
|
|
189
306
|
});
|
|
190
307
|
for await (const chunk of chunks) {
|
|
191
308
|
switch (chunk.type) {
|
|
192
309
|
case "text_delta":
|
|
193
310
|
assistantTextParts.push(chunk.text);
|
|
194
|
-
if (
|
|
195
|
-
yield {
|
|
311
|
+
if (includePartialMessages) {
|
|
312
|
+
yield {
|
|
313
|
+
type: "stream_event",
|
|
314
|
+
event: { type: "text_delta", text: chunk.text },
|
|
315
|
+
parent_tool_use_id: null,
|
|
316
|
+
uuid: uuid(),
|
|
317
|
+
session_id: sessionId
|
|
318
|
+
};
|
|
196
319
|
}
|
|
197
320
|
break;
|
|
198
321
|
case "thinking_delta":
|
|
199
|
-
if (
|
|
200
|
-
yield {
|
|
322
|
+
if (includePartialMessages) {
|
|
323
|
+
yield {
|
|
324
|
+
type: "stream_event",
|
|
325
|
+
event: { type: "thinking_delta", thinking: chunk.text },
|
|
326
|
+
parent_tool_use_id: null,
|
|
327
|
+
uuid: uuid(),
|
|
328
|
+
session_id: sessionId
|
|
329
|
+
};
|
|
201
330
|
}
|
|
202
331
|
break;
|
|
203
332
|
case "tool_call":
|
|
@@ -207,33 +336,54 @@ async function* agentLoop(prompt, options) {
|
|
|
207
336
|
turnUsage = mergeUsage(turnUsage, chunk.usage);
|
|
208
337
|
break;
|
|
209
338
|
case "done":
|
|
339
|
+
turnStopReason = chunk.stopReason ?? null;
|
|
210
340
|
break;
|
|
211
341
|
}
|
|
212
342
|
}
|
|
213
343
|
} catch (err) {
|
|
214
344
|
const message = err instanceof Error ? err.message : String(err);
|
|
215
|
-
|
|
345
|
+
if (fallbackModel && activeModel !== fallbackModel) {
|
|
346
|
+
effectiveModelState.current = fallbackModel;
|
|
347
|
+
yield {
|
|
348
|
+
type: "system",
|
|
349
|
+
subtype: "status",
|
|
350
|
+
status: null,
|
|
351
|
+
permissionMode: permissions.getMode(),
|
|
352
|
+
uuid: uuid(),
|
|
353
|
+
session_id: sessionId
|
|
354
|
+
};
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
yield makeErrorResult({
|
|
358
|
+
subtype: "error_during_execution",
|
|
359
|
+
errors: [`API error: ${message}`],
|
|
360
|
+
turns,
|
|
361
|
+
costUsd,
|
|
362
|
+
sessionId,
|
|
363
|
+
startTime,
|
|
364
|
+
apiTimeMs,
|
|
365
|
+
usage: totalUsage,
|
|
366
|
+
modelUsage,
|
|
367
|
+
permissionDenials
|
|
368
|
+
});
|
|
216
369
|
return;
|
|
217
370
|
}
|
|
218
371
|
apiTimeMs += Date.now() - apiStart;
|
|
219
372
|
turns++;
|
|
220
373
|
totalUsage = mergeUsage(totalUsage, turnUsage);
|
|
221
|
-
const turnCost = provider.calculateCost(
|
|
374
|
+
const turnCost = provider.calculateCost(activeModel, turnUsage);
|
|
222
375
|
costUsd += turnCost;
|
|
223
|
-
if (!modelUsage[
|
|
224
|
-
modelUsage[
|
|
225
|
-
inputTokens: 0,
|
|
226
|
-
outputTokens: 0,
|
|
227
|
-
cacheReadInputTokens: 0,
|
|
228
|
-
cacheCreationInputTokens: 0,
|
|
229
|
-
totalCostUsd: 0
|
|
230
|
-
};
|
|
376
|
+
if (!modelUsage[activeModel]) {
|
|
377
|
+
modelUsage[activeModel] = makeModelUsageEntry();
|
|
231
378
|
}
|
|
232
|
-
modelUsage[
|
|
233
|
-
modelUsage[
|
|
234
|
-
modelUsage[
|
|
235
|
-
modelUsage[
|
|
236
|
-
modelUsage[
|
|
379
|
+
modelUsage[activeModel].inputTokens += turnUsage.inputTokens;
|
|
380
|
+
modelUsage[activeModel].outputTokens += turnUsage.outputTokens;
|
|
381
|
+
modelUsage[activeModel].cacheReadInputTokens += turnUsage.cacheReadInputTokens;
|
|
382
|
+
modelUsage[activeModel].cacheCreationInputTokens += turnUsage.cacheCreationInputTokens;
|
|
383
|
+
modelUsage[activeModel].totalCostUsd += turnCost;
|
|
384
|
+
modelUsage[activeModel].webSearchRequests = (modelUsage[activeModel].webSearchRequests ?? 0) + (turnUsage.webSearchRequests ?? 0);
|
|
385
|
+
modelUsage[activeModel].costUSD = modelUsage[activeModel].totalCostUsd;
|
|
386
|
+
modelUsage[activeModel].contextWindow = provider.getContextWindow(activeModel);
|
|
237
387
|
const assistantText = assistantTextParts.join("");
|
|
238
388
|
const assistantContent = [];
|
|
239
389
|
if (assistantText) {
|
|
@@ -251,28 +401,72 @@ async function* agentLoop(prompt, options) {
|
|
|
251
401
|
if (sessionLogger) {
|
|
252
402
|
sessionLogger("assistant", assistantContent, null);
|
|
253
403
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
404
|
+
yield {
|
|
405
|
+
type: "assistant",
|
|
406
|
+
message: {
|
|
407
|
+
role: "assistant",
|
|
408
|
+
content: assistantContent
|
|
409
|
+
},
|
|
410
|
+
parent_tool_use_id: null,
|
|
411
|
+
uuid: uuid(),
|
|
412
|
+
session_id: sessionId
|
|
413
|
+
};
|
|
257
414
|
if (toolCalls.length === 0) {
|
|
415
|
+
let structuredOutput;
|
|
416
|
+
if (outputFormat?.type === "json_schema") {
|
|
417
|
+
const parsed = extractStructuredJson(assistantText);
|
|
418
|
+
if (!parsed.ok) {
|
|
419
|
+
yield makeErrorResult({
|
|
420
|
+
subtype: "error_max_structured_output_retries",
|
|
421
|
+
errors: [parsed.error],
|
|
422
|
+
turns,
|
|
423
|
+
costUsd,
|
|
424
|
+
sessionId,
|
|
425
|
+
startTime,
|
|
426
|
+
apiTimeMs,
|
|
427
|
+
usage: totalUsage,
|
|
428
|
+
modelUsage,
|
|
429
|
+
permissionDenials
|
|
430
|
+
});
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
structuredOutput = parsed.value;
|
|
434
|
+
}
|
|
258
435
|
if (hooks) {
|
|
259
|
-
await hooks.fire("Stop", {
|
|
436
|
+
await hooks.fire("Stop", {
|
|
437
|
+
event: "Stop",
|
|
438
|
+
hook_event_name: "Stop",
|
|
439
|
+
session_id: sessionId,
|
|
440
|
+
text: assistantText || undefined,
|
|
441
|
+
stop_reason: turnStopReason ?? undefined
|
|
442
|
+
}, undefined, {
|
|
443
|
+
signal
|
|
444
|
+
});
|
|
260
445
|
}
|
|
261
446
|
if (hooks) {
|
|
262
|
-
await hooks.fire("SessionEnd", {
|
|
447
|
+
await hooks.fire("SessionEnd", {
|
|
448
|
+
event: "SessionEnd",
|
|
449
|
+
hook_event_name: "SessionEnd",
|
|
450
|
+
session_id: sessionId,
|
|
451
|
+
reason: "other"
|
|
452
|
+
}, undefined, { signal });
|
|
263
453
|
}
|
|
264
454
|
yield {
|
|
265
455
|
type: "result",
|
|
266
456
|
subtype: "success",
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
457
|
+
duration_ms: Date.now() - startTime,
|
|
458
|
+
duration_api_ms: apiTimeMs,
|
|
459
|
+
is_error: false,
|
|
460
|
+
num_turns: turns,
|
|
461
|
+
result: assistantText,
|
|
462
|
+
stop_reason: turnStopReason,
|
|
463
|
+
total_cost_usd: costUsd,
|
|
273
464
|
usage: totalUsage,
|
|
274
465
|
modelUsage,
|
|
275
|
-
|
|
466
|
+
permission_denials: permissionDenials,
|
|
467
|
+
structured_output: structuredOutput,
|
|
468
|
+
uuid: uuid(),
|
|
469
|
+
session_id: sessionId
|
|
276
470
|
};
|
|
277
471
|
return;
|
|
278
472
|
}
|
|
@@ -281,7 +475,13 @@ async function* agentLoop(prompt, options) {
|
|
|
281
475
|
let hookDenied = false;
|
|
282
476
|
let hookUpdatedInput;
|
|
283
477
|
if (hooks) {
|
|
284
|
-
const hookResult = await hooks.fire("PreToolUse", {
|
|
478
|
+
const hookResult = await hooks.fire("PreToolUse", {
|
|
479
|
+
event: "PreToolUse",
|
|
480
|
+
hook_event_name: "PreToolUse",
|
|
481
|
+
tool_name: call.name,
|
|
482
|
+
tool_input: call.input,
|
|
483
|
+
session_id: sessionId
|
|
484
|
+
}, call.id, { signal });
|
|
285
485
|
if (hookResult) {
|
|
286
486
|
if (hookResult.permissionDecision === "deny") {
|
|
287
487
|
hookDenied = true;
|
|
@@ -293,14 +493,6 @@ async function* agentLoop(prompt, options) {
|
|
|
293
493
|
}
|
|
294
494
|
if (hookDenied) {
|
|
295
495
|
const denyContent = "Denied by hook";
|
|
296
|
-
yield {
|
|
297
|
-
type: "tool_result",
|
|
298
|
-
id: call.id,
|
|
299
|
-
name: call.name,
|
|
300
|
-
content: denyContent,
|
|
301
|
-
isError: true,
|
|
302
|
-
uuid: uuid()
|
|
303
|
-
};
|
|
304
496
|
toolResults.push({
|
|
305
497
|
type: "tool_result",
|
|
306
498
|
tool_use_id: call.id,
|
|
@@ -316,14 +508,11 @@ async function* agentLoop(prompt, options) {
|
|
|
316
508
|
const permResult = await permissions.check(call.name, inputAfterHook ?? {}, { signal, toolUseId: call.id });
|
|
317
509
|
if (permResult.behavior === "deny") {
|
|
318
510
|
const denyContent = `Permission denied: ${permResult.message}`;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
isError: true,
|
|
325
|
-
uuid: uuid()
|
|
326
|
-
};
|
|
511
|
+
permissionDenials.push({
|
|
512
|
+
tool_name: call.name,
|
|
513
|
+
tool_use_id: call.id,
|
|
514
|
+
tool_input: inputAfterHook ?? {}
|
|
515
|
+
});
|
|
327
516
|
toolResults.push({
|
|
328
517
|
type: "tool_result",
|
|
329
518
|
tool_use_id: call.id,
|
|
@@ -336,13 +525,6 @@ async function* agentLoop(prompt, options) {
|
|
|
336
525
|
continue;
|
|
337
526
|
}
|
|
338
527
|
const toolInput = permResult.behavior === "allow" && permResult.updatedInput ? permResult.updatedInput : inputAfterHook;
|
|
339
|
-
yield {
|
|
340
|
-
type: "tool_use",
|
|
341
|
-
id: call.id,
|
|
342
|
-
name: call.name,
|
|
343
|
-
input: toolInput,
|
|
344
|
-
uuid: uuid()
|
|
345
|
-
};
|
|
346
528
|
let result;
|
|
347
529
|
if (call.name === "memory" && nativeMemoryTool) {
|
|
348
530
|
try {
|
|
@@ -361,28 +543,36 @@ async function* agentLoop(prompt, options) {
|
|
|
361
543
|
};
|
|
362
544
|
result = await tools.execute(call.name, toolInput, toolCtx);
|
|
363
545
|
}
|
|
546
|
+
if (call.name === "ExitPlanMode") {
|
|
547
|
+
permissions.setMode("default");
|
|
548
|
+
}
|
|
364
549
|
if (debug) {
|
|
365
550
|
console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
|
|
366
551
|
}
|
|
367
552
|
if (hooks) {
|
|
368
553
|
if (result.isError) {
|
|
369
|
-
await hooks.fire("PostToolUseFailure", {
|
|
554
|
+
await hooks.fire("PostToolUseFailure", {
|
|
555
|
+
event: "PostToolUseFailure",
|
|
556
|
+
hook_event_name: "PostToolUseFailure",
|
|
557
|
+
tool_name: call.name,
|
|
558
|
+
tool_result: result.content,
|
|
559
|
+
tool_error: true,
|
|
560
|
+
session_id: sessionId
|
|
561
|
+
}, call.id, { signal });
|
|
370
562
|
} else {
|
|
371
|
-
const postResult = await hooks.fire("PostToolUse", {
|
|
563
|
+
const postResult = await hooks.fire("PostToolUse", {
|
|
564
|
+
event: "PostToolUse",
|
|
565
|
+
hook_event_name: "PostToolUse",
|
|
566
|
+
tool_name: call.name,
|
|
567
|
+
tool_result: result.content,
|
|
568
|
+
session_id: sessionId
|
|
569
|
+
}, call.id, { signal });
|
|
372
570
|
if (postResult?.additionalContext) {
|
|
373
571
|
result.content += `
|
|
374
572
|
${postResult.additionalContext}`;
|
|
375
573
|
}
|
|
376
574
|
}
|
|
377
575
|
}
|
|
378
|
-
yield {
|
|
379
|
-
type: "tool_result",
|
|
380
|
-
id: call.id,
|
|
381
|
-
name: call.name,
|
|
382
|
-
content: result.content,
|
|
383
|
-
isError: result.isError,
|
|
384
|
-
uuid: uuid()
|
|
385
|
-
};
|
|
386
576
|
toolResults.push({
|
|
387
577
|
type: "tool_result",
|
|
388
578
|
tool_use_id: call.id,
|
|
@@ -394,20 +584,19 @@ ${postResult.additionalContext}`;
|
|
|
394
584
|
if (sessionLogger) {
|
|
395
585
|
sessionLogger("user", toolResults, null);
|
|
396
586
|
}
|
|
587
|
+
yield {
|
|
588
|
+
type: "user",
|
|
589
|
+
message: {
|
|
590
|
+
role: "user",
|
|
591
|
+
content: toolResults
|
|
592
|
+
},
|
|
593
|
+
parent_tool_use_id: null,
|
|
594
|
+
isSynthetic: true,
|
|
595
|
+
uuid: uuid(),
|
|
596
|
+
session_id: sessionId
|
|
597
|
+
};
|
|
397
598
|
}
|
|
398
599
|
}
|
|
399
|
-
function makeError(subtype, errors, turns, costUsd, sessionId, startTime) {
|
|
400
|
-
return {
|
|
401
|
-
type: "result",
|
|
402
|
-
subtype,
|
|
403
|
-
errors,
|
|
404
|
-
turns,
|
|
405
|
-
costUsd,
|
|
406
|
-
durationMs: Date.now() - startTime,
|
|
407
|
-
sessionId,
|
|
408
|
-
uuid: uuid()
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
600
|
export {
|
|
412
601
|
agentLoop
|
|
413
602
|
};
|