@stacksjs/ai 0.70.53 → 0.70.55
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/package.json +3 -2
- package/src/agents/claude/index.ts +310 -0
- package/src/agents/index.ts +7 -0
- package/src/buddy.ts +619 -0
- package/src/drivers/anthropic/index.ts +430 -0
- package/src/drivers/claude-agent-sdk/index.ts +370 -0
- package/src/drivers/index.ts +14 -0
- package/src/drivers/ollama/index.ts +514 -0
- package/src/drivers/openai/index.ts +529 -0
- package/src/image.ts +607 -0
- package/src/index.ts +53 -0
- package/src/mcp.ts +658 -0
- package/src/personalization.ts +490 -0
- package/src/search.ts +555 -0
- package/src/text.ts +79 -0
- package/src/types.ts +229 -0
- package/src/utils/client-bedrock-runtime.ts +51 -0
- package/src/utils/client-bedrock.ts +75 -0
- package/src/utils/model-access.ts +32 -0
- package/src/utils/retry.ts +124 -0
- package/src/utils/tokens.ts +159 -0
- package/src/utils/usage.ts +98 -0
- package/src/utils/vision.ts +119 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK Driver
|
|
3
|
+
*
|
|
4
|
+
* Provides full agentic capabilities using the official Claude Agent SDK.
|
|
5
|
+
* This driver uses @anthropic-ai/claude-agent-sdk for programmatic access
|
|
6
|
+
* to Claude Code's capabilities including built-in tools for file operations,
|
|
7
|
+
* command execution, and code analysis.
|
|
8
|
+
*
|
|
9
|
+
* Authentication:
|
|
10
|
+
* - Uses ANTHROPIC_API_KEY environment variable if set
|
|
11
|
+
* - Falls back to local Claude Code authentication if available
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/sdk
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { AIDriver, AIDriverConfig, AIMessage, StreamingResult } from '../../types'
|
|
17
|
+
|
|
18
|
+
// Lazy import to avoid issues if SDK is not installed
|
|
19
|
+
let sdkModule: typeof import('@anthropic-ai/claude-agent-sdk') | null = null
|
|
20
|
+
|
|
21
|
+
async function getSDK() {
|
|
22
|
+
if (!sdkModule) {
|
|
23
|
+
try {
|
|
24
|
+
sdkModule = await import('@anthropic-ai/claude-agent-sdk')
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'Claude Agent SDK not installed. Run: bun add @anthropic-ai/claude-agent-sdk',
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return sdkModule
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ClaudeAgentSDKConfig extends AIDriverConfig {
|
|
36
|
+
/** Maximum agent turns before stopping (default: 25) */
|
|
37
|
+
maxTurns?: number
|
|
38
|
+
/** Working directory for the agent */
|
|
39
|
+
cwd?: string
|
|
40
|
+
/** Tools the agent is allowed to use */
|
|
41
|
+
allowedTools?: string[]
|
|
42
|
+
/** Tools the agent is not allowed to use */
|
|
43
|
+
disallowedTools?: string[]
|
|
44
|
+
/** Permission mode for tool execution */
|
|
45
|
+
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan'
|
|
46
|
+
/** Custom system prompt (overrides default) */
|
|
47
|
+
customSystemPrompt?: string
|
|
48
|
+
/** Appended to the default system prompt */
|
|
49
|
+
appendSystemPrompt?: string
|
|
50
|
+
/** Session ID to resume a previous conversation */
|
|
51
|
+
resumeSessionId?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// SDK state for session management
|
|
55
|
+
interface SDKState {
|
|
56
|
+
lastSessionId?: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sdkState: SDKState = {
|
|
60
|
+
lastSessionId: undefined,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Default configuration
|
|
64
|
+
const DEFAULT_CONFIG: Required<Pick<ClaudeAgentSDKConfig, 'maxTurns' | 'allowedTools' | 'permissionMode'>> = {
|
|
65
|
+
maxTurns: 25,
|
|
66
|
+
allowedTools: ['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'],
|
|
67
|
+
permissionMode: 'bypassPermissions',
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a Claude Agent SDK driver instance
|
|
72
|
+
*/
|
|
73
|
+
export function createClaudeAgentSDKDriver(config: ClaudeAgentSDKConfig = {}): AIDriver {
|
|
74
|
+
const {
|
|
75
|
+
maxTurns = DEFAULT_CONFIG.maxTurns,
|
|
76
|
+
cwd,
|
|
77
|
+
allowedTools = DEFAULT_CONFIG.allowedTools,
|
|
78
|
+
disallowedTools,
|
|
79
|
+
permissionMode = DEFAULT_CONFIG.permissionMode,
|
|
80
|
+
customSystemPrompt,
|
|
81
|
+
appendSystemPrompt,
|
|
82
|
+
resumeSessionId,
|
|
83
|
+
} = config
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
name: 'Claude Agent SDK',
|
|
87
|
+
|
|
88
|
+
async process(command: string, systemPrompt: string, _history: AIMessage[]): Promise<string> {
|
|
89
|
+
const sdk = await getSDK()
|
|
90
|
+
const { query } = sdk
|
|
91
|
+
|
|
92
|
+
// Build full prompt with context
|
|
93
|
+
const fullPrompt = systemPrompt
|
|
94
|
+
? `${systemPrompt}\n\nUser request: ${command}`
|
|
95
|
+
: command
|
|
96
|
+
|
|
97
|
+
// Build SDK options
|
|
98
|
+
const options: Record<string, unknown> = {
|
|
99
|
+
allowedTools,
|
|
100
|
+
permissionMode,
|
|
101
|
+
maxTurns,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (disallowedTools) {
|
|
105
|
+
options.disallowedTools = disallowedTools
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (customSystemPrompt) {
|
|
109
|
+
options.customSystemPrompt = customSystemPrompt
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (appendSystemPrompt) {
|
|
113
|
+
options.appendSystemPrompt = appendSystemPrompt
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (cwd) {
|
|
117
|
+
options.cwd = cwd
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (resumeSessionId || sdkState.lastSessionId) {
|
|
121
|
+
options.resume = resumeSessionId || sdkState.lastSessionId
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let result = ''
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
for await (const message of query({ prompt: fullPrompt, options })) {
|
|
128
|
+
// Capture session ID for potential resume
|
|
129
|
+
if (message.type === 'system' && message.subtype === 'init') {
|
|
130
|
+
sdkState.lastSessionId = (message as { session_id: string }).session_id
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Capture the final result
|
|
134
|
+
if ('result' in message && typeof message.result === 'string') {
|
|
135
|
+
result = message.result
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Tool usage events are available for debugging if needed
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result || 'No response from Claude Agent SDK'
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
const err = error as Error
|
|
145
|
+
if (err.message.includes('ANTHROPIC_API_KEY')) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
'Claude Agent SDK requires ANTHROPIC_API_KEY environment variable or Claude Code authentication.',
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`Claude Agent SDK error: ${err.message}`)
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
async *stream(command: string, systemPrompt: string, _history: AIMessage[]): AsyncGenerator<string> {
|
|
155
|
+
const sdk = await getSDK()
|
|
156
|
+
const { query } = sdk
|
|
157
|
+
|
|
158
|
+
// Build full prompt with context
|
|
159
|
+
const fullPrompt = systemPrompt
|
|
160
|
+
? `${systemPrompt}\n\nUser request: ${command}`
|
|
161
|
+
: command
|
|
162
|
+
|
|
163
|
+
// Build SDK options
|
|
164
|
+
const options: Record<string, unknown> = {
|
|
165
|
+
allowedTools,
|
|
166
|
+
permissionMode,
|
|
167
|
+
maxTurns,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (disallowedTools) {
|
|
171
|
+
options.disallowedTools = disallowedTools
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (customSystemPrompt) {
|
|
175
|
+
options.customSystemPrompt = customSystemPrompt
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (appendSystemPrompt) {
|
|
179
|
+
options.appendSystemPrompt = appendSystemPrompt
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (cwd) {
|
|
183
|
+
options.cwd = cwd
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (resumeSessionId || sdkState.lastSessionId) {
|
|
187
|
+
options.resume = resumeSessionId || sdkState.lastSessionId
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
for await (const message of query({ prompt: fullPrompt, options })) {
|
|
192
|
+
// Capture session ID
|
|
193
|
+
if (message.type === 'system' && message.subtype === 'init') {
|
|
194
|
+
sdkState.lastSessionId = (message as { session_id: string }).session_id
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Yield assistant text content as it streams
|
|
198
|
+
if (message.type === 'assistant') {
|
|
199
|
+
const assistantMsg = message as {
|
|
200
|
+
message?: { content?: Array<{ type: string, text?: string }> }
|
|
201
|
+
}
|
|
202
|
+
if (assistantMsg.message?.content) {
|
|
203
|
+
for (const block of assistantMsg.message.content) {
|
|
204
|
+
if (block.type === 'text' && block.text) {
|
|
205
|
+
yield block.text
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Yield the final result
|
|
212
|
+
if ('result' in message && typeof message.result === 'string') {
|
|
213
|
+
yield message.result
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
const err = error as Error
|
|
219
|
+
if (err.message.includes('ANTHROPIC_API_KEY')) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
'Claude Agent SDK requires ANTHROPIC_API_KEY environment variable or Claude Code authentication.',
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
throw new Error(`Claude Agent SDK error: ${err.message}`)
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Process a command with streaming and return a StreamingResult
|
|
232
|
+
*/
|
|
233
|
+
export async function processStreaming(
|
|
234
|
+
command: string,
|
|
235
|
+
cwd?: string,
|
|
236
|
+
config: Omit<ClaudeAgentSDKConfig, 'cwd'> = {},
|
|
237
|
+
): Promise<StreamingResult> {
|
|
238
|
+
const sdk = await getSDK()
|
|
239
|
+
const { query } = sdk
|
|
240
|
+
|
|
241
|
+
const {
|
|
242
|
+
maxTurns = DEFAULT_CONFIG.maxTurns,
|
|
243
|
+
allowedTools = DEFAULT_CONFIG.allowedTools,
|
|
244
|
+
disallowedTools,
|
|
245
|
+
permissionMode = DEFAULT_CONFIG.permissionMode,
|
|
246
|
+
customSystemPrompt,
|
|
247
|
+
appendSystemPrompt,
|
|
248
|
+
resumeSessionId,
|
|
249
|
+
} = config
|
|
250
|
+
|
|
251
|
+
// Build SDK options
|
|
252
|
+
const options: Record<string, unknown> = {
|
|
253
|
+
allowedTools,
|
|
254
|
+
permissionMode,
|
|
255
|
+
maxTurns,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (disallowedTools) options.disallowedTools = disallowedTools
|
|
259
|
+
if (customSystemPrompt) options.customSystemPrompt = customSystemPrompt
|
|
260
|
+
if (appendSystemPrompt) options.appendSystemPrompt = appendSystemPrompt
|
|
261
|
+
if (cwd) options.cwd = cwd
|
|
262
|
+
if (resumeSessionId || sdkState.lastSessionId) {
|
|
263
|
+
options.resume = resumeSessionId || sdkState.lastSessionId
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const encoder = new TextEncoder()
|
|
267
|
+
let fullResponse = ''
|
|
268
|
+
let resolveFullResponse: (value: string) => void
|
|
269
|
+
|
|
270
|
+
const fullResponsePromise = new Promise<string>((resolve) => {
|
|
271
|
+
resolveFullResponse = resolve
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
275
|
+
async start(controller) {
|
|
276
|
+
try {
|
|
277
|
+
for await (const message of query({ prompt: command, options })) {
|
|
278
|
+
// Capture session ID
|
|
279
|
+
if (message.type === 'system' && message.subtype === 'init') {
|
|
280
|
+
sdkState.lastSessionId = (message as { session_id: string }).session_id
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Stream assistant text content
|
|
284
|
+
if (message.type === 'assistant') {
|
|
285
|
+
const assistantMsg = message as {
|
|
286
|
+
message?: { content?: Array<{ type: string, text?: string }> }
|
|
287
|
+
}
|
|
288
|
+
if (assistantMsg.message?.content) {
|
|
289
|
+
for (const block of assistantMsg.message.content) {
|
|
290
|
+
if (block.type === 'text' && block.text) {
|
|
291
|
+
fullResponse += block.text
|
|
292
|
+
controller.enqueue(encoder.encode(block.text))
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Capture final result
|
|
299
|
+
if ('result' in message && typeof message.result === 'string') {
|
|
300
|
+
if (!fullResponse) {
|
|
301
|
+
fullResponse = message.result
|
|
302
|
+
controller.enqueue(encoder.encode(message.result))
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
resolveFullResponse(fullResponse)
|
|
308
|
+
controller.close()
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
resolveFullResponse(fullResponse)
|
|
312
|
+
controller.error(error)
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
stream,
|
|
319
|
+
fullResponse: fullResponsePromise,
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Resume a previous SDK session
|
|
325
|
+
*/
|
|
326
|
+
export async function resumeSession(sessionId: string, prompt: string): Promise<string> {
|
|
327
|
+
const sdk = await getSDK()
|
|
328
|
+
const { query } = sdk
|
|
329
|
+
|
|
330
|
+
let result = ''
|
|
331
|
+
|
|
332
|
+
for await (const message of query({
|
|
333
|
+
prompt,
|
|
334
|
+
options: {
|
|
335
|
+
resume: sessionId,
|
|
336
|
+
permissionMode: 'bypassPermissions',
|
|
337
|
+
},
|
|
338
|
+
})) {
|
|
339
|
+
if ('result' in message && typeof message.result === 'string') {
|
|
340
|
+
result = message.result
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return result || 'No response from resumed session'
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get the last session ID for potential resume
|
|
349
|
+
*/
|
|
350
|
+
export function getLastSessionId(): string | undefined {
|
|
351
|
+
return sdkState.lastSessionId
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Clear the stored session ID
|
|
356
|
+
*/
|
|
357
|
+
export function clearSession(): void {
|
|
358
|
+
sdkState.lastSessionId = undefined
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Export the driver creator and utilities
|
|
362
|
+
export const claudeAgentSDK = {
|
|
363
|
+
createDriver: createClaudeAgentSDKDriver,
|
|
364
|
+
processStreaming,
|
|
365
|
+
resumeSession,
|
|
366
|
+
getLastSessionId,
|
|
367
|
+
clearSession,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export default claudeAgentSDK
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Drivers
|
|
3
|
+
*
|
|
4
|
+
* Export all available AI drivers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { createAnthropicDriver, anthropicDriver, anthropic, estimateTokens } from './anthropic'
|
|
8
|
+
export type { AnthropicDriverConfig } from './anthropic'
|
|
9
|
+
export { createOpenAIDriver, openaiDriver, openai } from './openai'
|
|
10
|
+
export type { OpenAIDriverConfig } from './openai'
|
|
11
|
+
export { createOllamaDriver, ollamaDriver, ollama } from './ollama'
|
|
12
|
+
export type { OllamaDriverConfig } from './ollama'
|
|
13
|
+
export { createClaudeAgentSDKDriver, claudeAgentSDK, getLastSessionId, clearSession } from './claude-agent-sdk'
|
|
14
|
+
export type { ClaudeAgentSDKConfig } from './claude-agent-sdk'
|