@codeany/open-agent-sdk 0.2.2 → 0.2.3

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.
Files changed (280) hide show
  1. package/dist/agent.d.ts +113 -0
  2. package/dist/agent.d.ts.map +1 -0
  3. package/dist/agent.js +446 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/engine.d.ts +62 -0
  6. package/dist/engine.d.ts.map +1 -0
  7. package/dist/engine.js +496 -0
  8. package/dist/engine.js.map +1 -0
  9. package/dist/hooks.d.ts +111 -0
  10. package/dist/hooks.d.ts.map +1 -0
  11. package/dist/hooks.js +179 -0
  12. package/dist/hooks.js.map +1 -0
  13. package/dist/index.d.ts +66 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +133 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/mcp/client.d.ts +19 -0
  18. package/dist/mcp/client.d.ts.map +1 -0
  19. package/dist/mcp/client.js +126 -0
  20. package/dist/mcp/client.js.map +1 -0
  21. package/dist/providers/anthropic.d.ts +17 -0
  22. package/dist/providers/anthropic.d.ts.map +1 -0
  23. package/dist/providers/anthropic.js +47 -0
  24. package/dist/providers/anthropic.js.map +1 -0
  25. package/dist/providers/index.d.ts +20 -0
  26. package/dist/providers/index.d.ts.map +1 -0
  27. package/dist/providers/index.js +26 -0
  28. package/dist/providers/index.js.map +1 -0
  29. package/dist/providers/openai.d.ts +26 -0
  30. package/dist/providers/openai.d.ts.map +1 -0
  31. package/dist/providers/openai.js +212 -0
  32. package/dist/providers/openai.js.map +1 -0
  33. package/dist/providers/types.d.ts +84 -0
  34. package/dist/providers/types.d.ts.map +1 -0
  35. package/dist/providers/types.js +11 -0
  36. package/dist/providers/types.js.map +1 -0
  37. package/dist/sdk-mcp-server.d.ts +52 -0
  38. package/dist/sdk-mcp-server.d.ts.map +1 -0
  39. package/dist/sdk-mcp-server.js +57 -0
  40. package/dist/sdk-mcp-server.js.map +1 -0
  41. package/dist/session.d.ts +73 -0
  42. package/dist/session.d.ts.map +1 -0
  43. package/dist/session.js +159 -0
  44. package/dist/session.js.map +1 -0
  45. package/dist/skills/bundled/commit.d.ts +7 -0
  46. package/dist/skills/bundled/commit.d.ts.map +1 -0
  47. package/dist/skills/bundled/commit.js +35 -0
  48. package/dist/skills/bundled/commit.js.map +1 -0
  49. package/dist/skills/bundled/debug.d.ts +7 -0
  50. package/dist/skills/bundled/debug.d.ts.map +1 -0
  51. package/{src/skills/bundled/debug.ts → dist/skills/bundled/debug.js} +20 -22
  52. package/dist/skills/bundled/debug.js.map +1 -0
  53. package/dist/skills/bundled/index.d.ts +11 -0
  54. package/dist/skills/bundled/index.d.ts.map +1 -0
  55. package/dist/skills/bundled/index.js +26 -0
  56. package/dist/skills/bundled/index.js.map +1 -0
  57. package/dist/skills/bundled/review.d.ts +7 -0
  58. package/dist/skills/bundled/review.d.ts.map +1 -0
  59. package/{src/skills/bundled/review.ts → dist/skills/bundled/review.js} +18 -21
  60. package/dist/skills/bundled/review.js.map +1 -0
  61. package/dist/skills/bundled/simplify.d.ts +8 -0
  62. package/dist/skills/bundled/simplify.d.ts.map +1 -0
  63. package/{src/skills/bundled/simplify.ts → dist/skills/bundled/simplify.js} +16 -19
  64. package/dist/skills/bundled/simplify.js.map +1 -0
  65. package/dist/skills/bundled/test.d.ts +7 -0
  66. package/dist/skills/bundled/test.d.ts.map +1 -0
  67. package/{src/skills/bundled/test.ts → dist/skills/bundled/test.js} +18 -21
  68. package/dist/skills/bundled/test.js.map +1 -0
  69. package/dist/skills/index.d.ts +7 -0
  70. package/dist/skills/index.d.ts.map +1 -0
  71. package/dist/skills/index.js +8 -0
  72. package/dist/skills/index.js.map +1 -0
  73. package/dist/skills/registry.d.ts +43 -0
  74. package/dist/skills/registry.d.ts.map +1 -0
  75. package/dist/skills/registry.js +111 -0
  76. package/dist/skills/registry.js.map +1 -0
  77. package/dist/skills/types.d.ts +83 -0
  78. package/dist/skills/types.d.ts.map +1 -0
  79. package/dist/skills/types.js +8 -0
  80. package/dist/skills/types.js.map +1 -0
  81. package/dist/tool-helper.d.ts +73 -0
  82. package/dist/tool-helper.d.ts.map +1 -0
  83. package/dist/tool-helper.js +86 -0
  84. package/dist/tool-helper.js.map +1 -0
  85. package/dist/tools/agent-tool.d.ts +17 -0
  86. package/dist/tools/agent-tool.d.ts.map +1 -0
  87. package/dist/tools/agent-tool.js +146 -0
  88. package/dist/tools/agent-tool.js.map +1 -0
  89. package/dist/tools/ask-user.d.ts +18 -0
  90. package/dist/tools/ask-user.d.ts.map +1 -0
  91. package/dist/tools/ask-user.js +72 -0
  92. package/dist/tools/ask-user.js.map +1 -0
  93. package/dist/tools/bash.d.ts +5 -0
  94. package/dist/tools/bash.d.ts.map +1 -0
  95. package/dist/tools/bash.js +67 -0
  96. package/dist/tools/bash.js.map +1 -0
  97. package/dist/tools/config-tool.d.ts +20 -0
  98. package/dist/tools/config-tool.d.ts.map +1 -0
  99. package/dist/tools/config-tool.js +83 -0
  100. package/dist/tools/config-tool.js.map +1 -0
  101. package/dist/tools/cron-tools.d.ts +33 -0
  102. package/dist/tools/cron-tools.d.ts.map +1 -0
  103. package/dist/tools/cron-tools.js +128 -0
  104. package/dist/tools/cron-tools.js.map +1 -0
  105. package/dist/tools/edit.d.ts +5 -0
  106. package/dist/tools/edit.d.ts.map +1 -0
  107. package/dist/tools/edit.js +70 -0
  108. package/dist/tools/edit.js.map +1 -0
  109. package/dist/tools/glob.d.ts +5 -0
  110. package/dist/tools/glob.d.ts.map +1 -0
  111. package/dist/tools/glob.js +75 -0
  112. package/dist/tools/glob.js.map +1 -0
  113. package/dist/tools/grep.d.ts +5 -0
  114. package/dist/tools/grep.d.ts.map +1 -0
  115. package/dist/tools/grep.js +168 -0
  116. package/dist/tools/grep.js.map +1 -0
  117. package/dist/tools/index.d.ts +45 -0
  118. package/dist/tools/index.d.ts.map +1 -0
  119. package/dist/tools/index.js +162 -0
  120. package/dist/tools/index.js.map +1 -0
  121. package/dist/tools/lsp-tool.d.ts +9 -0
  122. package/dist/tools/lsp-tool.d.ts.map +1 -0
  123. package/dist/tools/lsp-tool.js +137 -0
  124. package/dist/tools/lsp-tool.js.map +1 -0
  125. package/dist/tools/mcp-resource-tools.d.ts +14 -0
  126. package/dist/tools/mcp-resource-tools.d.ts.map +1 -0
  127. package/dist/tools/mcp-resource-tools.js +117 -0
  128. package/dist/tools/mcp-resource-tools.js.map +1 -0
  129. package/dist/tools/notebook-edit.d.ts +5 -0
  130. package/dist/tools/notebook-edit.d.ts.map +1 -0
  131. package/dist/tools/notebook-edit.js +85 -0
  132. package/dist/tools/notebook-edit.js.map +1 -0
  133. package/dist/tools/plan-tools.d.ts +12 -0
  134. package/dist/tools/plan-tools.d.ts.map +1 -0
  135. package/dist/tools/plan-tools.js +77 -0
  136. package/dist/tools/plan-tools.js.map +1 -0
  137. package/dist/tools/read.d.ts +5 -0
  138. package/dist/tools/read.d.ts.map +1 -0
  139. package/dist/tools/read.js +66 -0
  140. package/dist/tools/read.js.map +1 -0
  141. package/dist/tools/send-message.d.ts +31 -0
  142. package/dist/tools/send-message.d.ts.map +1 -0
  143. package/dist/tools/send-message.js +77 -0
  144. package/dist/tools/send-message.js.map +1 -0
  145. package/dist/tools/skill-tool.d.ts +9 -0
  146. package/dist/tools/skill-tool.d.ts.map +1 -0
  147. package/dist/tools/skill-tool.js +115 -0
  148. package/dist/tools/skill-tool.js.map +1 -0
  149. package/dist/tools/task-tools.d.ts +48 -0
  150. package/dist/tools/task-tools.d.ts.map +1 -0
  151. package/dist/tools/task-tools.js +242 -0
  152. package/dist/tools/task-tools.js.map +1 -0
  153. package/dist/tools/team-tools.d.ts +34 -0
  154. package/dist/tools/team-tools.d.ts.map +1 -0
  155. package/dist/tools/team-tools.js +103 -0
  156. package/dist/tools/team-tools.js.map +1 -0
  157. package/dist/tools/todo-tool.d.ts +22 -0
  158. package/dist/tools/todo-tool.d.ts.map +1 -0
  159. package/dist/tools/todo-tool.js +93 -0
  160. package/dist/tools/todo-tool.js.map +1 -0
  161. package/dist/tools/tool-search.d.ts +13 -0
  162. package/dist/tools/tool-search.d.ts.map +1 -0
  163. package/dist/tools/tool-search.js +76 -0
  164. package/dist/tools/tool-search.js.map +1 -0
  165. package/dist/tools/types.d.ts +29 -0
  166. package/dist/tools/types.d.ts.map +1 -0
  167. package/dist/tools/types.js +52 -0
  168. package/dist/tools/types.js.map +1 -0
  169. package/dist/tools/web-fetch.d.ts +5 -0
  170. package/dist/tools/web-fetch.d.ts.map +1 -0
  171. package/dist/tools/web-fetch.js +60 -0
  172. package/dist/tools/web-fetch.js.map +1 -0
  173. package/dist/tools/web-search.d.ts +5 -0
  174. package/dist/tools/web-search.d.ts.map +1 -0
  175. package/dist/tools/web-search.js +77 -0
  176. package/dist/tools/web-search.js.map +1 -0
  177. package/dist/tools/worktree-tools.d.ts +10 -0
  178. package/dist/tools/worktree-tools.d.ts.map +1 -0
  179. package/dist/tools/worktree-tools.js +130 -0
  180. package/dist/tools/worktree-tools.js.map +1 -0
  181. package/dist/tools/write.d.ts +5 -0
  182. package/dist/tools/write.d.ts.map +1 -0
  183. package/dist/tools/write.js +40 -0
  184. package/dist/tools/write.js.map +1 -0
  185. package/dist/types.d.ts +423 -0
  186. package/dist/types.d.ts.map +1 -0
  187. package/dist/types.js +5 -0
  188. package/dist/types.js.map +1 -0
  189. package/dist/utils/compact.d.ts +44 -0
  190. package/dist/utils/compact.d.ts.map +1 -0
  191. package/dist/utils/compact.js +162 -0
  192. package/dist/utils/compact.js.map +1 -0
  193. package/dist/utils/context.d.ts +35 -0
  194. package/dist/utils/context.d.ts.map +1 -0
  195. package/dist/utils/context.js +174 -0
  196. package/dist/utils/context.js.map +1 -0
  197. package/dist/utils/fileCache.d.ts +64 -0
  198. package/dist/utils/fileCache.d.ts.map +1 -0
  199. package/dist/utils/fileCache.js +117 -0
  200. package/dist/utils/fileCache.js.map +1 -0
  201. package/dist/utils/messages.d.ts +57 -0
  202. package/dist/utils/messages.d.ts.map +1 -0
  203. package/dist/utils/messages.js +152 -0
  204. package/dist/utils/messages.js.map +1 -0
  205. package/dist/utils/retry.d.ts +48 -0
  206. package/dist/utils/retry.d.ts.map +1 -0
  207. package/dist/utils/retry.js +111 -0
  208. package/dist/utils/retry.js.map +1 -0
  209. package/dist/utils/tokens.d.ts +57 -0
  210. package/dist/utils/tokens.d.ts.map +1 -0
  211. package/dist/utils/tokens.js +134 -0
  212. package/dist/utils/tokens.js.map +1 -0
  213. package/package.json +6 -1
  214. package/.env.example +0 -8
  215. package/examples/01-simple-query.ts +0 -43
  216. package/examples/02-multi-tool.ts +0 -44
  217. package/examples/03-multi-turn.ts +0 -39
  218. package/examples/04-prompt-api.ts +0 -29
  219. package/examples/05-custom-system-prompt.ts +0 -26
  220. package/examples/06-mcp-server.ts +0 -49
  221. package/examples/07-custom-tools.ts +0 -87
  222. package/examples/08-official-api-compat.ts +0 -38
  223. package/examples/09-subagents.ts +0 -48
  224. package/examples/10-permissions.ts +0 -40
  225. package/examples/11-custom-mcp-tools.ts +0 -101
  226. package/examples/12-skills.ts +0 -88
  227. package/examples/13-hooks.ts +0 -88
  228. package/examples/14-openai-compat.ts +0 -71
  229. package/examples/web/index.html +0 -365
  230. package/examples/web/server.ts +0 -157
  231. package/src/agent.ts +0 -516
  232. package/src/engine.ts +0 -633
  233. package/src/hooks.ts +0 -261
  234. package/src/index.ts +0 -426
  235. package/src/mcp/client.ts +0 -150
  236. package/src/providers/anthropic.ts +0 -60
  237. package/src/providers/index.ts +0 -34
  238. package/src/providers/openai.ts +0 -315
  239. package/src/providers/types.ts +0 -85
  240. package/src/sdk-mcp-server.ts +0 -78
  241. package/src/session.ts +0 -227
  242. package/src/skills/bundled/commit.ts +0 -38
  243. package/src/skills/bundled/index.ts +0 -28
  244. package/src/skills/index.ts +0 -25
  245. package/src/skills/registry.ts +0 -133
  246. package/src/skills/types.ts +0 -99
  247. package/src/tool-helper.ts +0 -127
  248. package/src/tools/agent-tool.ts +0 -164
  249. package/src/tools/ask-user.ts +0 -79
  250. package/src/tools/bash.ts +0 -75
  251. package/src/tools/config-tool.ts +0 -89
  252. package/src/tools/cron-tools.ts +0 -153
  253. package/src/tools/edit.ts +0 -74
  254. package/src/tools/glob.ts +0 -77
  255. package/src/tools/grep.ts +0 -168
  256. package/src/tools/index.ts +0 -240
  257. package/src/tools/lsp-tool.ts +0 -163
  258. package/src/tools/mcp-resource-tools.ts +0 -125
  259. package/src/tools/notebook-edit.ts +0 -93
  260. package/src/tools/plan-tools.ts +0 -88
  261. package/src/tools/read.ts +0 -73
  262. package/src/tools/send-message.ts +0 -96
  263. package/src/tools/skill-tool.ts +0 -133
  264. package/src/tools/task-tools.ts +0 -290
  265. package/src/tools/team-tools.ts +0 -128
  266. package/src/tools/todo-tool.ts +0 -112
  267. package/src/tools/tool-search.ts +0 -87
  268. package/src/tools/types.ts +0 -66
  269. package/src/tools/web-fetch.ts +0 -66
  270. package/src/tools/web-search.ts +0 -86
  271. package/src/tools/worktree-tools.ts +0 -140
  272. package/src/tools/write.ts +0 -42
  273. package/src/types.ts +0 -486
  274. package/src/utils/compact.ts +0 -207
  275. package/src/utils/context.ts +0 -191
  276. package/src/utils/fileCache.ts +0 -148
  277. package/src/utils/messages.ts +0 -195
  278. package/src/utils/retry.ts +0 -140
  279. package/src/utils/tokens.ts +0 -145
  280. package/tsconfig.json +0 -19
package/src/engine.ts DELETED
@@ -1,633 +0,0 @@
1
- /**
2
- * QueryEngine - Core agentic loop
3
- *
4
- * Manages the full conversation lifecycle:
5
- * 1. Take user prompt
6
- * 2. Build system prompt with context (git status, project context, tools)
7
- * 3. Call LLM API with tools (via provider abstraction)
8
- * 4. Stream response
9
- * 5. Execute tool calls (concurrent for read-only, serial for mutations)
10
- * 6. Send results back, repeat until done
11
- * 7. Auto-compact when context exceeds threshold
12
- * 8. Retry with exponential backoff on transient errors
13
- */
14
-
15
- import type {
16
- SDKMessage,
17
- QueryEngineConfig,
18
- ToolDefinition,
19
- ToolResult,
20
- ToolContext,
21
- TokenUsage,
22
- } from './types.js'
23
- import type {
24
- LLMProvider,
25
- CreateMessageResponse,
26
- NormalizedMessageParam,
27
- NormalizedTool,
28
- } from './providers/types.js'
29
- import {
30
- estimateMessagesTokens,
31
- estimateCost,
32
- getAutoCompactThreshold,
33
- } from './utils/tokens.js'
34
- import {
35
- shouldAutoCompact,
36
- compactConversation,
37
- microCompactMessages,
38
- createAutoCompactState,
39
- type AutoCompactState,
40
- } from './utils/compact.js'
41
- import {
42
- withRetry,
43
- isPromptTooLongError,
44
- } from './utils/retry.js'
45
- import { getSystemContext, getUserContext } from './utils/context.js'
46
- import { normalizeMessagesForAPI } from './utils/messages.js'
47
- import type { HookRegistry, HookInput, HookOutput } from './hooks.js'
48
-
49
- // ============================================================================
50
- // Tool format conversion
51
- // ============================================================================
52
-
53
- /** Convert a ToolDefinition to the normalized provider tool format. */
54
- function toProviderTool(tool: ToolDefinition): NormalizedTool {
55
- return {
56
- name: tool.name,
57
- description: tool.description,
58
- input_schema: tool.inputSchema,
59
- }
60
- }
61
-
62
- // ============================================================================
63
- // ToolUseBlock (internal type for extracted tool_use blocks)
64
- // ============================================================================
65
-
66
- interface ToolUseBlock {
67
- type: 'tool_use'
68
- id: string
69
- name: string
70
- input: any
71
- }
72
-
73
- // ============================================================================
74
- // System Prompt Builder
75
- // ============================================================================
76
-
77
- async function buildSystemPrompt(config: QueryEngineConfig): Promise<string> {
78
- if (config.systemPrompt) {
79
- const base = config.systemPrompt
80
- return config.appendSystemPrompt
81
- ? base + '\n\n' + config.appendSystemPrompt
82
- : base
83
- }
84
-
85
- const parts: string[] = []
86
-
87
- parts.push(
88
- 'You are an AI assistant with access to tools. Use the tools provided to help the user accomplish their tasks.',
89
- 'You should use tools when they would help you complete the task more accurately or efficiently.',
90
- )
91
-
92
- // List available tools with descriptions
93
- parts.push('\n# Available Tools\n')
94
- for (const tool of config.tools) {
95
- parts.push(`- **${tool.name}**: ${tool.description}`)
96
- }
97
-
98
- // Add agent definitions
99
- if (config.agents && Object.keys(config.agents).length > 0) {
100
- parts.push('\n# Available Subagents\n')
101
- for (const [name, def] of Object.entries(config.agents)) {
102
- parts.push(`- **${name}**: ${def.description}`)
103
- }
104
- }
105
-
106
- // System context (git status, etc.)
107
- try {
108
- const sysCtx = await getSystemContext(config.cwd)
109
- if (sysCtx) {
110
- parts.push('\n# Environment\n')
111
- parts.push(sysCtx)
112
- }
113
- } catch {
114
- // Context is best-effort
115
- }
116
-
117
- // User context (AGENT.md, date)
118
- try {
119
- const userCtx = await getUserContext(config.cwd)
120
- if (userCtx) {
121
- parts.push('\n# Project Context\n')
122
- parts.push(userCtx)
123
- }
124
- } catch {
125
- // Context is best-effort
126
- }
127
-
128
- // Working directory
129
- parts.push(`\n# Working Directory\n${config.cwd}`)
130
-
131
- if (config.appendSystemPrompt) {
132
- parts.push('\n' + config.appendSystemPrompt)
133
- }
134
-
135
- return parts.join('\n')
136
- }
137
-
138
- // ============================================================================
139
- // QueryEngine
140
- // ============================================================================
141
-
142
- export class QueryEngine {
143
- private config: QueryEngineConfig
144
- private provider: LLMProvider
145
- public messages: NormalizedMessageParam[] = []
146
- private totalUsage: TokenUsage = { input_tokens: 0, output_tokens: 0 }
147
- private totalCost = 0
148
- private turnCount = 0
149
- private compactState: AutoCompactState
150
- private sessionId: string
151
- private apiTimeMs = 0
152
- private hookRegistry?: HookRegistry
153
-
154
- constructor(config: QueryEngineConfig) {
155
- this.config = config
156
- this.provider = config.provider
157
- this.compactState = createAutoCompactState()
158
- this.sessionId = config.sessionId || crypto.randomUUID()
159
- this.hookRegistry = config.hookRegistry
160
- }
161
-
162
- /**
163
- * Execute hooks for a lifecycle event.
164
- * Returns hook outputs; never throws.
165
- */
166
- private async executeHooks(
167
- event: import('./hooks.js').HookEvent,
168
- extra?: Partial<HookInput>,
169
- ): Promise<HookOutput[]> {
170
- if (!this.hookRegistry?.hasHooks(event)) return []
171
- try {
172
- return await this.hookRegistry.execute(event, {
173
- event,
174
- sessionId: this.sessionId,
175
- cwd: this.config.cwd,
176
- ...extra,
177
- })
178
- } catch {
179
- return []
180
- }
181
- }
182
-
183
- /**
184
- * Submit a user message and run the agentic loop.
185
- * Yields SDKMessage events as the agent works.
186
- */
187
- async *submitMessage(
188
- prompt: string | any[],
189
- ): AsyncGenerator<SDKMessage> {
190
- // Hook: SessionStart
191
- await this.executeHooks('SessionStart')
192
-
193
- // Hook: UserPromptSubmit
194
- const userHookResults = await this.executeHooks('UserPromptSubmit', {
195
- toolInput: prompt,
196
- })
197
- // Check if any hook blocks the submission
198
- if (userHookResults.some((r) => r.block)) {
199
- yield {
200
- type: 'result',
201
- subtype: 'error_during_execution',
202
- is_error: true,
203
- usage: this.totalUsage,
204
- num_turns: 0,
205
- cost: 0,
206
- errors: ['Blocked by UserPromptSubmit hook'],
207
- }
208
- return
209
- }
210
-
211
- // Add user message
212
- this.messages.push({ role: 'user', content: prompt as any })
213
-
214
- // Build tool definitions for provider
215
- const tools = this.config.tools.map(toProviderTool)
216
-
217
- // Build system prompt
218
- const systemPrompt = await buildSystemPrompt(this.config)
219
-
220
- // Emit init system message
221
- yield {
222
- type: 'system',
223
- subtype: 'init',
224
- session_id: this.sessionId,
225
- tools: this.config.tools.map(t => t.name),
226
- model: this.config.model,
227
- cwd: this.config.cwd,
228
- mcp_servers: [],
229
- permission_mode: 'bypassPermissions',
230
- } as SDKMessage
231
-
232
- // Agentic loop
233
- let turnsRemaining = this.config.maxTurns
234
- let budgetExceeded = false
235
- let maxOutputRecoveryAttempts = 0
236
- const MAX_OUTPUT_RECOVERY = 3
237
-
238
- while (turnsRemaining > 0) {
239
- if (this.config.abortSignal?.aborted) break
240
-
241
- // Check budget
242
- if (this.config.maxBudgetUsd && this.totalCost >= this.config.maxBudgetUsd) {
243
- budgetExceeded = true
244
- break
245
- }
246
-
247
- // Auto-compact if context is too large
248
- if (shouldAutoCompact(this.messages as any[], this.config.model, this.compactState)) {
249
- await this.executeHooks('PreCompact')
250
- try {
251
- const result = await compactConversation(
252
- this.provider,
253
- this.config.model,
254
- this.messages as any[],
255
- this.compactState,
256
- )
257
- this.messages = result.compactedMessages as NormalizedMessageParam[]
258
- this.compactState = result.state
259
- await this.executeHooks('PostCompact')
260
- } catch {
261
- // Continue with uncompacted messages
262
- }
263
- }
264
-
265
- // Micro-compact: truncate large tool results
266
- const apiMessages = microCompactMessages(
267
- normalizeMessagesForAPI(this.messages as any[]),
268
- ) as NormalizedMessageParam[]
269
-
270
- this.turnCount++
271
- turnsRemaining--
272
-
273
- // Make API call with retry via provider
274
- let response: CreateMessageResponse
275
- const apiStart = performance.now()
276
- try {
277
- response = await withRetry(
278
- async () => {
279
- return this.provider.createMessage({
280
- model: this.config.model,
281
- maxTokens: this.config.maxTokens,
282
- system: systemPrompt,
283
- messages: apiMessages,
284
- tools: tools.length > 0 ? tools : undefined,
285
- thinking:
286
- this.config.thinking?.type === 'enabled' &&
287
- this.config.thinking.budgetTokens
288
- ? {
289
- type: 'enabled',
290
- budget_tokens: this.config.thinking.budgetTokens,
291
- }
292
- : undefined,
293
- })
294
- },
295
- undefined,
296
- this.config.abortSignal,
297
- )
298
- } catch (err: any) {
299
- // Handle prompt-too-long by compacting
300
- if (isPromptTooLongError(err) && !this.compactState.compacted) {
301
- try {
302
- const result = await compactConversation(
303
- this.provider,
304
- this.config.model,
305
- this.messages as any[],
306
- this.compactState,
307
- )
308
- this.messages = result.compactedMessages as NormalizedMessageParam[]
309
- this.compactState = result.state
310
- turnsRemaining++ // Retry this turn
311
- this.turnCount--
312
- continue
313
- } catch {
314
- // Can't compact, give up
315
- }
316
- }
317
-
318
- const message = err?.message ? String(err.message) : String(err)
319
-
320
- yield {
321
- type: 'result',
322
- subtype: 'error',
323
- usage: this.totalUsage,
324
- num_turns: this.turnCount,
325
- cost: this.totalCost,
326
- errors: [message],
327
- }
328
- return
329
- }
330
-
331
- // Track API timing
332
- this.apiTimeMs += performance.now() - apiStart
333
-
334
- // Track usage (normalized by provider)
335
- if (response.usage) {
336
- this.totalUsage.input_tokens += response.usage.input_tokens
337
- this.totalUsage.output_tokens += response.usage.output_tokens
338
- if (response.usage.cache_creation_input_tokens) {
339
- this.totalUsage.cache_creation_input_tokens =
340
- (this.totalUsage.cache_creation_input_tokens || 0) +
341
- response.usage.cache_creation_input_tokens
342
- }
343
- if (response.usage.cache_read_input_tokens) {
344
- this.totalUsage.cache_read_input_tokens =
345
- (this.totalUsage.cache_read_input_tokens || 0) +
346
- response.usage.cache_read_input_tokens
347
- }
348
- this.totalCost += estimateCost(this.config.model, response.usage)
349
- }
350
-
351
- // Add assistant message to conversation
352
- this.messages.push({ role: 'assistant', content: response.content as any })
353
-
354
- // Yield assistant message
355
- yield {
356
- type: 'assistant',
357
- message: {
358
- role: 'assistant',
359
- content: response.content as any,
360
- },
361
- }
362
-
363
- // Handle max_output_tokens recovery
364
- if (
365
- response.stopReason === 'max_tokens' &&
366
- maxOutputRecoveryAttempts < MAX_OUTPUT_RECOVERY
367
- ) {
368
- maxOutputRecoveryAttempts++
369
- // Add continuation prompt
370
- this.messages.push({
371
- role: 'user',
372
- content: 'Please continue from where you left off.',
373
- })
374
- continue
375
- }
376
-
377
- // Check for tool use
378
- const toolUseBlocks = response.content.filter(
379
- (block): block is ToolUseBlock => block.type === 'tool_use',
380
- )
381
-
382
- if (toolUseBlocks.length === 0) {
383
- break // No tool calls - agent is done
384
- }
385
-
386
- // Reset max_output recovery counter on successful tool use
387
- maxOutputRecoveryAttempts = 0
388
-
389
- // Execute tools (concurrent read-only, serial mutations)
390
- const toolResults = await this.executeTools(toolUseBlocks)
391
-
392
- // Yield tool results
393
- for (const result of toolResults) {
394
- yield {
395
- type: 'tool_result',
396
- result: {
397
- tool_use_id: result.tool_use_id,
398
- tool_name: result.tool_name || '',
399
- output:
400
- typeof result.content === 'string'
401
- ? result.content
402
- : JSON.stringify(result.content),
403
- },
404
- }
405
- }
406
-
407
- // Add tool results to conversation
408
- this.messages.push({
409
- role: 'user',
410
- content: toolResults.map((r) => ({
411
- type: 'tool_result' as const,
412
- tool_use_id: r.tool_use_id,
413
- content:
414
- typeof r.content === 'string'
415
- ? r.content
416
- : JSON.stringify(r.content),
417
- is_error: r.is_error,
418
- })),
419
- })
420
-
421
- if (response.stopReason === 'end_turn') break
422
- }
423
-
424
- // Hook: Stop (end of agentic loop)
425
- await this.executeHooks('Stop')
426
-
427
- // Hook: SessionEnd
428
- await this.executeHooks('SessionEnd')
429
-
430
- // Yield enriched final result
431
- const endSubtype = budgetExceeded
432
- ? 'error_max_budget_usd'
433
- : turnsRemaining <= 0
434
- ? 'error_max_turns'
435
- : 'success'
436
-
437
- yield {
438
- type: 'result',
439
- subtype: endSubtype,
440
- session_id: this.sessionId,
441
- is_error: endSubtype !== 'success',
442
- num_turns: this.turnCount,
443
- total_cost_usd: this.totalCost,
444
- duration_api_ms: Math.round(this.apiTimeMs),
445
- usage: this.totalUsage,
446
- model_usage: { [this.config.model]: { input_tokens: this.totalUsage.input_tokens, output_tokens: this.totalUsage.output_tokens } },
447
- cost: this.totalCost,
448
- }
449
- }
450
-
451
- /**
452
- * Execute tool calls with concurrency control.
453
- *
454
- * Read-only tools run concurrently (up to 10 at a time).
455
- * Mutation tools run sequentially.
456
- */
457
- private async executeTools(
458
- toolUseBlocks: ToolUseBlock[],
459
- ): Promise<(ToolResult & { tool_name?: string })[]> {
460
- const context: ToolContext = {
461
- cwd: this.config.cwd,
462
- abortSignal: this.config.abortSignal,
463
- provider: this.provider,
464
- model: this.config.model,
465
- apiType: this.provider.apiType,
466
- }
467
-
468
- const MAX_CONCURRENCY = parseInt(
469
- process.env.AGENT_SDK_MAX_TOOL_CONCURRENCY || '10',
470
- )
471
-
472
- // Partition into read-only (concurrent) and mutation (serial)
473
- const readOnly: Array<{ block: ToolUseBlock; tool?: ToolDefinition }> = []
474
- const mutations: Array<{ block: ToolUseBlock; tool?: ToolDefinition }> = []
475
-
476
- for (const block of toolUseBlocks) {
477
- const tool = this.config.tools.find((t) => t.name === block.name)
478
- if (tool?.isReadOnly?.()) {
479
- readOnly.push({ block, tool })
480
- } else {
481
- mutations.push({ block, tool })
482
- }
483
- }
484
-
485
- const results: (ToolResult & { tool_name?: string })[] = []
486
-
487
- // Execute read-only tools concurrently (batched by MAX_CONCURRENCY)
488
- for (let i = 0; i < readOnly.length; i += MAX_CONCURRENCY) {
489
- const batch = readOnly.slice(i, i + MAX_CONCURRENCY)
490
- const batchResults = await Promise.all(
491
- batch.map((item) =>
492
- this.executeSingleTool(item.block, item.tool, context),
493
- ),
494
- )
495
- results.push(...batchResults)
496
- }
497
-
498
- // Execute mutation tools sequentially
499
- for (const item of mutations) {
500
- const result = await this.executeSingleTool(item.block, item.tool, context)
501
- results.push(result)
502
- }
503
-
504
- return results
505
- }
506
-
507
- /**
508
- * Execute a single tool with permission checking.
509
- */
510
- private async executeSingleTool(
511
- block: ToolUseBlock,
512
- tool: ToolDefinition | undefined,
513
- context: ToolContext,
514
- ): Promise<ToolResult & { tool_name?: string }> {
515
- if (!tool) {
516
- return {
517
- type: 'tool_result',
518
- tool_use_id: block.id,
519
- content: `Error: Unknown tool "${block.name}"`,
520
- is_error: true,
521
- tool_name: block.name,
522
- }
523
- }
524
-
525
- // Check enabled
526
- if (tool.isEnabled && !tool.isEnabled()) {
527
- return {
528
- type: 'tool_result',
529
- tool_use_id: block.id,
530
- content: `Error: Tool "${block.name}" is not enabled`,
531
- is_error: true,
532
- tool_name: block.name,
533
- }
534
- }
535
-
536
- // Check permissions
537
- if (this.config.canUseTool) {
538
- try {
539
- const permission = await this.config.canUseTool(tool, block.input)
540
- if (permission.behavior === 'deny') {
541
- return {
542
- type: 'tool_result',
543
- tool_use_id: block.id,
544
- content: permission.message || `Permission denied for tool "${block.name}"`,
545
- is_error: true,
546
- tool_name: block.name,
547
- }
548
- }
549
- if (permission.updatedInput !== undefined) {
550
- block = { ...block, input: permission.updatedInput }
551
- }
552
- } catch (err: any) {
553
- return {
554
- type: 'tool_result',
555
- tool_use_id: block.id,
556
- content: `Permission check error: ${err.message}`,
557
- is_error: true,
558
- tool_name: block.name,
559
- }
560
- }
561
- }
562
-
563
- // Hook: PreToolUse
564
- const preHookResults = await this.executeHooks('PreToolUse', {
565
- toolName: block.name,
566
- toolInput: block.input,
567
- toolUseId: block.id,
568
- })
569
- // Check if any hook blocks this tool
570
- if (preHookResults.some((r) => r.block)) {
571
- const msg = preHookResults.find((r) => r.message)?.message || 'Blocked by PreToolUse hook'
572
- return {
573
- type: 'tool_result',
574
- tool_use_id: block.id,
575
- content: msg,
576
- is_error: true,
577
- tool_name: block.name,
578
- }
579
- }
580
-
581
- // Execute the tool
582
- try {
583
- const result = await tool.call(block.input, context)
584
-
585
- // Hook: PostToolUse
586
- await this.executeHooks('PostToolUse', {
587
- toolName: block.name,
588
- toolInput: block.input,
589
- toolOutput: typeof result.content === 'string' ? result.content : JSON.stringify(result.content),
590
- toolUseId: block.id,
591
- })
592
-
593
- return { ...result, tool_use_id: block.id, tool_name: block.name }
594
- } catch (err: any) {
595
- // Hook: PostToolUseFailure
596
- await this.executeHooks('PostToolUseFailure', {
597
- toolName: block.name,
598
- toolInput: block.input,
599
- toolUseId: block.id,
600
- error: err.message,
601
- })
602
-
603
- return {
604
- type: 'tool_result',
605
- tool_use_id: block.id,
606
- content: `Tool execution error: ${err.message}`,
607
- is_error: true,
608
- tool_name: block.name,
609
- }
610
- }
611
- }
612
-
613
- /**
614
- * Get current messages for session persistence.
615
- */
616
- getMessages(): NormalizedMessageParam[] {
617
- return [...this.messages]
618
- }
619
-
620
- /**
621
- * Get total usage across all turns.
622
- */
623
- getUsage(): TokenUsage {
624
- return { ...this.totalUsage }
625
- }
626
-
627
- /**
628
- * Get total cost.
629
- */
630
- getCost(): number {
631
- return this.totalCost
632
- }
633
- }