@codeany/open-agent-sdk 0.1.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.
Files changed (57) hide show
  1. package/.env.example +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +388 -0
  4. package/examples/01-simple-query.ts +43 -0
  5. package/examples/02-multi-tool.ts +44 -0
  6. package/examples/03-multi-turn.ts +39 -0
  7. package/examples/04-prompt-api.ts +29 -0
  8. package/examples/05-custom-system-prompt.ts +26 -0
  9. package/examples/06-mcp-server.ts +49 -0
  10. package/examples/07-custom-tools.ts +87 -0
  11. package/examples/08-official-api-compat.ts +38 -0
  12. package/examples/09-subagents.ts +48 -0
  13. package/examples/10-permissions.ts +40 -0
  14. package/examples/11-custom-mcp-tools.ts +101 -0
  15. package/examples/web/index.html +365 -0
  16. package/examples/web/server.ts +157 -0
  17. package/package.json +60 -0
  18. package/src/agent.ts +425 -0
  19. package/src/engine.ts +520 -0
  20. package/src/hooks.ts +261 -0
  21. package/src/index.ts +376 -0
  22. package/src/mcp/client.ts +150 -0
  23. package/src/sdk-mcp-server.ts +78 -0
  24. package/src/session.ts +227 -0
  25. package/src/tool-helper.ts +127 -0
  26. package/src/tools/agent-tool.ts +153 -0
  27. package/src/tools/ask-user.ts +79 -0
  28. package/src/tools/bash.ts +75 -0
  29. package/src/tools/config-tool.ts +89 -0
  30. package/src/tools/cron-tools.ts +153 -0
  31. package/src/tools/edit.ts +74 -0
  32. package/src/tools/glob.ts +77 -0
  33. package/src/tools/grep.ts +168 -0
  34. package/src/tools/index.ts +232 -0
  35. package/src/tools/lsp-tool.ts +163 -0
  36. package/src/tools/mcp-resource-tools.ts +125 -0
  37. package/src/tools/notebook-edit.ts +93 -0
  38. package/src/tools/plan-tools.ts +88 -0
  39. package/src/tools/read.ts +73 -0
  40. package/src/tools/send-message.ts +96 -0
  41. package/src/tools/task-tools.ts +290 -0
  42. package/src/tools/team-tools.ts +128 -0
  43. package/src/tools/todo-tool.ts +112 -0
  44. package/src/tools/tool-search.ts +87 -0
  45. package/src/tools/types.ts +62 -0
  46. package/src/tools/web-fetch.ts +66 -0
  47. package/src/tools/web-search.ts +86 -0
  48. package/src/tools/worktree-tools.ts +140 -0
  49. package/src/tools/write.ts +42 -0
  50. package/src/types.ts +459 -0
  51. package/src/utils/compact.ts +206 -0
  52. package/src/utils/context.ts +191 -0
  53. package/src/utils/fileCache.ts +148 -0
  54. package/src/utils/messages.ts +196 -0
  55. package/src/utils/retry.ts +140 -0
  56. package/src/utils/tokens.ts +122 -0
  57. package/tsconfig.json +19 -0
package/src/hooks.ts ADDED
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Hook System
3
+ *
4
+ * Lifecycle hooks for intercepting agent behavior.
5
+ * Supports pre/post tool use, session lifecycle, and custom events.
6
+ *
7
+ * Hook events:
8
+ * - PreToolUse: before tool execution
9
+ * - PostToolUse: after tool execution
10
+ * - PostToolUseFailure: after tool failure
11
+ * - SessionStart: session initialization
12
+ * - SessionEnd: session cleanup
13
+ * - Stop: when turn completes
14
+ * - SubagentStart: subagent spawned
15
+ * - SubagentStop: subagent completed
16
+ * - UserPromptSubmit: user sends message
17
+ * - PermissionRequest: permission check triggered
18
+ * - TaskCreated: task created
19
+ * - TaskCompleted: task finished
20
+ * - ConfigChange: settings changed
21
+ * - CwdChanged: working directory changed
22
+ * - FileChanged: file modified
23
+ * - Notification: system notification
24
+ */
25
+
26
+ import { spawn, type ChildProcess } from 'child_process'
27
+
28
+ /**
29
+ * All supported hook events.
30
+ */
31
+ export const HOOK_EVENTS = [
32
+ 'PreToolUse',
33
+ 'PostToolUse',
34
+ 'PostToolUseFailure',
35
+ 'SessionStart',
36
+ 'SessionEnd',
37
+ 'Stop',
38
+ 'SubagentStart',
39
+ 'SubagentStop',
40
+ 'UserPromptSubmit',
41
+ 'PermissionRequest',
42
+ 'PermissionDenied',
43
+ 'TaskCreated',
44
+ 'TaskCompleted',
45
+ 'ConfigChange',
46
+ 'CwdChanged',
47
+ 'FileChanged',
48
+ 'Notification',
49
+ 'PreCompact',
50
+ 'PostCompact',
51
+ 'TeammateIdle',
52
+ ] as const
53
+
54
+ export type HookEvent = typeof HOOK_EVENTS[number]
55
+
56
+ /**
57
+ * Hook definition.
58
+ */
59
+ export interface HookDefinition {
60
+ /** Shell command or function to execute */
61
+ command?: string
62
+ /** Function handler */
63
+ handler?: (input: HookInput) => Promise<HookOutput | void>
64
+ /** Tool name matcher (regex pattern) */
65
+ matcher?: string
66
+ /** Timeout in milliseconds */
67
+ timeout?: number
68
+ }
69
+
70
+ /**
71
+ * Hook input passed to handlers.
72
+ */
73
+ export interface HookInput {
74
+ event: HookEvent
75
+ toolName?: string
76
+ toolInput?: unknown
77
+ toolOutput?: unknown
78
+ toolUseId?: string
79
+ sessionId?: string
80
+ cwd?: string
81
+ error?: string
82
+ [key: string]: unknown
83
+ }
84
+
85
+ /**
86
+ * Hook output returned by handlers.
87
+ */
88
+ export interface HookOutput {
89
+ /** Message to append to conversation */
90
+ message?: string
91
+ /** Permission update */
92
+ permissionUpdate?: {
93
+ tool: string
94
+ behavior: 'allow' | 'deny'
95
+ }
96
+ /** Whether to block the action */
97
+ block?: boolean
98
+ /** Notification */
99
+ notification?: {
100
+ title: string
101
+ body: string
102
+ level?: 'info' | 'warning' | 'error'
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Hook configuration (from settings).
108
+ */
109
+ export type HookConfig = Record<string, HookDefinition[]>
110
+
111
+ /**
112
+ * Hook registry for managing and executing hooks.
113
+ */
114
+ export class HookRegistry {
115
+ private hooks: Map<HookEvent, HookDefinition[]> = new Map()
116
+
117
+ /**
118
+ * Register hooks from configuration.
119
+ */
120
+ registerFromConfig(config: HookConfig): void {
121
+ for (const [event, definitions] of Object.entries(config)) {
122
+ const hookEvent = event as HookEvent
123
+ if (!HOOK_EVENTS.includes(hookEvent)) continue
124
+
125
+ const existing = this.hooks.get(hookEvent) || []
126
+ this.hooks.set(hookEvent, [...existing, ...definitions])
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Register a single hook.
132
+ */
133
+ register(event: HookEvent, definition: HookDefinition): void {
134
+ const existing = this.hooks.get(event) || []
135
+ existing.push(definition)
136
+ this.hooks.set(event, existing)
137
+ }
138
+
139
+ /**
140
+ * Execute hooks for an event.
141
+ */
142
+ async execute(
143
+ event: HookEvent,
144
+ input: HookInput,
145
+ ): Promise<HookOutput[]> {
146
+ const definitions = this.hooks.get(event) || []
147
+ const results: HookOutput[] = []
148
+
149
+ for (const def of definitions) {
150
+ // Check matcher for tool-specific hooks
151
+ if (def.matcher && input.toolName) {
152
+ const regex = new RegExp(def.matcher)
153
+ if (!regex.test(input.toolName)) continue
154
+ }
155
+
156
+ try {
157
+ let output: HookOutput | void = undefined
158
+
159
+ if (def.handler) {
160
+ // Function handler
161
+ output = await Promise.race([
162
+ def.handler(input),
163
+ new Promise<void>((_, reject) =>
164
+ setTimeout(() => reject(new Error('Hook timeout')), def.timeout || 30000),
165
+ ),
166
+ ])
167
+ } else if (def.command) {
168
+ // Shell command handler
169
+ output = await executeShellHook(def.command, input, def.timeout || 30000)
170
+ }
171
+
172
+ if (output) {
173
+ results.push(output)
174
+ }
175
+ } catch (err: any) {
176
+ // Log but don't fail on hook errors
177
+ console.error(`[Hook] ${event} hook failed: ${err.message}`)
178
+ }
179
+ }
180
+
181
+ return results
182
+ }
183
+
184
+ /**
185
+ * Check if any hooks are registered for an event.
186
+ */
187
+ hasHooks(event: HookEvent): boolean {
188
+ return (this.hooks.get(event)?.length || 0) > 0
189
+ }
190
+
191
+ /**
192
+ * Clear all hooks.
193
+ */
194
+ clear(): void {
195
+ this.hooks.clear()
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Execute a shell command as a hook.
201
+ */
202
+ async function executeShellHook(
203
+ command: string,
204
+ input: HookInput,
205
+ timeout: number,
206
+ ): Promise<HookOutput | void> {
207
+ return new Promise((resolve) => {
208
+ const proc = spawn('bash', ['-c', command], {
209
+ timeout,
210
+ env: {
211
+ ...process.env,
212
+ HOOK_EVENT: input.event,
213
+ HOOK_TOOL_NAME: input.toolName || '',
214
+ HOOK_SESSION_ID: input.sessionId || '',
215
+ HOOK_CWD: input.cwd || '',
216
+ },
217
+ stdio: ['pipe', 'pipe', 'pipe'],
218
+ })
219
+
220
+ // Send input as JSON on stdin
221
+ proc.stdin?.write(JSON.stringify(input))
222
+ proc.stdin?.end()
223
+
224
+ const chunks: Buffer[] = []
225
+ proc.stdout?.on('data', (d: Buffer) => chunks.push(d))
226
+
227
+ proc.on('close', (code) => {
228
+ if (code !== 0) {
229
+ resolve(undefined)
230
+ return
231
+ }
232
+
233
+ const stdout = Buffer.concat(chunks).toString('utf-8').trim()
234
+ if (!stdout) {
235
+ resolve(undefined)
236
+ return
237
+ }
238
+
239
+ try {
240
+ const output = JSON.parse(stdout) as HookOutput
241
+ resolve(output)
242
+ } catch {
243
+ // Non-JSON output treated as message
244
+ resolve({ message: stdout })
245
+ }
246
+ })
247
+
248
+ proc.on('error', () => resolve(undefined))
249
+ })
250
+ }
251
+
252
+ /**
253
+ * Create a default hook registry.
254
+ */
255
+ export function createHookRegistry(config?: HookConfig): HookRegistry {
256
+ const registry = new HookRegistry()
257
+ if (config) {
258
+ registry.registerFromConfig(config)
259
+ }
260
+ return registry
261
+ }
package/src/index.ts ADDED
@@ -0,0 +1,376 @@
1
+ /**
2
+ * @codeany/open-agent-sdk
3
+ *
4
+ * Open-source Agent SDK by CodeAny (https://codeany.ai).
5
+ * Runs the full agent loop in-process without spawning subprocesses.
6
+ *
7
+ * Features:
8
+ * - 30+ built-in tools (file I/O, shell, web, agents, tasks, teams, etc.)
9
+ * - MCP server integration (stdio, SSE, HTTP)
10
+ * - Context compression (auto-compact, micro-compact)
11
+ * - Retry with exponential backoff
12
+ * - Git status & project context injection
13
+ * - Multi-turn session persistence
14
+ * - Permission system (allow/deny/bypass modes)
15
+ * - Subagent spawning & team coordination
16
+ * - Task management & scheduling
17
+ * - Hook system (pre/post tool use, lifecycle events)
18
+ * - Token estimation & cost tracking
19
+ * - File state LRU caching
20
+ * - Plan mode for structured workflows
21
+ */
22
+
23
+ // --------------------------------------------------------------------------
24
+ // High-level Agent API
25
+ // --------------------------------------------------------------------------
26
+
27
+ export { Agent, createAgent, query } from './agent.js'
28
+
29
+ // --------------------------------------------------------------------------
30
+ // Tool Helper (Zod-based tool creation, compatible with official SDK)
31
+ // --------------------------------------------------------------------------
32
+
33
+ export { tool, sdkToolToToolDefinition } from './tool-helper.js'
34
+ export type {
35
+ ToolAnnotations,
36
+ CallToolResult,
37
+ SdkMcpToolDefinition,
38
+ } from './tool-helper.js'
39
+
40
+ // --------------------------------------------------------------------------
41
+ // In-Process MCP Server
42
+ // --------------------------------------------------------------------------
43
+
44
+ export { createSdkMcpServer, isSdkServerConfig } from './sdk-mcp-server.js'
45
+ export type { McpSdkServerConfig } from './sdk-mcp-server.js'
46
+
47
+ // --------------------------------------------------------------------------
48
+ // Core Engine
49
+ // --------------------------------------------------------------------------
50
+
51
+ export { QueryEngine } from './engine.js'
52
+
53
+ // --------------------------------------------------------------------------
54
+ // Tool System (30+ tools)
55
+ // --------------------------------------------------------------------------
56
+
57
+ export {
58
+ // Registry
59
+ getAllBaseTools,
60
+ filterTools,
61
+ assembleToolPool,
62
+
63
+ // Helpers
64
+ defineTool,
65
+ toApiTool,
66
+
67
+ // Core file I/O & execution
68
+ BashTool,
69
+ FileReadTool,
70
+ FileWriteTool,
71
+ FileEditTool,
72
+ GlobTool,
73
+ GrepTool,
74
+ NotebookEditTool,
75
+
76
+ // Web
77
+ WebFetchTool,
78
+ WebSearchTool,
79
+
80
+ // Agent & Multi-agent
81
+ AgentTool,
82
+ SendMessageTool,
83
+ TeamCreateTool,
84
+ TeamDeleteTool,
85
+
86
+ // Tasks
87
+ TaskCreateTool,
88
+ TaskListTool,
89
+ TaskUpdateTool,
90
+ TaskGetTool,
91
+ TaskStopTool,
92
+ TaskOutputTool,
93
+
94
+ // Worktree
95
+ EnterWorktreeTool,
96
+ ExitWorktreeTool,
97
+
98
+ // Planning
99
+ EnterPlanModeTool,
100
+ ExitPlanModeTool,
101
+
102
+ // User interaction
103
+ AskUserQuestionTool,
104
+
105
+ // Discovery
106
+ ToolSearchTool,
107
+
108
+ // MCP Resources
109
+ ListMcpResourcesTool,
110
+ ReadMcpResourceTool,
111
+
112
+ // Scheduling
113
+ CronCreateTool,
114
+ CronDeleteTool,
115
+ CronListTool,
116
+ RemoteTriggerTool,
117
+
118
+ // LSP
119
+ LSPTool,
120
+
121
+ // Config
122
+ ConfigTool,
123
+
124
+ // Todo
125
+ TodoWriteTool,
126
+ } from './tools/index.js'
127
+
128
+ // --------------------------------------------------------------------------
129
+ // MCP Client
130
+ // --------------------------------------------------------------------------
131
+
132
+ export { connectMCPServer, closeAllConnections } from './mcp/client.js'
133
+ export type { MCPConnection } from './mcp/client.js'
134
+
135
+ // --------------------------------------------------------------------------
136
+ // Hook System
137
+ // --------------------------------------------------------------------------
138
+
139
+ export {
140
+ HookRegistry,
141
+ createHookRegistry,
142
+ HOOK_EVENTS,
143
+ } from './hooks.js'
144
+ export type {
145
+ HookEvent,
146
+ HookDefinition,
147
+ HookInput,
148
+ HookOutput,
149
+ HookConfig,
150
+ } from './hooks.js'
151
+
152
+ // --------------------------------------------------------------------------
153
+ // Session Management
154
+ // --------------------------------------------------------------------------
155
+
156
+ export {
157
+ saveSession,
158
+ loadSession,
159
+ listSessions,
160
+ forkSession,
161
+ getSessionMessages,
162
+ getSessionInfo,
163
+ renameSession,
164
+ tagSession,
165
+ appendToSession,
166
+ deleteSession,
167
+ } from './session.js'
168
+ export type { SessionMetadata, SessionData } from './session.js'
169
+
170
+ // --------------------------------------------------------------------------
171
+ // Context Utilities
172
+ // --------------------------------------------------------------------------
173
+
174
+ export {
175
+ getSystemContext,
176
+ getUserContext,
177
+ getGitStatus,
178
+ readProjectContextContent,
179
+ discoverProjectContextFiles,
180
+ clearContextCache,
181
+ } from './utils/context.js'
182
+
183
+ // --------------------------------------------------------------------------
184
+ // Message Utilities
185
+ // --------------------------------------------------------------------------
186
+
187
+ export {
188
+ createUserMessage,
189
+ createAssistantMessage,
190
+ normalizeMessagesForAPI,
191
+ stripImagesFromMessages,
192
+ extractTextFromContent,
193
+ createCompactBoundaryMessage,
194
+ truncateText,
195
+ } from './utils/messages.js'
196
+
197
+ // --------------------------------------------------------------------------
198
+ // Token Estimation & Cost
199
+ // --------------------------------------------------------------------------
200
+
201
+ export {
202
+ estimateTokens,
203
+ estimateMessagesTokens,
204
+ estimateSystemPromptTokens,
205
+ getTokenCountFromUsage,
206
+ getContextWindowSize,
207
+ getAutoCompactThreshold,
208
+ estimateCost,
209
+ MODEL_PRICING,
210
+ AUTOCOMPACT_BUFFER_TOKENS,
211
+ } from './utils/tokens.js'
212
+
213
+ // --------------------------------------------------------------------------
214
+ // Context Compression
215
+ // --------------------------------------------------------------------------
216
+
217
+ export {
218
+ shouldAutoCompact,
219
+ compactConversation,
220
+ microCompactMessages,
221
+ createAutoCompactState,
222
+ } from './utils/compact.js'
223
+ export type { AutoCompactState } from './utils/compact.js'
224
+
225
+ // --------------------------------------------------------------------------
226
+ // Retry Logic
227
+ // --------------------------------------------------------------------------
228
+
229
+ export {
230
+ withRetry,
231
+ isRetryableError,
232
+ isPromptTooLongError,
233
+ isAuthError,
234
+ isRateLimitError,
235
+ formatApiError,
236
+ getRetryDelay,
237
+ DEFAULT_RETRY_CONFIG,
238
+ } from './utils/retry.js'
239
+ export type { RetryConfig } from './utils/retry.js'
240
+
241
+ // --------------------------------------------------------------------------
242
+ // File State Cache
243
+ // --------------------------------------------------------------------------
244
+
245
+ export {
246
+ FileStateCache,
247
+ createFileStateCache,
248
+ } from './utils/fileCache.js'
249
+ export type { FileState } from './utils/fileCache.js'
250
+
251
+ // --------------------------------------------------------------------------
252
+ // Task & Team State (for advanced usage)
253
+ // --------------------------------------------------------------------------
254
+
255
+ export {
256
+ getAllTasks,
257
+ getTask,
258
+ clearTasks,
259
+ } from './tools/task-tools.js'
260
+ export type { Task, TaskStatus } from './tools/task-tools.js'
261
+
262
+ export {
263
+ getAllTeams,
264
+ getTeam,
265
+ clearTeams,
266
+ } from './tools/team-tools.js'
267
+ export type { Team } from './tools/team-tools.js'
268
+
269
+ export {
270
+ readMailbox,
271
+ writeToMailbox,
272
+ clearMailboxes,
273
+ } from './tools/send-message.js'
274
+ export type { AgentMessage } from './tools/send-message.js'
275
+
276
+ export {
277
+ isPlanModeActive,
278
+ getCurrentPlan,
279
+ } from './tools/plan-tools.js'
280
+
281
+ export {
282
+ registerAgents,
283
+ clearAgents,
284
+ } from './tools/agent-tool.js'
285
+
286
+ export {
287
+ setQuestionHandler,
288
+ clearQuestionHandler,
289
+ } from './tools/ask-user.js'
290
+
291
+ export {
292
+ setDeferredTools,
293
+ } from './tools/tool-search.js'
294
+
295
+ export {
296
+ setMcpConnections,
297
+ } from './tools/mcp-resource-tools.js'
298
+
299
+ export {
300
+ getAllCronJobs,
301
+ clearCronJobs,
302
+ } from './tools/cron-tools.js'
303
+ export type { CronJob } from './tools/cron-tools.js'
304
+
305
+ export {
306
+ getConfig,
307
+ setConfig,
308
+ clearConfig,
309
+ } from './tools/config-tool.js'
310
+
311
+ export {
312
+ getTodos,
313
+ clearTodos,
314
+ } from './tools/todo-tool.js'
315
+ export type { TodoItem } from './tools/todo-tool.js'
316
+
317
+ // --------------------------------------------------------------------------
318
+ // Types
319
+ // --------------------------------------------------------------------------
320
+
321
+ export type {
322
+ // Message types
323
+ Message,
324
+ UserMessage,
325
+ AssistantMessage,
326
+ ConversationMessage,
327
+ MessageRole,
328
+
329
+ // SDK message types (streaming events)
330
+ SDKMessage,
331
+ SDKAssistantMessage,
332
+ SDKToolResultMessage,
333
+ SDKResultMessage,
334
+ SDKPartialMessage,
335
+
336
+ // Tool types
337
+ ToolDefinition,
338
+ ToolInputSchema,
339
+ ToolContext,
340
+ ToolResult,
341
+
342
+ // Permission types
343
+ PermissionMode,
344
+ CanUseToolFn,
345
+ CanUseToolResult,
346
+
347
+ // MCP types
348
+ McpServerConfig,
349
+ McpStdioConfig,
350
+ McpSseConfig,
351
+ McpHttpConfig,
352
+
353
+ // Agent types
354
+ AgentOptions,
355
+ AgentDefinition,
356
+ QueryResult,
357
+ ThinkingConfig,
358
+ TokenUsage,
359
+
360
+ // Engine types
361
+ QueryEngineConfig,
362
+
363
+ // Sandbox types
364
+ SandboxSettings,
365
+ SandboxNetworkConfig,
366
+ SandboxFilesystemConfig,
367
+
368
+ // Output format
369
+ OutputFormat,
370
+
371
+ // Setting sources
372
+ SettingSource,
373
+
374
+ // Model info
375
+ ModelInfo,
376
+ } from './types.js'