@codeany/open-agent-sdk 0.1.0 → 0.2.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 +144 -31
- package/dist/agent.d.ts +113 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +446 -0
- package/dist/agent.js.map +1 -0
- package/dist/engine.d.ts +62 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +494 -0
- package/dist/engine.js.map +1 -0
- package/dist/hooks.d.ts +111 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +179 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +19 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +126 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/providers/anthropic.d.ts +17 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +47 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +20 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +26 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +26 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +212 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +84 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +11 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/sdk-mcp-server.d.ts +52 -0
- package/dist/sdk-mcp-server.d.ts.map +1 -0
- package/dist/sdk-mcp-server.js +57 -0
- package/dist/sdk-mcp-server.js.map +1 -0
- package/dist/session.d.ts +73 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +159 -0
- package/dist/session.js.map +1 -0
- package/dist/skills/bundled/commit.d.ts +7 -0
- package/dist/skills/bundled/commit.d.ts.map +1 -0
- package/dist/skills/bundled/commit.js +35 -0
- package/dist/skills/bundled/commit.js.map +1 -0
- package/dist/skills/bundled/debug.d.ts +7 -0
- package/dist/skills/bundled/debug.d.ts.map +1 -0
- package/dist/skills/bundled/debug.js +46 -0
- package/dist/skills/bundled/debug.js.map +1 -0
- package/dist/skills/bundled/index.d.ts +11 -0
- package/dist/skills/bundled/index.d.ts.map +1 -0
- package/dist/skills/bundled/index.js +26 -0
- package/dist/skills/bundled/index.js.map +1 -0
- package/dist/skills/bundled/review.d.ts +7 -0
- package/dist/skills/bundled/review.d.ts.map +1 -0
- package/dist/skills/bundled/review.js +38 -0
- package/dist/skills/bundled/review.js.map +1 -0
- package/dist/skills/bundled/simplify.d.ts +8 -0
- package/dist/skills/bundled/simplify.d.ts.map +1 -0
- package/dist/skills/bundled/simplify.js +48 -0
- package/dist/skills/bundled/simplify.js.map +1 -0
- package/dist/skills/bundled/test.d.ts +7 -0
- package/dist/skills/bundled/test.d.ts.map +1 -0
- package/dist/skills/bundled/test.js +40 -0
- package/dist/skills/bundled/test.js.map +1 -0
- package/dist/skills/index.d.ts +7 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +8 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/registry.d.ts +43 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +111 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/types.d.ts +83 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +8 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tool-helper.d.ts +73 -0
- package/dist/tool-helper.d.ts.map +1 -0
- package/dist/tool-helper.js +86 -0
- package/dist/tool-helper.js.map +1 -0
- package/dist/tools/agent-tool.d.ts +17 -0
- package/dist/tools/agent-tool.d.ts.map +1 -0
- package/dist/tools/agent-tool.js +146 -0
- package/dist/tools/agent-tool.js.map +1 -0
- package/dist/tools/ask-user.d.ts +18 -0
- package/dist/tools/ask-user.d.ts.map +1 -0
- package/dist/tools/ask-user.js +72 -0
- package/dist/tools/ask-user.js.map +1 -0
- package/dist/tools/bash.d.ts +5 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +67 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/config-tool.d.ts +20 -0
- package/dist/tools/config-tool.d.ts.map +1 -0
- package/dist/tools/config-tool.js +83 -0
- package/dist/tools/config-tool.js.map +1 -0
- package/dist/tools/cron-tools.d.ts +33 -0
- package/dist/tools/cron-tools.d.ts.map +1 -0
- package/dist/tools/cron-tools.js +128 -0
- package/dist/tools/cron-tools.js.map +1 -0
- package/dist/tools/edit.d.ts +5 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +70 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +75 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +5 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +168 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +45 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +162 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/lsp-tool.d.ts +9 -0
- package/dist/tools/lsp-tool.d.ts.map +1 -0
- package/dist/tools/lsp-tool.js +137 -0
- package/dist/tools/lsp-tool.js.map +1 -0
- package/dist/tools/mcp-resource-tools.d.ts +14 -0
- package/dist/tools/mcp-resource-tools.d.ts.map +1 -0
- package/dist/tools/mcp-resource-tools.js +117 -0
- package/dist/tools/mcp-resource-tools.js.map +1 -0
- package/dist/tools/notebook-edit.d.ts +5 -0
- package/dist/tools/notebook-edit.d.ts.map +1 -0
- package/dist/tools/notebook-edit.js +85 -0
- package/dist/tools/notebook-edit.js.map +1 -0
- package/dist/tools/plan-tools.d.ts +12 -0
- package/dist/tools/plan-tools.d.ts.map +1 -0
- package/dist/tools/plan-tools.js +77 -0
- package/dist/tools/plan-tools.js.map +1 -0
- package/dist/tools/read.d.ts +5 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +66 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/send-message.d.ts +31 -0
- package/dist/tools/send-message.d.ts.map +1 -0
- package/dist/tools/send-message.js +77 -0
- package/dist/tools/send-message.js.map +1 -0
- package/dist/tools/skill-tool.d.ts +9 -0
- package/dist/tools/skill-tool.d.ts.map +1 -0
- package/dist/tools/skill-tool.js +115 -0
- package/dist/tools/skill-tool.js.map +1 -0
- package/dist/tools/task-tools.d.ts +48 -0
- package/dist/tools/task-tools.d.ts.map +1 -0
- package/dist/tools/task-tools.js +242 -0
- package/dist/tools/task-tools.js.map +1 -0
- package/dist/tools/team-tools.d.ts +34 -0
- package/dist/tools/team-tools.d.ts.map +1 -0
- package/dist/tools/team-tools.js +103 -0
- package/dist/tools/team-tools.js.map +1 -0
- package/dist/tools/todo-tool.d.ts +22 -0
- package/dist/tools/todo-tool.d.ts.map +1 -0
- package/dist/tools/todo-tool.js +93 -0
- package/dist/tools/todo-tool.js.map +1 -0
- package/dist/tools/tool-search.d.ts +13 -0
- package/dist/tools/tool-search.d.ts.map +1 -0
- package/dist/tools/tool-search.js +76 -0
- package/dist/tools/tool-search.js.map +1 -0
- package/dist/tools/types.d.ts +29 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +52 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +5 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +60 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +5 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +77 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/worktree-tools.d.ts +10 -0
- package/dist/tools/worktree-tools.d.ts.map +1 -0
- package/dist/tools/worktree-tools.js +130 -0
- package/dist/tools/worktree-tools.js.map +1 -0
- package/dist/tools/write.d.ts +5 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +40 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/types.d.ts +423 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/compact.d.ts +44 -0
- package/dist/utils/compact.d.ts.map +1 -0
- package/dist/utils/compact.js +162 -0
- package/dist/utils/compact.js.map +1 -0
- package/dist/utils/context.d.ts +35 -0
- package/dist/utils/context.d.ts.map +1 -0
- package/dist/utils/context.js +174 -0
- package/dist/utils/context.js.map +1 -0
- package/dist/utils/fileCache.d.ts +64 -0
- package/dist/utils/fileCache.d.ts.map +1 -0
- package/dist/utils/fileCache.js +117 -0
- package/dist/utils/fileCache.js.map +1 -0
- package/dist/utils/messages.d.ts +57 -0
- package/dist/utils/messages.d.ts.map +1 -0
- package/dist/utils/messages.js +152 -0
- package/dist/utils/messages.js.map +1 -0
- package/dist/utils/retry.d.ts +48 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +111 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/tokens.d.ts +57 -0
- package/dist/utils/tokens.d.ts.map +1 -0
- package/dist/utils/tokens.js +134 -0
- package/dist/utils/tokens.js.map +1 -0
- package/package.json +7 -1
- package/src/agent.ts +106 -15
- package/src/engine.ts +169 -59
- package/src/index.ts +51 -1
- package/src/providers/anthropic.ts +60 -0
- package/src/providers/index.ts +34 -0
- package/src/providers/openai.ts +315 -0
- package/src/providers/types.ts +85 -0
- package/src/session.ts +5 -5
- package/src/skills/bundled/commit.ts +38 -0
- package/src/skills/bundled/debug.ts +48 -0
- package/src/skills/bundled/index.ts +28 -0
- package/src/skills/bundled/review.ts +41 -0
- package/src/skills/bundled/simplify.ts +51 -0
- package/src/skills/bundled/test.ts +43 -0
- package/src/skills/index.ts +25 -0
- package/src/skills/registry.ts +133 -0
- package/src/skills/types.ts +99 -0
- package/src/tools/agent-tool.ts +13 -2
- package/src/tools/index.ts +8 -0
- package/src/tools/skill-tool.ts +133 -0
- package/src/tools/types.ts +7 -3
- package/src/types.ts +35 -8
- package/src/utils/compact.ts +18 -17
- package/src/utils/messages.ts +12 -13
- package/src/utils/tokens.ts +29 -6
- package/.env.example +0 -8
- package/examples/01-simple-query.ts +0 -43
- package/examples/02-multi-tool.ts +0 -44
- package/examples/03-multi-turn.ts +0 -39
- package/examples/04-prompt-api.ts +0 -29
- package/examples/05-custom-system-prompt.ts +0 -26
- package/examples/06-mcp-server.ts +0 -49
- package/examples/07-custom-tools.ts +0 -87
- package/examples/08-official-api-compat.ts +0 -38
- package/examples/09-subagents.ts +0 -48
- package/examples/10-permissions.ts +0 -40
- package/examples/11-custom-mcp-tools.ts +0 -101
- package/examples/web/index.html +0 -365
- package/examples/web/server.ts +0 -157
- package/tsconfig.json +0 -19
package/src/engine.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Manages the full conversation lifecycle:
|
|
5
5
|
* 1. Take user prompt
|
|
6
6
|
* 2. Build system prompt with context (git status, project context, tools)
|
|
7
|
-
* 3. Call LLM API with tools
|
|
7
|
+
* 3. Call LLM API with tools (via provider abstraction)
|
|
8
8
|
* 4. Stream response
|
|
9
9
|
* 5. Execute tool calls (concurrent for read-only, serial for mutations)
|
|
10
10
|
* 6. Send results back, repeat until done
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* 8. Retry with exponential backoff on transient errors
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import Anthropic from '@anthropic-ai/sdk'
|
|
16
15
|
import type {
|
|
17
16
|
SDKMessage,
|
|
18
17
|
QueryEngineConfig,
|
|
@@ -21,7 +20,12 @@ import type {
|
|
|
21
20
|
ToolContext,
|
|
22
21
|
TokenUsage,
|
|
23
22
|
} from './types.js'
|
|
24
|
-
import {
|
|
23
|
+
import type {
|
|
24
|
+
LLMProvider,
|
|
25
|
+
CreateMessageResponse,
|
|
26
|
+
NormalizedMessageParam,
|
|
27
|
+
NormalizedTool,
|
|
28
|
+
} from './providers/types.js'
|
|
25
29
|
import {
|
|
26
30
|
estimateMessagesTokens,
|
|
27
31
|
estimateCost,
|
|
@@ -37,10 +41,34 @@ import {
|
|
|
37
41
|
import {
|
|
38
42
|
withRetry,
|
|
39
43
|
isPromptTooLongError,
|
|
40
|
-
formatApiError,
|
|
41
44
|
} from './utils/retry.js'
|
|
42
45
|
import { getSystemContext, getUserContext } from './utils/context.js'
|
|
43
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
|
+
}
|
|
44
72
|
|
|
45
73
|
// ============================================================================
|
|
46
74
|
// System Prompt Builder
|
|
@@ -113,23 +141,43 @@ async function buildSystemPrompt(config: QueryEngineConfig): Promise<string> {
|
|
|
113
141
|
|
|
114
142
|
export class QueryEngine {
|
|
115
143
|
private config: QueryEngineConfig
|
|
116
|
-
private
|
|
117
|
-
public messages:
|
|
144
|
+
private provider: LLMProvider
|
|
145
|
+
public messages: NormalizedMessageParam[] = []
|
|
118
146
|
private totalUsage: TokenUsage = { input_tokens: 0, output_tokens: 0 }
|
|
119
147
|
private totalCost = 0
|
|
120
148
|
private turnCount = 0
|
|
121
149
|
private compactState: AutoCompactState
|
|
122
150
|
private sessionId: string
|
|
123
151
|
private apiTimeMs = 0
|
|
152
|
+
private hookRegistry?: HookRegistry
|
|
124
153
|
|
|
125
154
|
constructor(config: QueryEngineConfig) {
|
|
126
155
|
this.config = config
|
|
127
|
-
this.
|
|
128
|
-
apiKey: config.apiKey,
|
|
129
|
-
baseURL: config.baseURL,
|
|
130
|
-
})
|
|
156
|
+
this.provider = config.provider
|
|
131
157
|
this.compactState = createAutoCompactState()
|
|
132
|
-
this.sessionId = crypto.randomUUID()
|
|
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
|
+
}
|
|
133
181
|
}
|
|
134
182
|
|
|
135
183
|
/**
|
|
@@ -137,13 +185,34 @@ export class QueryEngine {
|
|
|
137
185
|
* Yields SDKMessage events as the agent works.
|
|
138
186
|
*/
|
|
139
187
|
async *submitMessage(
|
|
140
|
-
prompt: string |
|
|
188
|
+
prompt: string | any[],
|
|
141
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
|
+
|
|
142
211
|
// Add user message
|
|
143
|
-
this.messages.push({ role: 'user', content: prompt })
|
|
212
|
+
this.messages.push({ role: 'user', content: prompt as any })
|
|
144
213
|
|
|
145
|
-
// Build tool definitions for
|
|
146
|
-
const tools = this.config.tools.map(
|
|
214
|
+
// Build tool definitions for provider
|
|
215
|
+
const tools = this.config.tools.map(toProviderTool)
|
|
147
216
|
|
|
148
217
|
// Build system prompt
|
|
149
218
|
const systemPrompt = await buildSystemPrompt(this.config)
|
|
@@ -176,16 +245,18 @@ export class QueryEngine {
|
|
|
176
245
|
}
|
|
177
246
|
|
|
178
247
|
// Auto-compact if context is too large
|
|
179
|
-
if (shouldAutoCompact(this.messages, this.config.model, this.compactState)) {
|
|
248
|
+
if (shouldAutoCompact(this.messages as any[], this.config.model, this.compactState)) {
|
|
249
|
+
await this.executeHooks('PreCompact')
|
|
180
250
|
try {
|
|
181
251
|
const result = await compactConversation(
|
|
182
|
-
this.
|
|
252
|
+
this.provider,
|
|
183
253
|
this.config.model,
|
|
184
|
-
this.messages,
|
|
254
|
+
this.messages as any[],
|
|
185
255
|
this.compactState,
|
|
186
256
|
)
|
|
187
|
-
this.messages = result.compactedMessages
|
|
257
|
+
this.messages = result.compactedMessages as NormalizedMessageParam[]
|
|
188
258
|
this.compactState = result.state
|
|
259
|
+
await this.executeHooks('PostCompact')
|
|
189
260
|
} catch {
|
|
190
261
|
// Continue with uncompacted messages
|
|
191
262
|
}
|
|
@@ -193,38 +264,33 @@ export class QueryEngine {
|
|
|
193
264
|
|
|
194
265
|
// Micro-compact: truncate large tool results
|
|
195
266
|
const apiMessages = microCompactMessages(
|
|
196
|
-
normalizeMessagesForAPI(this.messages),
|
|
197
|
-
)
|
|
267
|
+
normalizeMessagesForAPI(this.messages as any[]),
|
|
268
|
+
) as NormalizedMessageParam[]
|
|
198
269
|
|
|
199
270
|
this.turnCount++
|
|
200
271
|
turnsRemaining--
|
|
201
272
|
|
|
202
|
-
// Make API call with retry
|
|
203
|
-
let response:
|
|
273
|
+
// Make API call with retry via provider
|
|
274
|
+
let response: CreateMessageResponse
|
|
204
275
|
const apiStart = performance.now()
|
|
205
276
|
try {
|
|
206
277
|
response = await withRetry(
|
|
207
278
|
async () => {
|
|
208
|
-
|
|
279
|
+
return this.provider.createMessage({
|
|
209
280
|
model: this.config.model,
|
|
210
|
-
|
|
281
|
+
maxTokens: this.config.maxTokens,
|
|
211
282
|
system: systemPrompt,
|
|
212
283
|
messages: apiMessages,
|
|
213
284
|
tools: tools.length > 0 ? tools : undefined,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
budget_tokens: this.config.thinking.budgetTokens,
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return this.client.messages.create(requestParams)
|
|
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
|
+
})
|
|
228
294
|
},
|
|
229
295
|
undefined,
|
|
230
296
|
this.config.abortSignal,
|
|
@@ -234,12 +300,12 @@ export class QueryEngine {
|
|
|
234
300
|
if (isPromptTooLongError(err) && !this.compactState.compacted) {
|
|
235
301
|
try {
|
|
236
302
|
const result = await compactConversation(
|
|
237
|
-
this.
|
|
303
|
+
this.provider,
|
|
238
304
|
this.config.model,
|
|
239
|
-
this.messages,
|
|
305
|
+
this.messages as any[],
|
|
240
306
|
this.compactState,
|
|
241
307
|
)
|
|
242
|
-
this.messages = result.compactedMessages
|
|
308
|
+
this.messages = result.compactedMessages as NormalizedMessageParam[]
|
|
243
309
|
this.compactState = result.state
|
|
244
310
|
turnsRemaining++ // Retry this turn
|
|
245
311
|
this.turnCount--
|
|
@@ -262,38 +328,38 @@ export class QueryEngine {
|
|
|
262
328
|
// Track API timing
|
|
263
329
|
this.apiTimeMs += performance.now() - apiStart
|
|
264
330
|
|
|
265
|
-
// Track usage
|
|
331
|
+
// Track usage (normalized by provider)
|
|
266
332
|
if (response.usage) {
|
|
267
333
|
this.totalUsage.input_tokens += response.usage.input_tokens
|
|
268
334
|
this.totalUsage.output_tokens += response.usage.output_tokens
|
|
269
|
-
if (
|
|
335
|
+
if (response.usage.cache_creation_input_tokens) {
|
|
270
336
|
this.totalUsage.cache_creation_input_tokens =
|
|
271
337
|
(this.totalUsage.cache_creation_input_tokens || 0) +
|
|
272
|
-
|
|
338
|
+
response.usage.cache_creation_input_tokens
|
|
273
339
|
}
|
|
274
|
-
if (
|
|
340
|
+
if (response.usage.cache_read_input_tokens) {
|
|
275
341
|
this.totalUsage.cache_read_input_tokens =
|
|
276
342
|
(this.totalUsage.cache_read_input_tokens || 0) +
|
|
277
|
-
|
|
343
|
+
response.usage.cache_read_input_tokens
|
|
278
344
|
}
|
|
279
|
-
this.totalCost += estimateCost(this.config.model, response.usage
|
|
345
|
+
this.totalCost += estimateCost(this.config.model, response.usage)
|
|
280
346
|
}
|
|
281
347
|
|
|
282
348
|
// Add assistant message to conversation
|
|
283
|
-
this.messages.push({ role: 'assistant', content: response.content })
|
|
349
|
+
this.messages.push({ role: 'assistant', content: response.content as any })
|
|
284
350
|
|
|
285
351
|
// Yield assistant message
|
|
286
352
|
yield {
|
|
287
353
|
type: 'assistant',
|
|
288
354
|
message: {
|
|
289
355
|
role: 'assistant',
|
|
290
|
-
content: response.content,
|
|
356
|
+
content: response.content as any,
|
|
291
357
|
},
|
|
292
358
|
}
|
|
293
359
|
|
|
294
360
|
// Handle max_output_tokens recovery
|
|
295
361
|
if (
|
|
296
|
-
response.
|
|
362
|
+
response.stopReason === 'max_tokens' &&
|
|
297
363
|
maxOutputRecoveryAttempts < MAX_OUTPUT_RECOVERY
|
|
298
364
|
) {
|
|
299
365
|
maxOutputRecoveryAttempts++
|
|
@@ -307,7 +373,7 @@ export class QueryEngine {
|
|
|
307
373
|
|
|
308
374
|
// Check for tool use
|
|
309
375
|
const toolUseBlocks = response.content.filter(
|
|
310
|
-
(block): block is
|
|
376
|
+
(block): block is ToolUseBlock => block.type === 'tool_use',
|
|
311
377
|
)
|
|
312
378
|
|
|
313
379
|
if (toolUseBlocks.length === 0) {
|
|
@@ -349,9 +415,15 @@ export class QueryEngine {
|
|
|
349
415
|
})),
|
|
350
416
|
})
|
|
351
417
|
|
|
352
|
-
if (response.
|
|
418
|
+
if (response.stopReason === 'end_turn') break
|
|
353
419
|
}
|
|
354
420
|
|
|
421
|
+
// Hook: Stop (end of agentic loop)
|
|
422
|
+
await this.executeHooks('Stop')
|
|
423
|
+
|
|
424
|
+
// Hook: SessionEnd
|
|
425
|
+
await this.executeHooks('SessionEnd')
|
|
426
|
+
|
|
355
427
|
// Yield enriched final result
|
|
356
428
|
const endSubtype = budgetExceeded
|
|
357
429
|
? 'error_max_budget_usd'
|
|
@@ -380,11 +452,14 @@ export class QueryEngine {
|
|
|
380
452
|
* Mutation tools run sequentially.
|
|
381
453
|
*/
|
|
382
454
|
private async executeTools(
|
|
383
|
-
toolUseBlocks:
|
|
455
|
+
toolUseBlocks: ToolUseBlock[],
|
|
384
456
|
): Promise<(ToolResult & { tool_name?: string })[]> {
|
|
385
457
|
const context: ToolContext = {
|
|
386
458
|
cwd: this.config.cwd,
|
|
387
459
|
abortSignal: this.config.abortSignal,
|
|
460
|
+
provider: this.provider,
|
|
461
|
+
model: this.config.model,
|
|
462
|
+
apiType: this.provider.apiType,
|
|
388
463
|
}
|
|
389
464
|
|
|
390
465
|
const MAX_CONCURRENCY = parseInt(
|
|
@@ -392,8 +467,8 @@ export class QueryEngine {
|
|
|
392
467
|
)
|
|
393
468
|
|
|
394
469
|
// Partition into read-only (concurrent) and mutation (serial)
|
|
395
|
-
const readOnly: Array<{ block:
|
|
396
|
-
const mutations: Array<{ block:
|
|
470
|
+
const readOnly: Array<{ block: ToolUseBlock; tool?: ToolDefinition }> = []
|
|
471
|
+
const mutations: Array<{ block: ToolUseBlock; tool?: ToolDefinition }> = []
|
|
397
472
|
|
|
398
473
|
for (const block of toolUseBlocks) {
|
|
399
474
|
const tool = this.config.tools.find((t) => t.name === block.name)
|
|
@@ -430,7 +505,7 @@ export class QueryEngine {
|
|
|
430
505
|
* Execute a single tool with permission checking.
|
|
431
506
|
*/
|
|
432
507
|
private async executeSingleTool(
|
|
433
|
-
block:
|
|
508
|
+
block: ToolUseBlock,
|
|
434
509
|
tool: ToolDefinition | undefined,
|
|
435
510
|
context: ToolContext,
|
|
436
511
|
): Promise<ToolResult & { tool_name?: string }> {
|
|
@@ -469,7 +544,7 @@ export class QueryEngine {
|
|
|
469
544
|
}
|
|
470
545
|
}
|
|
471
546
|
if (permission.updatedInput !== undefined) {
|
|
472
|
-
block = { ...block, input: permission.updatedInput
|
|
547
|
+
block = { ...block, input: permission.updatedInput }
|
|
473
548
|
}
|
|
474
549
|
} catch (err: any) {
|
|
475
550
|
return {
|
|
@@ -482,11 +557,46 @@ export class QueryEngine {
|
|
|
482
557
|
}
|
|
483
558
|
}
|
|
484
559
|
|
|
560
|
+
// Hook: PreToolUse
|
|
561
|
+
const preHookResults = await this.executeHooks('PreToolUse', {
|
|
562
|
+
toolName: block.name,
|
|
563
|
+
toolInput: block.input,
|
|
564
|
+
toolUseId: block.id,
|
|
565
|
+
})
|
|
566
|
+
// Check if any hook blocks this tool
|
|
567
|
+
if (preHookResults.some((r) => r.block)) {
|
|
568
|
+
const msg = preHookResults.find((r) => r.message)?.message || 'Blocked by PreToolUse hook'
|
|
569
|
+
return {
|
|
570
|
+
type: 'tool_result',
|
|
571
|
+
tool_use_id: block.id,
|
|
572
|
+
content: msg,
|
|
573
|
+
is_error: true,
|
|
574
|
+
tool_name: block.name,
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
485
578
|
// Execute the tool
|
|
486
579
|
try {
|
|
487
580
|
const result = await tool.call(block.input, context)
|
|
581
|
+
|
|
582
|
+
// Hook: PostToolUse
|
|
583
|
+
await this.executeHooks('PostToolUse', {
|
|
584
|
+
toolName: block.name,
|
|
585
|
+
toolInput: block.input,
|
|
586
|
+
toolOutput: typeof result.content === 'string' ? result.content : JSON.stringify(result.content),
|
|
587
|
+
toolUseId: block.id,
|
|
588
|
+
})
|
|
589
|
+
|
|
488
590
|
return { ...result, tool_use_id: block.id, tool_name: block.name }
|
|
489
591
|
} catch (err: any) {
|
|
592
|
+
// Hook: PostToolUseFailure
|
|
593
|
+
await this.executeHooks('PostToolUseFailure', {
|
|
594
|
+
toolName: block.name,
|
|
595
|
+
toolInput: block.input,
|
|
596
|
+
toolUseId: block.id,
|
|
597
|
+
error: err.message,
|
|
598
|
+
})
|
|
599
|
+
|
|
490
600
|
return {
|
|
491
601
|
type: 'tool_result',
|
|
492
602
|
tool_use_id: block.id,
|
|
@@ -500,7 +610,7 @@ export class QueryEngine {
|
|
|
500
610
|
/**
|
|
501
611
|
* Get current messages for session persistence.
|
|
502
612
|
*/
|
|
503
|
-
getMessages():
|
|
613
|
+
getMessages(): NormalizedMessageParam[] {
|
|
504
614
|
return [...this.messages]
|
|
505
615
|
}
|
|
506
616
|
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
8
8
|
* - 30+ built-in tools (file I/O, shell, web, agents, tasks, teams, etc.)
|
|
9
|
+
* - Skill system (reusable prompt templates with bundled skills)
|
|
9
10
|
* - MCP server integration (stdio, SSE, HTTP)
|
|
10
11
|
* - Context compression (auto-compact, micro-compact)
|
|
11
12
|
* - Retry with exponential backoff
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
* - Permission system (allow/deny/bypass modes)
|
|
15
16
|
* - Subagent spawning & team coordination
|
|
16
17
|
* - Task management & scheduling
|
|
17
|
-
* - Hook system (pre/post tool use,
|
|
18
|
+
* - Hook system with lifecycle integration (pre/post tool use, session, compact)
|
|
18
19
|
* - Token estimation & cost tracking
|
|
19
20
|
* - File state LRU caching
|
|
20
21
|
* - Plan mode for structured workflows
|
|
@@ -50,6 +51,26 @@ export type { McpSdkServerConfig } from './sdk-mcp-server.js'
|
|
|
50
51
|
|
|
51
52
|
export { QueryEngine } from './engine.js'
|
|
52
53
|
|
|
54
|
+
// --------------------------------------------------------------------------
|
|
55
|
+
// LLM Providers (Anthropic + OpenAI)
|
|
56
|
+
// --------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
createProvider,
|
|
60
|
+
AnthropicProvider,
|
|
61
|
+
OpenAIProvider,
|
|
62
|
+
} from './providers/index.js'
|
|
63
|
+
export type {
|
|
64
|
+
ApiType,
|
|
65
|
+
LLMProvider,
|
|
66
|
+
CreateMessageParams,
|
|
67
|
+
CreateMessageResponse,
|
|
68
|
+
NormalizedMessageParam,
|
|
69
|
+
NormalizedContentBlock,
|
|
70
|
+
NormalizedTool,
|
|
71
|
+
NormalizedResponseBlock,
|
|
72
|
+
} from './providers/index.js'
|
|
73
|
+
|
|
53
74
|
// --------------------------------------------------------------------------
|
|
54
75
|
// Tool System (30+ tools)
|
|
55
76
|
// --------------------------------------------------------------------------
|
|
@@ -123,6 +144,9 @@ export {
|
|
|
123
144
|
|
|
124
145
|
// Todo
|
|
125
146
|
TodoWriteTool,
|
|
147
|
+
|
|
148
|
+
// Skill
|
|
149
|
+
SkillTool,
|
|
126
150
|
} from './tools/index.js'
|
|
127
151
|
|
|
128
152
|
// --------------------------------------------------------------------------
|
|
@@ -132,6 +156,27 @@ export {
|
|
|
132
156
|
export { connectMCPServer, closeAllConnections } from './mcp/client.js'
|
|
133
157
|
export type { MCPConnection } from './mcp/client.js'
|
|
134
158
|
|
|
159
|
+
// --------------------------------------------------------------------------
|
|
160
|
+
// Skill System
|
|
161
|
+
// --------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
export {
|
|
164
|
+
registerSkill,
|
|
165
|
+
getSkill,
|
|
166
|
+
getAllSkills,
|
|
167
|
+
getUserInvocableSkills,
|
|
168
|
+
hasSkill,
|
|
169
|
+
unregisterSkill,
|
|
170
|
+
clearSkills,
|
|
171
|
+
formatSkillsForPrompt,
|
|
172
|
+
initBundledSkills,
|
|
173
|
+
} from './skills/index.js'
|
|
174
|
+
export type {
|
|
175
|
+
SkillDefinition,
|
|
176
|
+
SkillContentBlock,
|
|
177
|
+
SkillResult,
|
|
178
|
+
} from './skills/index.js'
|
|
179
|
+
|
|
135
180
|
// --------------------------------------------------------------------------
|
|
136
181
|
// Hook System
|
|
137
182
|
// --------------------------------------------------------------------------
|
|
@@ -341,6 +386,7 @@ export type {
|
|
|
341
386
|
|
|
342
387
|
// Permission types
|
|
343
388
|
PermissionMode,
|
|
389
|
+
PermissionBehavior,
|
|
344
390
|
CanUseToolFn,
|
|
345
391
|
CanUseToolResult,
|
|
346
392
|
|
|
@@ -360,6 +406,10 @@ export type {
|
|
|
360
406
|
// Engine types
|
|
361
407
|
QueryEngineConfig,
|
|
362
408
|
|
|
409
|
+
// Content block types
|
|
410
|
+
ContentBlockParam,
|
|
411
|
+
ContentBlock,
|
|
412
|
+
|
|
363
413
|
// Sandbox types
|
|
364
414
|
SandboxSettings,
|
|
365
415
|
SandboxNetworkConfig,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Messages API Provider
|
|
3
|
+
*
|
|
4
|
+
* Wraps the @anthropic-ai/sdk client. Since our internal format is
|
|
5
|
+
* Anthropic-like, this is mostly a thin pass-through.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import Anthropic from '@anthropic-ai/sdk'
|
|
9
|
+
import type {
|
|
10
|
+
LLMProvider,
|
|
11
|
+
CreateMessageParams,
|
|
12
|
+
CreateMessageResponse,
|
|
13
|
+
} from './types.js'
|
|
14
|
+
|
|
15
|
+
export class AnthropicProvider implements LLMProvider {
|
|
16
|
+
readonly apiType = 'anthropic-messages' as const
|
|
17
|
+
private client: Anthropic
|
|
18
|
+
|
|
19
|
+
constructor(opts: { apiKey?: string; baseURL?: string }) {
|
|
20
|
+
this.client = new Anthropic({
|
|
21
|
+
apiKey: opts.apiKey,
|
|
22
|
+
baseURL: opts.baseURL,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async createMessage(params: CreateMessageParams): Promise<CreateMessageResponse> {
|
|
27
|
+
const requestParams: Anthropic.MessageCreateParamsNonStreaming = {
|
|
28
|
+
model: params.model,
|
|
29
|
+
max_tokens: params.maxTokens,
|
|
30
|
+
system: params.system,
|
|
31
|
+
messages: params.messages as Anthropic.MessageParam[],
|
|
32
|
+
tools: params.tools
|
|
33
|
+
? (params.tools as Anthropic.Tool[])
|
|
34
|
+
: undefined,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Add extended thinking if configured
|
|
38
|
+
if (params.thinking?.type === 'enabled' && params.thinking.budget_tokens) {
|
|
39
|
+
(requestParams as any).thinking = {
|
|
40
|
+
type: 'enabled',
|
|
41
|
+
budget_tokens: params.thinking.budget_tokens,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const response = await this.client.messages.create(requestParams)
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
content: response.content as CreateMessageResponse['content'],
|
|
49
|
+
stopReason: response.stop_reason || 'end_turn',
|
|
50
|
+
usage: {
|
|
51
|
+
input_tokens: response.usage.input_tokens,
|
|
52
|
+
output_tokens: response.usage.output_tokens,
|
|
53
|
+
cache_creation_input_tokens:
|
|
54
|
+
(response.usage as any).cache_creation_input_tokens,
|
|
55
|
+
cache_read_input_tokens:
|
|
56
|
+
(response.usage as any).cache_read_input_tokens,
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Provider Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates the appropriate provider based on API type configuration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type { ApiType, LLMProvider, CreateMessageParams, CreateMessageResponse, NormalizedMessageParam, NormalizedContentBlock, NormalizedTool, NormalizedResponseBlock } from './types.js'
|
|
8
|
+
|
|
9
|
+
export { AnthropicProvider } from './anthropic.js'
|
|
10
|
+
export { OpenAIProvider } from './openai.js'
|
|
11
|
+
|
|
12
|
+
import type { ApiType, LLMProvider } from './types.js'
|
|
13
|
+
import { AnthropicProvider } from './anthropic.js'
|
|
14
|
+
import { OpenAIProvider } from './openai.js'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create an LLM provider based on the API type.
|
|
18
|
+
*
|
|
19
|
+
* @param apiType - 'anthropic-messages' or 'openai-completions'
|
|
20
|
+
* @param opts - API credentials
|
|
21
|
+
*/
|
|
22
|
+
export function createProvider(
|
|
23
|
+
apiType: ApiType,
|
|
24
|
+
opts: { apiKey?: string; baseURL?: string },
|
|
25
|
+
): LLMProvider {
|
|
26
|
+
switch (apiType) {
|
|
27
|
+
case 'anthropic-messages':
|
|
28
|
+
return new AnthropicProvider(opts)
|
|
29
|
+
case 'openai-completions':
|
|
30
|
+
return new OpenAIProvider(opts)
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Unsupported API type: ${apiType}. Use 'anthropic-messages' or 'openai-completions'.`)
|
|
33
|
+
}
|
|
34
|
+
}
|