fourmis-agents-sdk 0.3.0 → 0.4.0
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/dist/agent-loop.d.ts +21 -3
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +294 -90
- package/dist/agents/index.js +2798 -1857
- package/dist/agents/task-manager.js +15 -0
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +2798 -1857
- 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 +2394 -886
- package/dist/auth/gemini-oauth.js +15 -0
- package/dist/auth/login-openai.js +15 -0
- package/dist/auth/openai-oauth.js +15 -0
- package/dist/hooks.d.ts +19 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +42 -2
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2407 -887
- package/dist/mcp/client.d.ts +7 -0
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +146 -12
- package/dist/mcp/index.js +146 -12
- package/dist/mcp/server.js +15 -0
- package/dist/mcp/types.d.ts +19 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/memory/index.js +15 -0
- package/dist/memory/memory-handler.js +15 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +22 -3
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +56 -2
- package/dist/providers/gemini.js +15 -0
- package/dist/providers/openai.js +15 -0
- package/dist/providers/registry.js +56 -2
- 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 +84 -1
- package/dist/settings.js +15 -0
- package/dist/skills/frontmatter.d.ts +15 -0
- package/dist/skills/frontmatter.d.ts.map +1 -0
- package/dist/skills/frontmatter.js +66 -0
- package/dist/skills/index.d.ts +8 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +326 -0
- package/dist/skills/skills.d.ts +94 -0
- package/dist/skills/skills.d.ts.map +1 -0
- package/dist/skills/skills.js +324 -0
- 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 +63 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +62 -2
- package/dist/tools/config.d.ts +7 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +129 -0
- package/dist/tools/edit.js +15 -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 +49 -0
- package/dist/tools/glob.js +15 -0
- package/dist/tools/grep.js +15 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +521 -9
- package/dist/tools/mcp-resources.js +15 -0
- 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 +98 -0
- package/dist/tools/presets.d.ts +2 -1
- package/dist/tools/presets.d.ts.map +1 -1
- package/dist/tools/presets.js +37 -4
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +27 -1
- package/dist/tools/registry.d.ts +2 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +25 -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 +84 -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 +100 -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 +93 -0
- package/dist/tools/write.js +15 -0
- package/dist/types.d.ts +360 -42
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -0
- package/dist/utils/cost.js +15 -0
- 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 +64 -2
- package/dist/utils/system-prompt.d.ts +4 -0
- package/dist/utils/system-prompt.d.ts.map +1 -1
- package/dist/utils/system-prompt.js +326 -6
- package/package.json +4 -2
package/dist/agent-loop.d.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* AsyncGenerator that orchestrates:
|
|
5
5
|
* 1. Call LLM via provider adapter
|
|
6
|
-
* 2. Stream
|
|
6
|
+
* 2. Stream partial assistant deltas (optional)
|
|
7
7
|
* 3. Collect tool calls
|
|
8
8
|
* 4. Execute tools with permission checks
|
|
9
9
|
* 5. Feed results back to LLM
|
|
10
10
|
* 6. Repeat until done or limits reached
|
|
11
11
|
*/
|
|
12
|
-
import type { AgentMessage } from "./types.js";
|
|
12
|
+
import type { AgentMessage, OutputFormat, ThinkingConfig, Effort, SdkBeta, SdkPluginConfig } from "./types.js";
|
|
13
13
|
import type { ProviderAdapter, NormalizedMessage, NormalizedContent } from "./providers/types.js";
|
|
14
14
|
import type { ToolRegistry } from "./tools/registry.js";
|
|
15
15
|
import type { PermissionManager } from "./permissions.js";
|
|
@@ -20,6 +20,16 @@ export type SessionLogger = (role: "user" | "assistant", content: NormalizedCont
|
|
|
20
20
|
export type AgentLoopOptions = {
|
|
21
21
|
provider: ProviderAdapter;
|
|
22
22
|
model: string;
|
|
23
|
+
fallbackModel?: string;
|
|
24
|
+
modelState?: {
|
|
25
|
+
current: string;
|
|
26
|
+
};
|
|
27
|
+
maxThinkingTokensState?: {
|
|
28
|
+
current: number | undefined;
|
|
29
|
+
};
|
|
30
|
+
thinking?: ThinkingConfig;
|
|
31
|
+
effort?: Effort;
|
|
32
|
+
outputFormat?: OutputFormat;
|
|
23
33
|
systemPrompt: string;
|
|
24
34
|
tools: ToolRegistry;
|
|
25
35
|
permissions: PermissionManager;
|
|
@@ -27,7 +37,7 @@ export type AgentLoopOptions = {
|
|
|
27
37
|
sessionId: string;
|
|
28
38
|
maxTurns: number;
|
|
29
39
|
maxBudgetUsd: number;
|
|
30
|
-
|
|
40
|
+
includePartialMessages: boolean;
|
|
31
41
|
signal: AbortSignal;
|
|
32
42
|
env?: Record<string, string>;
|
|
33
43
|
debug?: boolean;
|
|
@@ -37,6 +47,14 @@ export type AgentLoopOptions = {
|
|
|
37
47
|
sessionLogger?: SessionLogger;
|
|
38
48
|
/** Native memory tool for Anthropic provider (handled specially) */
|
|
39
49
|
nativeMemoryTool?: NativeMemoryTool;
|
|
50
|
+
initMeta?: {
|
|
51
|
+
agents?: string[];
|
|
52
|
+
betas?: SdkBeta[];
|
|
53
|
+
slashCommands?: string[];
|
|
54
|
+
outputStyle?: string;
|
|
55
|
+
skills?: string[];
|
|
56
|
+
plugins?: SdkPluginConfig[];
|
|
57
|
+
};
|
|
40
58
|
};
|
|
41
59
|
export declare function agentLoop(prompt: string, options: AgentLoopOptions): AsyncGenerator<AgentMessage>;
|
|
42
60
|
//# sourceMappingURL=agent-loop.d.ts.map
|
package/dist/agent-loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,YAAY,EAMZ,YAAY,EACZ,cAAc,EACd,MAAM,EAEN,OAAO,EACP,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,iBAAiB,EAAE,GAAG,MAAM,EACrC,UAAU,EAAE,MAAM,GAAG,IAAI,KACtB,MAAM,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,sBAAsB,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACzD,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB,EAAE,OAAO,CAAC;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACvC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;KAC7B,CAAC;CACH,CAAC;AA+DF,wBAAuB,SAAS,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,gBAAgB,GACxB,cAAc,CAAC,YAAY,CAAC,CAwjB9B"}
|
package/dist/agent-loop.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
2
4
|
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
3
18
|
var __export = (target, all) => {
|
|
4
19
|
for (var name in all)
|
|
5
20
|
__defProp(target, name, {
|
|
@@ -104,10 +119,57 @@ function mergeUsage(a, b) {
|
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
// src/agent-loop.ts
|
|
122
|
+
function makeModelUsageEntry() {
|
|
123
|
+
return {
|
|
124
|
+
inputTokens: 0,
|
|
125
|
+
outputTokens: 0,
|
|
126
|
+
cacheReadInputTokens: 0,
|
|
127
|
+
cacheCreationInputTokens: 0,
|
|
128
|
+
totalCostUsd: 0
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function makeErrorResult(params) {
|
|
132
|
+
return {
|
|
133
|
+
type: "result",
|
|
134
|
+
subtype: params.subtype,
|
|
135
|
+
duration_ms: Date.now() - params.startTime,
|
|
136
|
+
duration_api_ms: params.apiTimeMs,
|
|
137
|
+
is_error: true,
|
|
138
|
+
num_turns: params.turns,
|
|
139
|
+
stop_reason: null,
|
|
140
|
+
total_cost_usd: params.costUsd,
|
|
141
|
+
usage: params.usage,
|
|
142
|
+
modelUsage: params.modelUsage,
|
|
143
|
+
permission_denials: params.permissionDenials,
|
|
144
|
+
errors: params.errors,
|
|
145
|
+
uuid: uuid(),
|
|
146
|
+
session_id: params.sessionId
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function extractStructuredJson(text) {
|
|
150
|
+
const trimmed = text.trim();
|
|
151
|
+
if (!trimmed) {
|
|
152
|
+
return { ok: false, error: "Empty result text; expected JSON output." };
|
|
153
|
+
}
|
|
154
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
155
|
+
const candidate = fenced ? fenced[1].trim() : trimmed;
|
|
156
|
+
try {
|
|
157
|
+
return { ok: true, value: JSON.parse(candidate) };
|
|
158
|
+
} catch (err) {
|
|
159
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
160
|
+
return { ok: false, error: `Invalid JSON output: ${message}` };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
107
163
|
async function* agentLoop(prompt, options) {
|
|
108
164
|
const {
|
|
109
165
|
provider,
|
|
110
166
|
model,
|
|
167
|
+
fallbackModel,
|
|
168
|
+
modelState,
|
|
169
|
+
maxThinkingTokensState,
|
|
170
|
+
thinking,
|
|
171
|
+
effort,
|
|
172
|
+
outputFormat,
|
|
111
173
|
systemPrompt,
|
|
112
174
|
tools,
|
|
113
175
|
permissions,
|
|
@@ -115,7 +177,7 @@ async function* agentLoop(prompt, options) {
|
|
|
115
177
|
sessionId,
|
|
116
178
|
maxTurns,
|
|
117
179
|
maxBudgetUsd,
|
|
118
|
-
|
|
180
|
+
includePartialMessages,
|
|
119
181
|
signal,
|
|
120
182
|
env,
|
|
121
183
|
debug,
|
|
@@ -123,14 +185,17 @@ async function* agentLoop(prompt, options) {
|
|
|
123
185
|
mcpClient,
|
|
124
186
|
previousMessages,
|
|
125
187
|
sessionLogger,
|
|
126
|
-
nativeMemoryTool
|
|
188
|
+
nativeMemoryTool,
|
|
189
|
+
initMeta
|
|
127
190
|
} = options;
|
|
191
|
+
const effectiveModelState = modelState ?? { current: model };
|
|
128
192
|
const startTime = Date.now();
|
|
129
193
|
let apiTimeMs = 0;
|
|
130
194
|
let turns = 0;
|
|
131
195
|
let totalUsage = emptyTokenUsage();
|
|
132
196
|
let costUsd = 0;
|
|
133
197
|
const modelUsage = {};
|
|
198
|
+
const permissionDenials = [];
|
|
134
199
|
if (mcpClient) {
|
|
135
200
|
await mcpClient.connectAll();
|
|
136
201
|
for (const tool of mcpClient.getTools()) {
|
|
@@ -148,56 +213,135 @@ async function* agentLoop(prompt, options) {
|
|
|
148
213
|
sessionLogger("user", prompt, null);
|
|
149
214
|
}
|
|
150
215
|
yield {
|
|
151
|
-
type: "
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
216
|
+
type: "system",
|
|
217
|
+
subtype: "init",
|
|
218
|
+
apiKeySource: "user",
|
|
219
|
+
claude_code_version: "fourmis-agent-sdk",
|
|
220
|
+
session_id: sessionId,
|
|
221
|
+
model: effectiveModelState.current,
|
|
155
222
|
tools: tools.list(),
|
|
156
223
|
cwd,
|
|
224
|
+
mcp_servers: (mcpClient?.status() ?? []).map((s) => ({ name: s.name, status: s.status })),
|
|
225
|
+
permissionMode: permissions.getMode(),
|
|
226
|
+
agents: initMeta?.agents,
|
|
227
|
+
betas: initMeta?.betas,
|
|
228
|
+
slash_commands: initMeta?.slashCommands ?? [],
|
|
229
|
+
output_style: initMeta?.outputStyle ?? "default",
|
|
230
|
+
skills: initMeta?.skills ?? [],
|
|
231
|
+
plugins: (initMeta?.plugins ?? []).map((p) => ({ name: p.path.split("/").pop() ?? p.path, path: p.path })),
|
|
157
232
|
uuid: uuid()
|
|
158
233
|
};
|
|
159
234
|
if (hooks) {
|
|
160
|
-
await hooks.fire("
|
|
235
|
+
await hooks.fire("Setup", {
|
|
236
|
+
event: "Setup",
|
|
237
|
+
hook_event_name: "Setup",
|
|
238
|
+
trigger: "init",
|
|
239
|
+
session_id: sessionId,
|
|
240
|
+
cwd,
|
|
241
|
+
permission_mode: permissions.getMode()
|
|
242
|
+
}, undefined, { signal });
|
|
243
|
+
}
|
|
244
|
+
if (hooks) {
|
|
245
|
+
await hooks.fire("SessionStart", {
|
|
246
|
+
event: "SessionStart",
|
|
247
|
+
hook_event_name: "SessionStart",
|
|
248
|
+
session_id: sessionId,
|
|
249
|
+
source: "startup",
|
|
250
|
+
model: effectiveModelState.current,
|
|
251
|
+
cwd,
|
|
252
|
+
permission_mode: permissions.getMode()
|
|
253
|
+
}, undefined, { signal });
|
|
161
254
|
}
|
|
162
255
|
while (true) {
|
|
163
256
|
if (signal.aborted) {
|
|
164
|
-
yield
|
|
257
|
+
yield makeErrorResult({
|
|
258
|
+
subtype: "error_during_execution",
|
|
259
|
+
errors: ["Aborted"],
|
|
260
|
+
turns,
|
|
261
|
+
costUsd,
|
|
262
|
+
sessionId,
|
|
263
|
+
startTime,
|
|
264
|
+
apiTimeMs,
|
|
265
|
+
usage: totalUsage,
|
|
266
|
+
modelUsage,
|
|
267
|
+
permissionDenials
|
|
268
|
+
});
|
|
165
269
|
return;
|
|
166
270
|
}
|
|
167
271
|
if (turns >= maxTurns) {
|
|
168
|
-
yield
|
|
272
|
+
yield makeErrorResult({
|
|
273
|
+
subtype: "error_max_turns",
|
|
274
|
+
errors: [`Reached maximum turns (${maxTurns})`],
|
|
275
|
+
turns,
|
|
276
|
+
costUsd,
|
|
277
|
+
sessionId,
|
|
278
|
+
startTime,
|
|
279
|
+
apiTimeMs,
|
|
280
|
+
usage: totalUsage,
|
|
281
|
+
modelUsage,
|
|
282
|
+
permissionDenials
|
|
283
|
+
});
|
|
169
284
|
return;
|
|
170
285
|
}
|
|
171
286
|
if (maxBudgetUsd > 0 && costUsd >= maxBudgetUsd) {
|
|
172
|
-
yield
|
|
287
|
+
yield makeErrorResult({
|
|
288
|
+
subtype: "error_max_budget_usd",
|
|
289
|
+
errors: [],
|
|
290
|
+
turns,
|
|
291
|
+
costUsd,
|
|
292
|
+
sessionId,
|
|
293
|
+
startTime,
|
|
294
|
+
apiTimeMs,
|
|
295
|
+
usage: totalUsage,
|
|
296
|
+
modelUsage,
|
|
297
|
+
permissionDenials
|
|
298
|
+
});
|
|
173
299
|
return;
|
|
174
300
|
}
|
|
301
|
+
const activeModel = effectiveModelState.current;
|
|
175
302
|
const toolDefs = tools.getDefinitions();
|
|
176
303
|
const apiStart = Date.now();
|
|
177
304
|
let assistantTextParts = [];
|
|
178
|
-
|
|
305
|
+
const toolCalls = [];
|
|
179
306
|
let turnUsage = emptyTokenUsage();
|
|
307
|
+
let turnStopReason = null;
|
|
180
308
|
const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
|
|
181
309
|
try {
|
|
182
310
|
const chunks = provider.chat({
|
|
183
|
-
model,
|
|
311
|
+
model: activeModel,
|
|
184
312
|
messages,
|
|
185
313
|
tools: toolDefs.length > 0 ? toolDefs : undefined,
|
|
186
314
|
systemPrompt,
|
|
187
315
|
signal,
|
|
188
|
-
nativeTools
|
|
316
|
+
nativeTools,
|
|
317
|
+
thinkingBudget: maxThinkingTokensState?.current,
|
|
318
|
+
thinking,
|
|
319
|
+
effort,
|
|
320
|
+
outputFormat
|
|
189
321
|
});
|
|
190
322
|
for await (const chunk of chunks) {
|
|
191
323
|
switch (chunk.type) {
|
|
192
324
|
case "text_delta":
|
|
193
325
|
assistantTextParts.push(chunk.text);
|
|
194
|
-
if (
|
|
195
|
-
yield {
|
|
326
|
+
if (includePartialMessages) {
|
|
327
|
+
yield {
|
|
328
|
+
type: "stream_event",
|
|
329
|
+
event: { type: "text_delta", text: chunk.text },
|
|
330
|
+
parent_tool_use_id: null,
|
|
331
|
+
uuid: uuid(),
|
|
332
|
+
session_id: sessionId
|
|
333
|
+
};
|
|
196
334
|
}
|
|
197
335
|
break;
|
|
198
336
|
case "thinking_delta":
|
|
199
|
-
if (
|
|
200
|
-
yield {
|
|
337
|
+
if (includePartialMessages) {
|
|
338
|
+
yield {
|
|
339
|
+
type: "stream_event",
|
|
340
|
+
event: { type: "thinking_delta", thinking: chunk.text },
|
|
341
|
+
parent_tool_use_id: null,
|
|
342
|
+
uuid: uuid(),
|
|
343
|
+
session_id: sessionId
|
|
344
|
+
};
|
|
201
345
|
}
|
|
202
346
|
break;
|
|
203
347
|
case "tool_call":
|
|
@@ -207,33 +351,54 @@ async function* agentLoop(prompt, options) {
|
|
|
207
351
|
turnUsage = mergeUsage(turnUsage, chunk.usage);
|
|
208
352
|
break;
|
|
209
353
|
case "done":
|
|
354
|
+
turnStopReason = chunk.stopReason ?? null;
|
|
210
355
|
break;
|
|
211
356
|
}
|
|
212
357
|
}
|
|
213
358
|
} catch (err) {
|
|
214
359
|
const message = err instanceof Error ? err.message : String(err);
|
|
215
|
-
|
|
360
|
+
if (fallbackModel && activeModel !== fallbackModel) {
|
|
361
|
+
effectiveModelState.current = fallbackModel;
|
|
362
|
+
yield {
|
|
363
|
+
type: "system",
|
|
364
|
+
subtype: "status",
|
|
365
|
+
status: null,
|
|
366
|
+
permissionMode: permissions.getMode(),
|
|
367
|
+
uuid: uuid(),
|
|
368
|
+
session_id: sessionId
|
|
369
|
+
};
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
yield makeErrorResult({
|
|
373
|
+
subtype: "error_during_execution",
|
|
374
|
+
errors: [`API error: ${message}`],
|
|
375
|
+
turns,
|
|
376
|
+
costUsd,
|
|
377
|
+
sessionId,
|
|
378
|
+
startTime,
|
|
379
|
+
apiTimeMs,
|
|
380
|
+
usage: totalUsage,
|
|
381
|
+
modelUsage,
|
|
382
|
+
permissionDenials
|
|
383
|
+
});
|
|
216
384
|
return;
|
|
217
385
|
}
|
|
218
386
|
apiTimeMs += Date.now() - apiStart;
|
|
219
387
|
turns++;
|
|
220
388
|
totalUsage = mergeUsage(totalUsage, turnUsage);
|
|
221
|
-
const turnCost = provider.calculateCost(
|
|
389
|
+
const turnCost = provider.calculateCost(activeModel, turnUsage);
|
|
222
390
|
costUsd += turnCost;
|
|
223
|
-
if (!modelUsage[
|
|
224
|
-
modelUsage[
|
|
225
|
-
inputTokens: 0,
|
|
226
|
-
outputTokens: 0,
|
|
227
|
-
cacheReadInputTokens: 0,
|
|
228
|
-
cacheCreationInputTokens: 0,
|
|
229
|
-
totalCostUsd: 0
|
|
230
|
-
};
|
|
391
|
+
if (!modelUsage[activeModel]) {
|
|
392
|
+
modelUsage[activeModel] = makeModelUsageEntry();
|
|
231
393
|
}
|
|
232
|
-
modelUsage[
|
|
233
|
-
modelUsage[
|
|
234
|
-
modelUsage[
|
|
235
|
-
modelUsage[
|
|
236
|
-
modelUsage[
|
|
394
|
+
modelUsage[activeModel].inputTokens += turnUsage.inputTokens;
|
|
395
|
+
modelUsage[activeModel].outputTokens += turnUsage.outputTokens;
|
|
396
|
+
modelUsage[activeModel].cacheReadInputTokens += turnUsage.cacheReadInputTokens;
|
|
397
|
+
modelUsage[activeModel].cacheCreationInputTokens += turnUsage.cacheCreationInputTokens;
|
|
398
|
+
modelUsage[activeModel].totalCostUsd += turnCost;
|
|
399
|
+
modelUsage[activeModel].webSearchRequests = (modelUsage[activeModel].webSearchRequests ?? 0) + (turnUsage.webSearchRequests ?? 0);
|
|
400
|
+
modelUsage[activeModel].costUSD = modelUsage[activeModel].totalCostUsd;
|
|
401
|
+
modelUsage[activeModel].contextWindow = provider.getContextWindow(activeModel);
|
|
237
402
|
const assistantText = assistantTextParts.join("");
|
|
238
403
|
const assistantContent = [];
|
|
239
404
|
if (assistantText) {
|
|
@@ -251,28 +416,72 @@ async function* agentLoop(prompt, options) {
|
|
|
251
416
|
if (sessionLogger) {
|
|
252
417
|
sessionLogger("assistant", assistantContent, null);
|
|
253
418
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
419
|
+
yield {
|
|
420
|
+
type: "assistant",
|
|
421
|
+
message: {
|
|
422
|
+
role: "assistant",
|
|
423
|
+
content: assistantContent
|
|
424
|
+
},
|
|
425
|
+
parent_tool_use_id: null,
|
|
426
|
+
uuid: uuid(),
|
|
427
|
+
session_id: sessionId
|
|
428
|
+
};
|
|
257
429
|
if (toolCalls.length === 0) {
|
|
430
|
+
let structuredOutput;
|
|
431
|
+
if (outputFormat?.type === "json_schema") {
|
|
432
|
+
const parsed = extractStructuredJson(assistantText);
|
|
433
|
+
if (!parsed.ok) {
|
|
434
|
+
yield makeErrorResult({
|
|
435
|
+
subtype: "error_max_structured_output_retries",
|
|
436
|
+
errors: [parsed.error],
|
|
437
|
+
turns,
|
|
438
|
+
costUsd,
|
|
439
|
+
sessionId,
|
|
440
|
+
startTime,
|
|
441
|
+
apiTimeMs,
|
|
442
|
+
usage: totalUsage,
|
|
443
|
+
modelUsage,
|
|
444
|
+
permissionDenials
|
|
445
|
+
});
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
structuredOutput = parsed.value;
|
|
449
|
+
}
|
|
258
450
|
if (hooks) {
|
|
259
|
-
await hooks.fire("Stop", {
|
|
451
|
+
await hooks.fire("Stop", {
|
|
452
|
+
event: "Stop",
|
|
453
|
+
hook_event_name: "Stop",
|
|
454
|
+
session_id: sessionId,
|
|
455
|
+
text: assistantText || undefined,
|
|
456
|
+
stop_reason: turnStopReason ?? undefined
|
|
457
|
+
}, undefined, {
|
|
458
|
+
signal
|
|
459
|
+
});
|
|
260
460
|
}
|
|
261
461
|
if (hooks) {
|
|
262
|
-
await hooks.fire("SessionEnd", {
|
|
462
|
+
await hooks.fire("SessionEnd", {
|
|
463
|
+
event: "SessionEnd",
|
|
464
|
+
hook_event_name: "SessionEnd",
|
|
465
|
+
session_id: sessionId,
|
|
466
|
+
reason: "other"
|
|
467
|
+
}, undefined, { signal });
|
|
263
468
|
}
|
|
264
469
|
yield {
|
|
265
470
|
type: "result",
|
|
266
471
|
subtype: "success",
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
472
|
+
duration_ms: Date.now() - startTime,
|
|
473
|
+
duration_api_ms: apiTimeMs,
|
|
474
|
+
is_error: false,
|
|
475
|
+
num_turns: turns,
|
|
476
|
+
result: assistantText,
|
|
477
|
+
stop_reason: turnStopReason,
|
|
478
|
+
total_cost_usd: costUsd,
|
|
273
479
|
usage: totalUsage,
|
|
274
480
|
modelUsage,
|
|
275
|
-
|
|
481
|
+
permission_denials: permissionDenials,
|
|
482
|
+
structured_output: structuredOutput,
|
|
483
|
+
uuid: uuid(),
|
|
484
|
+
session_id: sessionId
|
|
276
485
|
};
|
|
277
486
|
return;
|
|
278
487
|
}
|
|
@@ -281,7 +490,13 @@ async function* agentLoop(prompt, options) {
|
|
|
281
490
|
let hookDenied = false;
|
|
282
491
|
let hookUpdatedInput;
|
|
283
492
|
if (hooks) {
|
|
284
|
-
const hookResult = await hooks.fire("PreToolUse", {
|
|
493
|
+
const hookResult = await hooks.fire("PreToolUse", {
|
|
494
|
+
event: "PreToolUse",
|
|
495
|
+
hook_event_name: "PreToolUse",
|
|
496
|
+
tool_name: call.name,
|
|
497
|
+
tool_input: call.input,
|
|
498
|
+
session_id: sessionId
|
|
499
|
+
}, call.id, { signal });
|
|
285
500
|
if (hookResult) {
|
|
286
501
|
if (hookResult.permissionDecision === "deny") {
|
|
287
502
|
hookDenied = true;
|
|
@@ -293,14 +508,6 @@ async function* agentLoop(prompt, options) {
|
|
|
293
508
|
}
|
|
294
509
|
if (hookDenied) {
|
|
295
510
|
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
511
|
toolResults.push({
|
|
305
512
|
type: "tool_result",
|
|
306
513
|
tool_use_id: call.id,
|
|
@@ -316,14 +523,11 @@ async function* agentLoop(prompt, options) {
|
|
|
316
523
|
const permResult = await permissions.check(call.name, inputAfterHook ?? {}, { signal, toolUseId: call.id });
|
|
317
524
|
if (permResult.behavior === "deny") {
|
|
318
525
|
const denyContent = `Permission denied: ${permResult.message}`;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
isError: true,
|
|
325
|
-
uuid: uuid()
|
|
326
|
-
};
|
|
526
|
+
permissionDenials.push({
|
|
527
|
+
tool_name: call.name,
|
|
528
|
+
tool_use_id: call.id,
|
|
529
|
+
tool_input: inputAfterHook ?? {}
|
|
530
|
+
});
|
|
327
531
|
toolResults.push({
|
|
328
532
|
type: "tool_result",
|
|
329
533
|
tool_use_id: call.id,
|
|
@@ -336,13 +540,6 @@ async function* agentLoop(prompt, options) {
|
|
|
336
540
|
continue;
|
|
337
541
|
}
|
|
338
542
|
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
543
|
let result;
|
|
347
544
|
if (call.name === "memory" && nativeMemoryTool) {
|
|
348
545
|
try {
|
|
@@ -361,28 +558,36 @@ async function* agentLoop(prompt, options) {
|
|
|
361
558
|
};
|
|
362
559
|
result = await tools.execute(call.name, toolInput, toolCtx);
|
|
363
560
|
}
|
|
561
|
+
if (call.name === "ExitPlanMode") {
|
|
562
|
+
permissions.setMode("default");
|
|
563
|
+
}
|
|
364
564
|
if (debug) {
|
|
365
565
|
console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
|
|
366
566
|
}
|
|
367
567
|
if (hooks) {
|
|
368
568
|
if (result.isError) {
|
|
369
|
-
await hooks.fire("PostToolUseFailure", {
|
|
569
|
+
await hooks.fire("PostToolUseFailure", {
|
|
570
|
+
event: "PostToolUseFailure",
|
|
571
|
+
hook_event_name: "PostToolUseFailure",
|
|
572
|
+
tool_name: call.name,
|
|
573
|
+
tool_result: result.content,
|
|
574
|
+
tool_error: true,
|
|
575
|
+
session_id: sessionId
|
|
576
|
+
}, call.id, { signal });
|
|
370
577
|
} else {
|
|
371
|
-
const postResult = await hooks.fire("PostToolUse", {
|
|
578
|
+
const postResult = await hooks.fire("PostToolUse", {
|
|
579
|
+
event: "PostToolUse",
|
|
580
|
+
hook_event_name: "PostToolUse",
|
|
581
|
+
tool_name: call.name,
|
|
582
|
+
tool_result: result.content,
|
|
583
|
+
session_id: sessionId
|
|
584
|
+
}, call.id, { signal });
|
|
372
585
|
if (postResult?.additionalContext) {
|
|
373
586
|
result.content += `
|
|
374
587
|
${postResult.additionalContext}`;
|
|
375
588
|
}
|
|
376
589
|
}
|
|
377
590
|
}
|
|
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
591
|
toolResults.push({
|
|
387
592
|
type: "tool_result",
|
|
388
593
|
tool_use_id: call.id,
|
|
@@ -394,20 +599,19 @@ ${postResult.additionalContext}`;
|
|
|
394
599
|
if (sessionLogger) {
|
|
395
600
|
sessionLogger("user", toolResults, null);
|
|
396
601
|
}
|
|
602
|
+
yield {
|
|
603
|
+
type: "user",
|
|
604
|
+
message: {
|
|
605
|
+
role: "user",
|
|
606
|
+
content: toolResults
|
|
607
|
+
},
|
|
608
|
+
parent_tool_use_id: null,
|
|
609
|
+
isSynthetic: true,
|
|
610
|
+
uuid: uuid(),
|
|
611
|
+
session_id: sessionId
|
|
612
|
+
};
|
|
397
613
|
}
|
|
398
614
|
}
|
|
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
615
|
export {
|
|
412
616
|
agentLoop
|
|
413
617
|
};
|