@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.
Files changed (254) hide show
  1. package/README.md +144 -31
  2. package/dist/agent.d.ts +113 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +446 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/engine.d.ts +62 -0
  7. package/dist/engine.d.ts.map +1 -0
  8. package/dist/engine.js +494 -0
  9. package/dist/engine.js.map +1 -0
  10. package/dist/hooks.d.ts +111 -0
  11. package/dist/hooks.d.ts.map +1 -0
  12. package/dist/hooks.js +179 -0
  13. package/dist/hooks.js.map +1 -0
  14. package/dist/index.d.ts +66 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +133 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/mcp/client.d.ts +19 -0
  19. package/dist/mcp/client.d.ts.map +1 -0
  20. package/dist/mcp/client.js +126 -0
  21. package/dist/mcp/client.js.map +1 -0
  22. package/dist/providers/anthropic.d.ts +17 -0
  23. package/dist/providers/anthropic.d.ts.map +1 -0
  24. package/dist/providers/anthropic.js +47 -0
  25. package/dist/providers/anthropic.js.map +1 -0
  26. package/dist/providers/index.d.ts +20 -0
  27. package/dist/providers/index.d.ts.map +1 -0
  28. package/dist/providers/index.js +26 -0
  29. package/dist/providers/index.js.map +1 -0
  30. package/dist/providers/openai.d.ts +26 -0
  31. package/dist/providers/openai.d.ts.map +1 -0
  32. package/dist/providers/openai.js +212 -0
  33. package/dist/providers/openai.js.map +1 -0
  34. package/dist/providers/types.d.ts +84 -0
  35. package/dist/providers/types.d.ts.map +1 -0
  36. package/dist/providers/types.js +11 -0
  37. package/dist/providers/types.js.map +1 -0
  38. package/dist/sdk-mcp-server.d.ts +52 -0
  39. package/dist/sdk-mcp-server.d.ts.map +1 -0
  40. package/dist/sdk-mcp-server.js +57 -0
  41. package/dist/sdk-mcp-server.js.map +1 -0
  42. package/dist/session.d.ts +73 -0
  43. package/dist/session.d.ts.map +1 -0
  44. package/dist/session.js +159 -0
  45. package/dist/session.js.map +1 -0
  46. package/dist/skills/bundled/commit.d.ts +7 -0
  47. package/dist/skills/bundled/commit.d.ts.map +1 -0
  48. package/dist/skills/bundled/commit.js +35 -0
  49. package/dist/skills/bundled/commit.js.map +1 -0
  50. package/dist/skills/bundled/debug.d.ts +7 -0
  51. package/dist/skills/bundled/debug.d.ts.map +1 -0
  52. package/dist/skills/bundled/debug.js +46 -0
  53. package/dist/skills/bundled/debug.js.map +1 -0
  54. package/dist/skills/bundled/index.d.ts +11 -0
  55. package/dist/skills/bundled/index.d.ts.map +1 -0
  56. package/dist/skills/bundled/index.js +26 -0
  57. package/dist/skills/bundled/index.js.map +1 -0
  58. package/dist/skills/bundled/review.d.ts +7 -0
  59. package/dist/skills/bundled/review.d.ts.map +1 -0
  60. package/dist/skills/bundled/review.js +38 -0
  61. package/dist/skills/bundled/review.js.map +1 -0
  62. package/dist/skills/bundled/simplify.d.ts +8 -0
  63. package/dist/skills/bundled/simplify.d.ts.map +1 -0
  64. package/dist/skills/bundled/simplify.js +48 -0
  65. package/dist/skills/bundled/simplify.js.map +1 -0
  66. package/dist/skills/bundled/test.d.ts +7 -0
  67. package/dist/skills/bundled/test.d.ts.map +1 -0
  68. package/dist/skills/bundled/test.js +40 -0
  69. package/dist/skills/bundled/test.js.map +1 -0
  70. package/dist/skills/index.d.ts +7 -0
  71. package/dist/skills/index.d.ts.map +1 -0
  72. package/dist/skills/index.js +8 -0
  73. package/dist/skills/index.js.map +1 -0
  74. package/dist/skills/registry.d.ts +43 -0
  75. package/dist/skills/registry.d.ts.map +1 -0
  76. package/dist/skills/registry.js +111 -0
  77. package/dist/skills/registry.js.map +1 -0
  78. package/dist/skills/types.d.ts +83 -0
  79. package/dist/skills/types.d.ts.map +1 -0
  80. package/dist/skills/types.js +8 -0
  81. package/dist/skills/types.js.map +1 -0
  82. package/dist/tool-helper.d.ts +73 -0
  83. package/dist/tool-helper.d.ts.map +1 -0
  84. package/dist/tool-helper.js +86 -0
  85. package/dist/tool-helper.js.map +1 -0
  86. package/dist/tools/agent-tool.d.ts +17 -0
  87. package/dist/tools/agent-tool.d.ts.map +1 -0
  88. package/dist/tools/agent-tool.js +146 -0
  89. package/dist/tools/agent-tool.js.map +1 -0
  90. package/dist/tools/ask-user.d.ts +18 -0
  91. package/dist/tools/ask-user.d.ts.map +1 -0
  92. package/dist/tools/ask-user.js +72 -0
  93. package/dist/tools/ask-user.js.map +1 -0
  94. package/dist/tools/bash.d.ts +5 -0
  95. package/dist/tools/bash.d.ts.map +1 -0
  96. package/dist/tools/bash.js +67 -0
  97. package/dist/tools/bash.js.map +1 -0
  98. package/dist/tools/config-tool.d.ts +20 -0
  99. package/dist/tools/config-tool.d.ts.map +1 -0
  100. package/dist/tools/config-tool.js +83 -0
  101. package/dist/tools/config-tool.js.map +1 -0
  102. package/dist/tools/cron-tools.d.ts +33 -0
  103. package/dist/tools/cron-tools.d.ts.map +1 -0
  104. package/dist/tools/cron-tools.js +128 -0
  105. package/dist/tools/cron-tools.js.map +1 -0
  106. package/dist/tools/edit.d.ts +5 -0
  107. package/dist/tools/edit.d.ts.map +1 -0
  108. package/dist/tools/edit.js +70 -0
  109. package/dist/tools/edit.js.map +1 -0
  110. package/dist/tools/glob.d.ts +5 -0
  111. package/dist/tools/glob.d.ts.map +1 -0
  112. package/dist/tools/glob.js +75 -0
  113. package/dist/tools/glob.js.map +1 -0
  114. package/dist/tools/grep.d.ts +5 -0
  115. package/dist/tools/grep.d.ts.map +1 -0
  116. package/dist/tools/grep.js +168 -0
  117. package/dist/tools/grep.js.map +1 -0
  118. package/dist/tools/index.d.ts +45 -0
  119. package/dist/tools/index.d.ts.map +1 -0
  120. package/dist/tools/index.js +162 -0
  121. package/dist/tools/index.js.map +1 -0
  122. package/dist/tools/lsp-tool.d.ts +9 -0
  123. package/dist/tools/lsp-tool.d.ts.map +1 -0
  124. package/dist/tools/lsp-tool.js +137 -0
  125. package/dist/tools/lsp-tool.js.map +1 -0
  126. package/dist/tools/mcp-resource-tools.d.ts +14 -0
  127. package/dist/tools/mcp-resource-tools.d.ts.map +1 -0
  128. package/dist/tools/mcp-resource-tools.js +117 -0
  129. package/dist/tools/mcp-resource-tools.js.map +1 -0
  130. package/dist/tools/notebook-edit.d.ts +5 -0
  131. package/dist/tools/notebook-edit.d.ts.map +1 -0
  132. package/dist/tools/notebook-edit.js +85 -0
  133. package/dist/tools/notebook-edit.js.map +1 -0
  134. package/dist/tools/plan-tools.d.ts +12 -0
  135. package/dist/tools/plan-tools.d.ts.map +1 -0
  136. package/dist/tools/plan-tools.js +77 -0
  137. package/dist/tools/plan-tools.js.map +1 -0
  138. package/dist/tools/read.d.ts +5 -0
  139. package/dist/tools/read.d.ts.map +1 -0
  140. package/dist/tools/read.js +66 -0
  141. package/dist/tools/read.js.map +1 -0
  142. package/dist/tools/send-message.d.ts +31 -0
  143. package/dist/tools/send-message.d.ts.map +1 -0
  144. package/dist/tools/send-message.js +77 -0
  145. package/dist/tools/send-message.js.map +1 -0
  146. package/dist/tools/skill-tool.d.ts +9 -0
  147. package/dist/tools/skill-tool.d.ts.map +1 -0
  148. package/dist/tools/skill-tool.js +115 -0
  149. package/dist/tools/skill-tool.js.map +1 -0
  150. package/dist/tools/task-tools.d.ts +48 -0
  151. package/dist/tools/task-tools.d.ts.map +1 -0
  152. package/dist/tools/task-tools.js +242 -0
  153. package/dist/tools/task-tools.js.map +1 -0
  154. package/dist/tools/team-tools.d.ts +34 -0
  155. package/dist/tools/team-tools.d.ts.map +1 -0
  156. package/dist/tools/team-tools.js +103 -0
  157. package/dist/tools/team-tools.js.map +1 -0
  158. package/dist/tools/todo-tool.d.ts +22 -0
  159. package/dist/tools/todo-tool.d.ts.map +1 -0
  160. package/dist/tools/todo-tool.js +93 -0
  161. package/dist/tools/todo-tool.js.map +1 -0
  162. package/dist/tools/tool-search.d.ts +13 -0
  163. package/dist/tools/tool-search.d.ts.map +1 -0
  164. package/dist/tools/tool-search.js +76 -0
  165. package/dist/tools/tool-search.js.map +1 -0
  166. package/dist/tools/types.d.ts +29 -0
  167. package/dist/tools/types.d.ts.map +1 -0
  168. package/dist/tools/types.js +52 -0
  169. package/dist/tools/types.js.map +1 -0
  170. package/dist/tools/web-fetch.d.ts +5 -0
  171. package/dist/tools/web-fetch.d.ts.map +1 -0
  172. package/dist/tools/web-fetch.js +60 -0
  173. package/dist/tools/web-fetch.js.map +1 -0
  174. package/dist/tools/web-search.d.ts +5 -0
  175. package/dist/tools/web-search.d.ts.map +1 -0
  176. package/dist/tools/web-search.js +77 -0
  177. package/dist/tools/web-search.js.map +1 -0
  178. package/dist/tools/worktree-tools.d.ts +10 -0
  179. package/dist/tools/worktree-tools.d.ts.map +1 -0
  180. package/dist/tools/worktree-tools.js +130 -0
  181. package/dist/tools/worktree-tools.js.map +1 -0
  182. package/dist/tools/write.d.ts +5 -0
  183. package/dist/tools/write.d.ts.map +1 -0
  184. package/dist/tools/write.js +40 -0
  185. package/dist/tools/write.js.map +1 -0
  186. package/dist/types.d.ts +423 -0
  187. package/dist/types.d.ts.map +1 -0
  188. package/dist/types.js +5 -0
  189. package/dist/types.js.map +1 -0
  190. package/dist/utils/compact.d.ts +44 -0
  191. package/dist/utils/compact.d.ts.map +1 -0
  192. package/dist/utils/compact.js +162 -0
  193. package/dist/utils/compact.js.map +1 -0
  194. package/dist/utils/context.d.ts +35 -0
  195. package/dist/utils/context.d.ts.map +1 -0
  196. package/dist/utils/context.js +174 -0
  197. package/dist/utils/context.js.map +1 -0
  198. package/dist/utils/fileCache.d.ts +64 -0
  199. package/dist/utils/fileCache.d.ts.map +1 -0
  200. package/dist/utils/fileCache.js +117 -0
  201. package/dist/utils/fileCache.js.map +1 -0
  202. package/dist/utils/messages.d.ts +57 -0
  203. package/dist/utils/messages.d.ts.map +1 -0
  204. package/dist/utils/messages.js +152 -0
  205. package/dist/utils/messages.js.map +1 -0
  206. package/dist/utils/retry.d.ts +48 -0
  207. package/dist/utils/retry.d.ts.map +1 -0
  208. package/dist/utils/retry.js +111 -0
  209. package/dist/utils/retry.js.map +1 -0
  210. package/dist/utils/tokens.d.ts +57 -0
  211. package/dist/utils/tokens.d.ts.map +1 -0
  212. package/dist/utils/tokens.js +134 -0
  213. package/dist/utils/tokens.js.map +1 -0
  214. package/package.json +7 -1
  215. package/src/agent.ts +106 -15
  216. package/src/engine.ts +169 -59
  217. package/src/index.ts +51 -1
  218. package/src/providers/anthropic.ts +60 -0
  219. package/src/providers/index.ts +34 -0
  220. package/src/providers/openai.ts +315 -0
  221. package/src/providers/types.ts +85 -0
  222. package/src/session.ts +5 -5
  223. package/src/skills/bundled/commit.ts +38 -0
  224. package/src/skills/bundled/debug.ts +48 -0
  225. package/src/skills/bundled/index.ts +28 -0
  226. package/src/skills/bundled/review.ts +41 -0
  227. package/src/skills/bundled/simplify.ts +51 -0
  228. package/src/skills/bundled/test.ts +43 -0
  229. package/src/skills/index.ts +25 -0
  230. package/src/skills/registry.ts +133 -0
  231. package/src/skills/types.ts +99 -0
  232. package/src/tools/agent-tool.ts +13 -2
  233. package/src/tools/index.ts +8 -0
  234. package/src/tools/skill-tool.ts +133 -0
  235. package/src/tools/types.ts +7 -3
  236. package/src/types.ts +35 -8
  237. package/src/utils/compact.ts +18 -17
  238. package/src/utils/messages.ts +12 -13
  239. package/src/utils/tokens.ts +29 -6
  240. package/.env.example +0 -8
  241. package/examples/01-simple-query.ts +0 -43
  242. package/examples/02-multi-tool.ts +0 -44
  243. package/examples/03-multi-turn.ts +0 -39
  244. package/examples/04-prompt-api.ts +0 -29
  245. package/examples/05-custom-system-prompt.ts +0 -26
  246. package/examples/06-mcp-server.ts +0 -49
  247. package/examples/07-custom-tools.ts +0 -87
  248. package/examples/08-official-api-compat.ts +0 -38
  249. package/examples/09-subagents.ts +0 -48
  250. package/examples/10-permissions.ts +0 -40
  251. package/examples/11-custom-mcp-tools.ts +0 -101
  252. package/examples/web/index.html +0 -365
  253. package/examples/web/server.ts +0 -157
  254. package/tsconfig.json +0 -19
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Skills Module - Public API
3
+ */
4
+
5
+ // Types
6
+ export type {
7
+ SkillDefinition,
8
+ SkillContentBlock,
9
+ SkillResult,
10
+ } from './types.js'
11
+
12
+ // Registry
13
+ export {
14
+ registerSkill,
15
+ getSkill,
16
+ getAllSkills,
17
+ getUserInvocableSkills,
18
+ hasSkill,
19
+ unregisterSkill,
20
+ clearSkills,
21
+ formatSkillsForPrompt,
22
+ } from './registry.js'
23
+
24
+ // Bundled skills
25
+ export { initBundledSkills } from './bundled/index.js'
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Skill Registry
3
+ *
4
+ * Central registry for managing skill definitions.
5
+ * Skills can be registered programmatically or loaded from bundled definitions.
6
+ */
7
+
8
+ import type { SkillDefinition } from './types.js'
9
+
10
+ /** Internal skill store */
11
+ const skills: Map<string, SkillDefinition> = new Map()
12
+
13
+ /** Alias -> skill name mapping */
14
+ const aliases: Map<string, string> = new Map()
15
+
16
+ /**
17
+ * Register a skill definition.
18
+ */
19
+ export function registerSkill(definition: SkillDefinition): void {
20
+ skills.set(definition.name, definition)
21
+
22
+ // Register aliases
23
+ if (definition.aliases) {
24
+ for (const alias of definition.aliases) {
25
+ aliases.set(alias, definition.name)
26
+ }
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Get a skill by name or alias.
32
+ */
33
+ export function getSkill(name: string): SkillDefinition | undefined {
34
+ // Direct lookup
35
+ const direct = skills.get(name)
36
+ if (direct) return direct
37
+
38
+ // Alias lookup
39
+ const resolved = aliases.get(name)
40
+ if (resolved) return skills.get(resolved)
41
+
42
+ return undefined
43
+ }
44
+
45
+ /**
46
+ * Get all registered skills.
47
+ */
48
+ export function getAllSkills(): SkillDefinition[] {
49
+ return Array.from(skills.values())
50
+ }
51
+
52
+ /**
53
+ * Get all user-invocable skills (for /command listing).
54
+ */
55
+ export function getUserInvocableSkills(): SkillDefinition[] {
56
+ return getAllSkills().filter(
57
+ (s) => s.userInvocable !== false && (!s.isEnabled || s.isEnabled()),
58
+ )
59
+ }
60
+
61
+ /**
62
+ * Check if a skill exists.
63
+ */
64
+ export function hasSkill(name: string): boolean {
65
+ return skills.has(name) || aliases.has(name)
66
+ }
67
+
68
+ /**
69
+ * Remove a skill.
70
+ */
71
+ export function unregisterSkill(name: string): boolean {
72
+ const skill = skills.get(name)
73
+ if (!skill) return false
74
+
75
+ // Remove aliases
76
+ if (skill.aliases) {
77
+ for (const alias of skill.aliases) {
78
+ aliases.delete(alias)
79
+ }
80
+ }
81
+
82
+ return skills.delete(name)
83
+ }
84
+
85
+ /**
86
+ * Clear all skills (for testing).
87
+ */
88
+ export function clearSkills(): void {
89
+ skills.clear()
90
+ aliases.clear()
91
+ }
92
+
93
+ /**
94
+ * Format skills listing for system prompt injection.
95
+ *
96
+ * Uses a budget system: skills listing gets a limited character budget
97
+ * to avoid bloating the context window.
98
+ */
99
+ export function formatSkillsForPrompt(
100
+ contextWindowTokens?: number,
101
+ ): string {
102
+ const invocable = getUserInvocableSkills()
103
+ if (invocable.length === 0) return ''
104
+
105
+ // Budget: 1% of context window in characters (4 chars per token)
106
+ const CHARS_PER_TOKEN = 4
107
+ const DEFAULT_BUDGET = 8000
108
+ const MAX_DESC_CHARS = 250
109
+ const budget = contextWindowTokens
110
+ ? Math.floor(contextWindowTokens * 0.01 * CHARS_PER_TOKEN)
111
+ : DEFAULT_BUDGET
112
+
113
+ const lines: string[] = []
114
+ let used = 0
115
+
116
+ for (const skill of invocable) {
117
+ const desc = skill.description.length > MAX_DESC_CHARS
118
+ ? skill.description.slice(0, MAX_DESC_CHARS) + '...'
119
+ : skill.description
120
+
121
+ const trigger = skill.whenToUse
122
+ ? ` TRIGGER when: ${skill.whenToUse}`
123
+ : ''
124
+
125
+ const line = `- ${skill.name}: ${desc}${trigger}`
126
+
127
+ if (used + line.length > budget) break
128
+ lines.push(line)
129
+ used += line.length
130
+ }
131
+
132
+ return lines.join('\n')
133
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Skill System Types
3
+ *
4
+ * Skills are reusable prompt templates that extend agent capabilities.
5
+ * They can be invoked by the model via the Skill tool or by users via /skillname.
6
+ */
7
+
8
+ import type { ToolContext } from '../types.js'
9
+ import type { HookConfig } from '../hooks.js'
10
+
11
+ /**
12
+ * Content block for skill prompts (compatible with Anthropic API).
13
+ */
14
+ export type SkillContentBlock =
15
+ | { type: 'text'; text: string }
16
+ | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } }
17
+
18
+ /**
19
+ * Bundled skill definition.
20
+ *
21
+ * Inspired by Claude Code's skill system. Skills provide specialized
22
+ * capabilities by injecting context-specific prompts with optional
23
+ * tool restrictions and model overrides.
24
+ */
25
+ export interface SkillDefinition {
26
+ /** Unique skill name (e.g., 'simplify', 'commit') */
27
+ name: string
28
+
29
+ /** Human-readable description */
30
+ description: string
31
+
32
+ /** Alternative names for the skill */
33
+ aliases?: string[]
34
+
35
+ /** When the model should invoke this skill (used in system prompt) */
36
+ whenToUse?: string
37
+
38
+ /** Hint for expected arguments */
39
+ argumentHint?: string
40
+
41
+ /** Tools the skill is allowed to use (empty = all tools) */
42
+ allowedTools?: string[]
43
+
44
+ /** Model override for this skill */
45
+ model?: string
46
+
47
+ /** Whether the skill can be invoked by users via /command */
48
+ userInvocable?: boolean
49
+
50
+ /** Runtime check for availability */
51
+ isEnabled?: () => boolean
52
+
53
+ /** Hook overrides while skill is active */
54
+ hooks?: HookConfig
55
+
56
+ /** Execution context: 'inline' runs in current context, 'fork' spawns a subagent */
57
+ context?: 'inline' | 'fork'
58
+
59
+ /** Subagent type for forked execution */
60
+ agent?: string
61
+
62
+ /**
63
+ * Generate the prompt content blocks for this skill.
64
+ *
65
+ * @param args - User-provided arguments (e.g., from "/simplify focus on error handling")
66
+ * @param context - Tool execution context (cwd, etc.)
67
+ * @returns Content blocks to inject into the conversation
68
+ */
69
+ getPrompt: (
70
+ args: string,
71
+ context: ToolContext,
72
+ ) => Promise<SkillContentBlock[]>
73
+ }
74
+
75
+ /**
76
+ * Result of executing a skill.
77
+ */
78
+ export interface SkillResult {
79
+ /** Whether execution succeeded */
80
+ success: boolean
81
+
82
+ /** Skill name that was executed */
83
+ skillName: string
84
+
85
+ /** Execution status */
86
+ status: 'inline' | 'forked'
87
+
88
+ /** Allowed tools override (for inline execution) */
89
+ allowedTools?: string[]
90
+
91
+ /** Model override */
92
+ model?: string
93
+
94
+ /** Result text (for forked execution) */
95
+ result?: string
96
+
97
+ /** Error message */
98
+ error?: string
99
+ }
@@ -8,7 +8,7 @@
8
8
  import type { ToolDefinition, ToolContext, ToolResult, AgentDefinition } from '../types.js'
9
9
  import { QueryEngine } from '../engine.js'
10
10
  import { getAllBaseTools, filterTools } from './index.js'
11
- import { toApiTool } from './types.js'
11
+ import { createProvider, type ApiType } from '../providers/index.js'
12
12
 
13
13
  // Store for registered agent definitions
14
14
  let registeredAgents: Record<string, AgentDefinition> = {}
@@ -101,10 +101,21 @@ export const AgentTool: ToolDefinition = {
101
101
  const systemPrompt = agentDef?.prompt ||
102
102
  'You are a helpful assistant. Complete the given task using the available tools.'
103
103
 
104
+ // Inherit provider and model from parent agent context, fall back to env vars
105
+ const subModel = input.model || context.model || process.env.CODEANY_MODEL || 'claude-sonnet-4-6'
106
+ const provider = context.provider ?? createProvider(
107
+ (context.apiType || process.env.CODEANY_API_TYPE as ApiType) || 'anthropic-messages',
108
+ {
109
+ apiKey: process.env.CODEANY_API_KEY,
110
+ baseURL: process.env.CODEANY_BASE_URL,
111
+ },
112
+ )
113
+
104
114
  // Create subagent engine
105
115
  const engine = new QueryEngine({
106
116
  cwd: context.cwd,
107
- model: input.model || process.env.CODEANY_MODEL || 'claude-sonnet-4-6',
117
+ model: subModel,
118
+ provider,
108
119
  tools,
109
120
  systemPrompt,
110
121
  maxTurns: agentDef?.maxTurns || 10,
@@ -62,6 +62,9 @@ import { ConfigTool } from './config-tool.js'
62
62
  // Todo
63
63
  import { TodoWriteTool } from './todo-tool.js'
64
64
 
65
+ // Skill
66
+ import { SkillTool } from './skill-tool.js'
67
+
65
68
  /**
66
69
  * All built-in tools (30+).
67
70
  */
@@ -125,6 +128,9 @@ const ALL_TOOLS: ToolDefinition[] = [
125
128
 
126
129
  // Todo
127
130
  TodoWriteTool,
131
+
132
+ // Skill
133
+ SkillTool,
128
134
  ]
129
135
 
130
136
  /**
@@ -226,6 +232,8 @@ export {
226
232
  ConfigTool,
227
233
  // Todo
228
234
  TodoWriteTool,
235
+ // Skill
236
+ SkillTool,
229
237
  }
230
238
 
231
239
  // Re-export helpers
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Skill Tool
3
+ *
4
+ * Allows the model to invoke registered skills by name.
5
+ * Skills are prompt templates that provide specialized capabilities.
6
+ */
7
+
8
+ import type { ToolDefinition, ToolResult, ToolContext } from '../types.js'
9
+ import { getSkill, getUserInvocableSkills } from '../skills/registry.js'
10
+
11
+ export const SkillTool: ToolDefinition = {
12
+ name: 'Skill',
13
+ description:
14
+ 'Execute a skill within the current conversation. ' +
15
+ 'Skills provide specialized capabilities and domain knowledge. ' +
16
+ 'Use this tool with the skill name and optional arguments. ' +
17
+ 'Available skills are listed in system-reminder messages.',
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ skill: {
22
+ type: 'string',
23
+ description: 'The skill name to execute (e.g., "commit", "review", "simplify")',
24
+ },
25
+ args: {
26
+ type: 'string',
27
+ description: 'Optional arguments for the skill',
28
+ },
29
+ },
30
+ required: ['skill'],
31
+ },
32
+
33
+ isReadOnly: () => false,
34
+ isConcurrencySafe: () => false,
35
+ isEnabled: () => getUserInvocableSkills().length > 0,
36
+
37
+ async prompt(): Promise<string> {
38
+ const skills = getUserInvocableSkills()
39
+ if (skills.length === 0) return ''
40
+
41
+ const lines = skills.map((s) => {
42
+ const desc =
43
+ s.description.length > 200
44
+ ? s.description.slice(0, 200) + '...'
45
+ : s.description
46
+ return `- ${s.name}: ${desc}`
47
+ })
48
+
49
+ return (
50
+ 'Execute a skill within the main conversation.\n\n' +
51
+ 'Available skills:\n' +
52
+ lines.join('\n') +
53
+ '\n\nWhen a skill matches the user\'s request, invoke it using the Skill tool.'
54
+ )
55
+ },
56
+
57
+ async call(input: any, context: ToolContext): Promise<ToolResult> {
58
+ const skillName: string = input.skill
59
+ const args: string = input.args || ''
60
+
61
+ if (!skillName) {
62
+ return {
63
+ type: 'tool_result',
64
+ tool_use_id: '',
65
+ content: 'Error: skill name is required',
66
+ is_error: true,
67
+ }
68
+ }
69
+
70
+ const skill = getSkill(skillName)
71
+ if (!skill) {
72
+ const available = getUserInvocableSkills()
73
+ .map((s) => s.name)
74
+ .join(', ')
75
+ return {
76
+ type: 'tool_result',
77
+ tool_use_id: '',
78
+ content: `Error: Unknown skill "${skillName}". Available skills: ${available || 'none'}`,
79
+ is_error: true,
80
+ }
81
+ }
82
+
83
+ // Check if skill is enabled
84
+ if (skill.isEnabled && !skill.isEnabled()) {
85
+ return {
86
+ type: 'tool_result',
87
+ tool_use_id: '',
88
+ content: `Error: Skill "${skillName}" is currently disabled`,
89
+ is_error: true,
90
+ }
91
+ }
92
+
93
+ try {
94
+ // Get skill prompt
95
+ const contentBlocks = await skill.getPrompt(args, context)
96
+
97
+ // Convert content blocks to text
98
+ const promptText = contentBlocks
99
+ .filter((b): b is { type: 'text'; text: string } => b.type === 'text')
100
+ .map((b) => b.text)
101
+ .join('\n\n')
102
+
103
+ // Build result with metadata
104
+ const result: Record<string, unknown> = {
105
+ success: true,
106
+ commandName: skill.name,
107
+ status: skill.context === 'fork' ? 'forked' : 'inline',
108
+ prompt: promptText,
109
+ }
110
+
111
+ if (skill.allowedTools) {
112
+ result.allowedTools = skill.allowedTools
113
+ }
114
+
115
+ if (skill.model) {
116
+ result.model = skill.model
117
+ }
118
+
119
+ return {
120
+ type: 'tool_result',
121
+ tool_use_id: '',
122
+ content: JSON.stringify(result),
123
+ }
124
+ } catch (err: any) {
125
+ return {
126
+ type: 'tool_result',
127
+ tool_use_id: '',
128
+ content: `Error executing skill "${skillName}": ${err.message}`,
129
+ is_error: true,
130
+ }
131
+ }
132
+ },
133
+ }
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  import type { ToolDefinition, ToolInputSchema, ToolContext, ToolResult } from '../types.js'
6
- import type Anthropic from '@anthropic-ai/sdk'
7
6
 
8
7
  /**
9
8
  * Helper to create a tool definition with sensible defaults.
@@ -52,11 +51,16 @@ export function defineTool(config: {
52
51
 
53
52
  /**
54
53
  * Convert a ToolDefinition to API-compatible tool format.
54
+ * Returns the normalized tool format used by providers.
55
55
  */
56
- export function toApiTool(tool: ToolDefinition): Anthropic.Tool {
56
+ export function toApiTool(tool: ToolDefinition): {
57
+ name: string
58
+ description: string
59
+ input_schema: ToolInputSchema
60
+ } {
57
61
  return {
58
62
  name: tool.name,
59
63
  description: tool.description,
60
- input_schema: tool.inputSchema as Anthropic.Tool.InputSchema,
64
+ input_schema: tool.inputSchema,
61
65
  }
62
66
  }
package/src/types.ts CHANGED
@@ -2,7 +2,17 @@
2
2
  * Core type definitions for the Agent SDK
3
3
  */
4
4
 
5
- import type Anthropic from '@anthropic-ai/sdk'
5
+ // Content block types (provider-agnostic, compatible with Anthropic format)
6
+ export type ContentBlockParam =
7
+ | { type: 'text'; text: string }
8
+ | { type: 'image'; source: any }
9
+ | { type: 'tool_use'; id: string; name: string; input: any }
10
+ | { type: 'tool_result'; tool_use_id: string; content: string | any[]; is_error?: boolean }
11
+
12
+ export type ContentBlock =
13
+ | { type: 'text'; text: string }
14
+ | { type: 'tool_use'; id: string; name: string; input: any }
15
+ | { type: 'thinking'; thinking: string }
6
16
 
7
17
  // --------------------------------------------------------------------------
8
18
  // Message Types
@@ -12,7 +22,7 @@ export type MessageRole = 'user' | 'assistant'
12
22
 
13
23
  export interface ConversationMessage {
14
24
  role: MessageRole
15
- content: string | Anthropic.ContentBlockParam[]
25
+ content: string | ContentBlockParam[]
16
26
  }
17
27
 
18
28
  export interface UserMessage {
@@ -26,7 +36,7 @@ export interface AssistantMessage {
26
36
  type: 'assistant'
27
37
  message: {
28
38
  role: 'assistant'
29
- content: Anthropic.ContentBlock[]
39
+ content: ContentBlock[]
30
40
  }
31
41
  uuid: string
32
42
  timestamp: string
@@ -57,7 +67,7 @@ export interface SDKAssistantMessage {
57
67
  session_id?: string
58
68
  message: {
59
69
  role: 'assistant'
60
- content: Anthropic.ContentBlock[]
70
+ content: ContentBlock[]
61
71
  }
62
72
  parent_tool_use_id?: string | null
63
73
  }
@@ -181,12 +191,18 @@ export interface ToolInputSchema {
181
191
  export interface ToolContext {
182
192
  cwd: string
183
193
  abortSignal?: AbortSignal
194
+ /** Parent agent's LLM provider (inherited by subagents) */
195
+ provider?: import('./providers/types.js').LLMProvider
196
+ /** Parent agent's model ID */
197
+ model?: string
198
+ /** Parent agent's API type */
199
+ apiType?: import('./providers/types.js').ApiType
184
200
  }
185
201
 
186
202
  export interface ToolResult {
187
203
  type: 'tool_result'
188
204
  tool_use_id: string
189
- content: string | Anthropic.ToolResultBlockParam['content']
205
+ content: string | any[]
190
206
  is_error?: boolean
191
207
  }
192
208
 
@@ -202,8 +218,10 @@ export type PermissionMode =
202
218
  | 'dontAsk'
203
219
  | 'auto'
204
220
 
221
+ export type PermissionBehavior = 'allow' | 'deny'
222
+
205
223
  export type CanUseToolResult = {
206
- behavior: 'allow' | 'deny'
224
+ behavior: PermissionBehavior
207
225
  updatedInput?: unknown
208
226
  message?: string
209
227
  }
@@ -326,6 +344,11 @@ export interface ModelInfo {
326
344
  export interface AgentOptions {
327
345
  /** LLM model ID */
328
346
  model?: string
347
+ /**
348
+ * API type: 'anthropic-messages' or 'openai-completions'.
349
+ * Falls back to CODEANY_API_TYPE env var. Default: 'anthropic-messages'.
350
+ */
351
+ apiType?: import('./providers/types.js').ApiType
329
352
  /** API key. Falls back to CODEANY_API_KEY env var. */
330
353
  apiKey?: string
331
354
  /** API base URL override */
@@ -442,8 +465,8 @@ export interface QueryResult {
442
465
  export interface QueryEngineConfig {
443
466
  cwd: string
444
467
  model: string
445
- apiKey?: string
446
- baseURL?: string
468
+ /** LLM provider instance (created from apiType) */
469
+ provider: import('./providers/types.js').LLMProvider
447
470
  tools: ToolDefinition[]
448
471
  systemPrompt?: string
449
472
  appendSystemPrompt?: string
@@ -456,4 +479,8 @@ export interface QueryEngineConfig {
456
479
  includePartialMessages: boolean
457
480
  abortSignal?: AbortSignal
458
481
  agents?: Record<string, AgentDefinition>
482
+ /** Hook registry for lifecycle events */
483
+ hookRegistry?: import('./hooks.js').HookRegistry
484
+ /** Session ID for hook context */
485
+ sessionId?: string
459
486
  }
@@ -8,7 +8,8 @@
8
8
  * 3. Session memory compaction: consolidates across sessions
9
9
  */
10
10
 
11
- import Anthropic from '@anthropic-ai/sdk'
11
+ import type { LLMProvider } from '../providers/types.js'
12
+ import type { NormalizedMessageParam } from '../providers/types.js'
12
13
  import {
13
14
  estimateMessagesTokens,
14
15
  getAutoCompactThreshold,
@@ -38,7 +39,7 @@ export function createAutoCompactState(): AutoCompactState {
38
39
  * Check if auto-compaction should trigger.
39
40
  */
40
41
  export function shouldAutoCompact(
41
- messages: Anthropic.MessageParam[],
42
+ messages: any[],
42
43
  model: string,
43
44
  state: AutoCompactState,
44
45
  ): boolean {
@@ -57,12 +58,12 @@ export function shouldAutoCompact(
57
58
  * then replaces the history with a compact summary.
58
59
  */
59
60
  export async function compactConversation(
60
- client: Anthropic,
61
+ provider: LLMProvider,
61
62
  model: string,
62
- messages: Anthropic.MessageParam[],
63
+ messages: any[],
63
64
  state: AutoCompactState,
64
65
  ): Promise<{
65
- compactedMessages: Anthropic.MessageParam[]
66
+ compactedMessages: NormalizedMessageParam[]
66
67
  summary: string
67
68
  state: AutoCompactState
68
69
  }> {
@@ -73,9 +74,9 @@ export async function compactConversation(
73
74
  // Build compaction prompt
74
75
  const compactionPrompt = buildCompactionPrompt(strippedMessages)
75
76
 
76
- const response = await client.messages.create({
77
+ const response = await provider.createMessage({
77
78
  model,
78
- max_tokens: 8192,
79
+ maxTokens: 8192,
79
80
  system: 'You are a conversation summarizer. Create a detailed summary of the conversation that preserves all important context, decisions made, files modified, tool outputs, and current state. The summary should allow the conversation to continue seamlessly.',
80
81
  messages: [
81
82
  {
@@ -86,12 +87,12 @@ export async function compactConversation(
86
87
  })
87
88
 
88
89
  const summary = response.content
89
- .filter((b): b is Anthropic.TextBlock => b.type === 'text')
90
- .map((b) => b.text)
90
+ .filter((b) => b.type === 'text')
91
+ .map((b) => (b as { type: 'text'; text: string }).text)
91
92
  .join('\n')
92
93
 
93
94
  // Replace messages with summary
94
- const compactedMessages: Anthropic.MessageParam[] = [
95
+ const compactedMessages: NormalizedMessageParam[] = [
95
96
  {
96
97
  role: 'user',
97
98
  content: `[Previous conversation summary]\n\n${summary}\n\n[End of summary - conversation continues below]`,
@@ -127,9 +128,9 @@ export async function compactConversation(
127
128
  * Strip images from messages for compaction safety.
128
129
  */
129
130
  function stripImagesFromMessages(
130
- messages: Anthropic.MessageParam[],
131
- ): Anthropic.MessageParam[] {
132
- return messages.map((msg) => {
131
+ messages: any[],
132
+ ): any[] {
133
+ return messages.map((msg: any) => {
133
134
  if (typeof msg.content === 'string') return msg
134
135
 
135
136
  const filtered = (msg.content as any[]).filter((block: any) => {
@@ -143,7 +144,7 @@ function stripImagesFromMessages(
143
144
  /**
144
145
  * Build compaction prompt from messages.
145
146
  */
146
- function buildCompactionPrompt(messages: Anthropic.MessageParam[]): string {
147
+ function buildCompactionPrompt(messages: any[]): string {
147
148
  const parts: string[] = ['Please summarize this conversation:\n']
148
149
 
149
150
  for (const msg of messages) {
@@ -179,10 +180,10 @@ function buildCompactionPrompt(messages: Anthropic.MessageParam[]): string {
179
180
  * to fit within token budgets.
180
181
  */
181
182
  export function microCompactMessages(
182
- messages: Anthropic.MessageParam[],
183
+ messages: any[],
183
184
  maxToolResultChars: number = 50000,
184
- ): Anthropic.MessageParam[] {
185
- return messages.map((msg) => {
185
+ ): any[] {
186
+ return messages.map((msg: any) => {
186
187
  if (typeof msg.content === 'string') return msg
187
188
  if (!Array.isArray(msg.content)) return msg
188
189