@codeany/open-agent-sdk 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/engine.d.ts.map +1 -1
  2. package/dist/engine.js +2 -0
  3. package/dist/engine.js.map +1 -1
  4. package/package.json +5 -6
  5. package/src/agent.ts +0 -516
  6. package/src/engine.ts +0 -630
  7. package/src/hooks.ts +0 -261
  8. package/src/index.ts +0 -426
  9. package/src/mcp/client.ts +0 -150
  10. package/src/providers/anthropic.ts +0 -60
  11. package/src/providers/index.ts +0 -34
  12. package/src/providers/openai.ts +0 -315
  13. package/src/providers/types.ts +0 -85
  14. package/src/sdk-mcp-server.ts +0 -78
  15. package/src/session.ts +0 -227
  16. package/src/skills/bundled/commit.ts +0 -38
  17. package/src/skills/bundled/debug.ts +0 -48
  18. package/src/skills/bundled/index.ts +0 -28
  19. package/src/skills/bundled/review.ts +0 -41
  20. package/src/skills/bundled/simplify.ts +0 -51
  21. package/src/skills/bundled/test.ts +0 -43
  22. package/src/skills/index.ts +0 -25
  23. package/src/skills/registry.ts +0 -133
  24. package/src/skills/types.ts +0 -99
  25. package/src/tool-helper.ts +0 -127
  26. package/src/tools/agent-tool.ts +0 -164
  27. package/src/tools/ask-user.ts +0 -79
  28. package/src/tools/bash.ts +0 -75
  29. package/src/tools/config-tool.ts +0 -89
  30. package/src/tools/cron-tools.ts +0 -153
  31. package/src/tools/edit.ts +0 -74
  32. package/src/tools/glob.ts +0 -77
  33. package/src/tools/grep.ts +0 -168
  34. package/src/tools/index.ts +0 -240
  35. package/src/tools/lsp-tool.ts +0 -163
  36. package/src/tools/mcp-resource-tools.ts +0 -125
  37. package/src/tools/notebook-edit.ts +0 -93
  38. package/src/tools/plan-tools.ts +0 -88
  39. package/src/tools/read.ts +0 -73
  40. package/src/tools/send-message.ts +0 -96
  41. package/src/tools/skill-tool.ts +0 -133
  42. package/src/tools/task-tools.ts +0 -290
  43. package/src/tools/team-tools.ts +0 -128
  44. package/src/tools/todo-tool.ts +0 -112
  45. package/src/tools/tool-search.ts +0 -87
  46. package/src/tools/types.ts +0 -66
  47. package/src/tools/web-fetch.ts +0 -66
  48. package/src/tools/web-search.ts +0 -86
  49. package/src/tools/worktree-tools.ts +0 -140
  50. package/src/tools/write.ts +0 -42
  51. package/src/types.ts +0 -486
  52. package/src/utils/compact.ts +0 -207
  53. package/src/utils/context.ts +0 -191
  54. package/src/utils/fileCache.ts +0 -148
  55. package/src/utils/messages.ts +0 -195
  56. package/src/utils/retry.ts +0 -140
  57. package/src/utils/tokens.ts +0 -145
@@ -1,240 +0,0 @@
1
- /**
2
- * Tool Registry - All built-in tool definitions
3
- *
4
- * 30+ tools covering file I/O, execution, search, web, agents,
5
- * tasks, teams, messaging, worktree, planning, scheduling, and more.
6
- */
7
-
8
- import type { ToolDefinition } from '../types.js'
9
-
10
- // File I/O
11
- import { BashTool } from './bash.js'
12
- import { FileReadTool } from './read.js'
13
- import { FileWriteTool } from './write.js'
14
- import { FileEditTool } from './edit.js'
15
- import { GlobTool } from './glob.js'
16
- import { GrepTool } from './grep.js'
17
- import { NotebookEditTool } from './notebook-edit.js'
18
-
19
- // Web
20
- import { WebFetchTool } from './web-fetch.js'
21
- import { WebSearchTool } from './web-search.js'
22
-
23
- // Agent & Multi-agent
24
- import { AgentTool } from './agent-tool.js'
25
- import { SendMessageTool } from './send-message.js'
26
- import { TeamCreateTool, TeamDeleteTool } from './team-tools.js'
27
-
28
- // Tasks
29
- import {
30
- TaskCreateTool,
31
- TaskListTool,
32
- TaskUpdateTool,
33
- TaskGetTool,
34
- TaskStopTool,
35
- TaskOutputTool,
36
- } from './task-tools.js'
37
-
38
- // Worktree
39
- import { EnterWorktreeTool, ExitWorktreeTool } from './worktree-tools.js'
40
-
41
- // Planning
42
- import { EnterPlanModeTool, ExitPlanModeTool } from './plan-tools.js'
43
-
44
- // User interaction
45
- import { AskUserQuestionTool } from './ask-user.js'
46
-
47
- // Discovery
48
- import { ToolSearchTool } from './tool-search.js'
49
-
50
- // MCP Resources
51
- import { ListMcpResourcesTool, ReadMcpResourceTool } from './mcp-resource-tools.js'
52
-
53
- // Scheduling
54
- import { CronCreateTool, CronDeleteTool, CronListTool, RemoteTriggerTool } from './cron-tools.js'
55
-
56
- // LSP
57
- import { LSPTool } from './lsp-tool.js'
58
-
59
- // Config
60
- import { ConfigTool } from './config-tool.js'
61
-
62
- // Todo
63
- import { TodoWriteTool } from './todo-tool.js'
64
-
65
- // Skill
66
- import { SkillTool } from './skill-tool.js'
67
-
68
- /**
69
- * All built-in tools (30+).
70
- */
71
- const ALL_TOOLS: ToolDefinition[] = [
72
- // Core file I/O & execution
73
- BashTool,
74
- FileReadTool,
75
- FileWriteTool,
76
- FileEditTool,
77
- GlobTool,
78
- GrepTool,
79
- NotebookEditTool,
80
-
81
- // Web
82
- WebFetchTool,
83
- WebSearchTool,
84
-
85
- // Agent & Multi-agent
86
- AgentTool,
87
- SendMessageTool,
88
- TeamCreateTool,
89
- TeamDeleteTool,
90
-
91
- // Tasks
92
- TaskCreateTool,
93
- TaskListTool,
94
- TaskUpdateTool,
95
- TaskGetTool,
96
- TaskStopTool,
97
- TaskOutputTool,
98
-
99
- // Worktree
100
- EnterWorktreeTool,
101
- ExitWorktreeTool,
102
-
103
- // Planning
104
- EnterPlanModeTool,
105
- ExitPlanModeTool,
106
-
107
- // User interaction
108
- AskUserQuestionTool,
109
-
110
- // Discovery
111
- ToolSearchTool,
112
-
113
- // MCP Resources
114
- ListMcpResourcesTool,
115
- ReadMcpResourceTool,
116
-
117
- // Scheduling
118
- CronCreateTool,
119
- CronDeleteTool,
120
- CronListTool,
121
- RemoteTriggerTool,
122
-
123
- // LSP
124
- LSPTool,
125
-
126
- // Config
127
- ConfigTool,
128
-
129
- // Todo
130
- TodoWriteTool,
131
-
132
- // Skill
133
- SkillTool,
134
- ]
135
-
136
- /**
137
- * Get all built-in tools.
138
- */
139
- export function getAllBaseTools(): ToolDefinition[] {
140
- return [...ALL_TOOLS]
141
- }
142
-
143
- /**
144
- * Filter tools by allowed/disallowed lists.
145
- */
146
- export function filterTools(
147
- tools: ToolDefinition[],
148
- allowedTools?: string[],
149
- disallowedTools?: string[],
150
- ): ToolDefinition[] {
151
- let filtered = tools
152
-
153
- if (allowedTools && allowedTools.length > 0) {
154
- const allowed = new Set(allowedTools)
155
- filtered = filtered.filter((t) => allowed.has(t.name))
156
- }
157
-
158
- if (disallowedTools && disallowedTools.length > 0) {
159
- const disallowed = new Set(disallowedTools)
160
- filtered = filtered.filter((t) => !disallowed.has(t.name))
161
- }
162
-
163
- return filtered
164
- }
165
-
166
- /**
167
- * Assemble tool pool: base tools + MCP tools, with deduplication.
168
- */
169
- export function assembleToolPool(
170
- baseTools: ToolDefinition[],
171
- mcpTools: ToolDefinition[] = [],
172
- allowedTools?: string[],
173
- disallowedTools?: string[],
174
- ): ToolDefinition[] {
175
- const combined = [...baseTools, ...mcpTools]
176
-
177
- // Deduplicate by name (later definitions override)
178
- const byName = new Map<string, ToolDefinition>()
179
- for (const tool of combined) {
180
- byName.set(tool.name, tool)
181
- }
182
-
183
- let tools = Array.from(byName.values())
184
- return filterTools(tools, allowedTools, disallowedTools)
185
- }
186
-
187
- // Re-export individual tools
188
- export {
189
- // Core
190
- BashTool,
191
- FileReadTool,
192
- FileWriteTool,
193
- FileEditTool,
194
- GlobTool,
195
- GrepTool,
196
- NotebookEditTool,
197
- WebFetchTool,
198
- WebSearchTool,
199
- // Agent
200
- AgentTool,
201
- SendMessageTool,
202
- TeamCreateTool,
203
- TeamDeleteTool,
204
- // Tasks
205
- TaskCreateTool,
206
- TaskListTool,
207
- TaskUpdateTool,
208
- TaskGetTool,
209
- TaskStopTool,
210
- TaskOutputTool,
211
- // Worktree
212
- EnterWorktreeTool,
213
- ExitWorktreeTool,
214
- // Planning
215
- EnterPlanModeTool,
216
- ExitPlanModeTool,
217
- // User
218
- AskUserQuestionTool,
219
- // Discovery
220
- ToolSearchTool,
221
- // MCP
222
- ListMcpResourcesTool,
223
- ReadMcpResourceTool,
224
- // Scheduling
225
- CronCreateTool,
226
- CronDeleteTool,
227
- CronListTool,
228
- RemoteTriggerTool,
229
- // LSP
230
- LSPTool,
231
- // Config
232
- ConfigTool,
233
- // Todo
234
- TodoWriteTool,
235
- // Skill
236
- SkillTool,
237
- }
238
-
239
- // Re-export helpers
240
- export { defineTool, toApiTool } from './types.js'
@@ -1,163 +0,0 @@
1
- /**
2
- * LSPTool - Language Server Protocol integration
3
- *
4
- * Provides code intelligence: go-to-definition, find-references,
5
- * hover, document symbols, workspace symbols, etc.
6
- */
7
-
8
- import { execSync } from 'child_process'
9
- import type { ToolDefinition, ToolResult } from '../types.js'
10
-
11
- export const LSPTool: ToolDefinition = {
12
- name: 'LSP',
13
- description: 'Language Server Protocol operations for code intelligence. Supports go-to-definition, find-references, hover, and symbol lookup.',
14
- inputSchema: {
15
- type: 'object',
16
- properties: {
17
- operation: {
18
- type: 'string',
19
- enum: [
20
- 'goToDefinition',
21
- 'findReferences',
22
- 'hover',
23
- 'documentSymbol',
24
- 'workspaceSymbol',
25
- 'goToImplementation',
26
- 'prepareCallHierarchy',
27
- 'incomingCalls',
28
- 'outgoingCalls',
29
- ],
30
- description: 'LSP operation to perform',
31
- },
32
- file_path: { type: 'string', description: 'File path for the operation' },
33
- line: { type: 'number', description: 'Line number (0-based)' },
34
- character: { type: 'number', description: 'Character position (0-based)' },
35
- query: { type: 'string', description: 'Symbol name (for workspace symbol search)' },
36
- },
37
- required: ['operation'],
38
- },
39
- isReadOnly: () => true,
40
- isConcurrencySafe: () => true,
41
- isEnabled: () => true,
42
- async prompt() { return 'Code intelligence via Language Server Protocol.' },
43
- async call(input: any, context: { cwd: string }): Promise<ToolResult> {
44
- const { operation, file_path, line, character, query } = input
45
-
46
- // LSP requires a running language server. In standalone mode,
47
- // we fall back to basic grep/ripgrep-based symbol lookup.
48
- try {
49
- switch (operation) {
50
- case 'goToDefinition':
51
- case 'goToImplementation': {
52
- if (!file_path || line === undefined) {
53
- return { type: 'tool_result', tool_use_id: '', content: 'file_path and line required', is_error: true }
54
- }
55
- // Use grep to find definition
56
- const symbol = await getSymbolAtPosition(file_path, line, character || 0, context.cwd)
57
- if (!symbol) {
58
- return { type: 'tool_result', tool_use_id: '', content: 'Could not identify symbol at position' }
59
- }
60
- const results = execSync(
61
- `rg -n "(?:function|class|interface|type|const|let|var|export)\\s+${symbol}" --type-add 'src:*.{ts,tsx,js,jsx,py,go,rs,java}' -t src ${context.cwd} 2>/dev/null || grep -rn "(?:function|class|interface|type|const|let|var|export)\\s*${symbol}" ${context.cwd} --include='*.ts' --include='*.js' 2>/dev/null`,
62
- { encoding: 'utf-8', timeout: 10000 },
63
- ).trim()
64
- return { type: 'tool_result', tool_use_id: '', content: results || `No definition found for "${symbol}"` }
65
- }
66
-
67
- case 'findReferences': {
68
- if (!file_path || line === undefined) {
69
- return { type: 'tool_result', tool_use_id: '', content: 'file_path and line required', is_error: true }
70
- }
71
- const sym = await getSymbolAtPosition(file_path, line, character || 0, context.cwd)
72
- if (!sym) {
73
- return { type: 'tool_result', tool_use_id: '', content: 'Could not identify symbol at position' }
74
- }
75
- const refs = execSync(
76
- `rg -n "${sym}" ${context.cwd} --type-add 'src:*.{ts,tsx,js,jsx,py,go,rs,java}' -t src 2>/dev/null | head -50`,
77
- { encoding: 'utf-8', timeout: 10000 },
78
- ).trim()
79
- return { type: 'tool_result', tool_use_id: '', content: refs || `No references found for "${sym}"` }
80
- }
81
-
82
- case 'hover': {
83
- return {
84
- type: 'tool_result',
85
- tool_use_id: '',
86
- content: 'Hover information requires a running language server. Use Read tool to examine the file content.',
87
- }
88
- }
89
-
90
- case 'documentSymbol': {
91
- if (!file_path) {
92
- return { type: 'tool_result', tool_use_id: '', content: 'file_path required', is_error: true }
93
- }
94
- const symbols = execSync(
95
- `rg -n "^\\s*(export\\s+)?(function|class|interface|type|const|let|var|enum)\\s+" ${JSON.stringify(file_path)} 2>/dev/null || grep -n "^\\s*\\(export\\s\\+\\)\\?\\(function\\|class\\|interface\\|type\\|const\\|let\\|var\\|enum\\)\\s" ${JSON.stringify(file_path)} 2>/dev/null`,
96
- { encoding: 'utf-8', cwd: context.cwd, timeout: 10000 },
97
- ).trim()
98
- return { type: 'tool_result', tool_use_id: '', content: symbols || 'No symbols found' }
99
- }
100
-
101
- case 'workspaceSymbol': {
102
- if (!query) {
103
- return { type: 'tool_result', tool_use_id: '', content: 'query required', is_error: true }
104
- }
105
- const wsSymbols = execSync(
106
- `rg -n "${query}" ${context.cwd} --type-add 'src:*.{ts,tsx,js,jsx,py,go,rs,java}' -t src 2>/dev/null | head -30`,
107
- { encoding: 'utf-8', timeout: 10000 },
108
- ).trim()
109
- return { type: 'tool_result', tool_use_id: '', content: wsSymbols || `No symbols found for "${query}"` }
110
- }
111
-
112
- default:
113
- return {
114
- type: 'tool_result',
115
- tool_use_id: '',
116
- content: `LSP operation "${operation}" requires a running language server.`,
117
- }
118
- }
119
- } catch (err: any) {
120
- return {
121
- type: 'tool_result',
122
- tool_use_id: '',
123
- content: `LSP error: ${err.message}`,
124
- is_error: true,
125
- }
126
- }
127
- },
128
- }
129
-
130
- /**
131
- * Get the symbol at a given position in a file.
132
- */
133
- async function getSymbolAtPosition(
134
- filePath: string,
135
- line: number,
136
- character: number,
137
- cwd: string,
138
- ): Promise<string | null> {
139
- try {
140
- const { readFile } = await import('fs/promises')
141
- const { resolve } = await import('path')
142
- const content = await readFile(resolve(cwd, filePath), 'utf-8')
143
- const lines = content.split('\n')
144
-
145
- if (line >= lines.length) return null
146
-
147
- const lineText = lines[line]
148
- if (!lineText || character >= lineText.length) return null
149
-
150
- // Extract word at position
151
- const wordMatch = /\b\w+\b/g
152
- let match
153
- while ((match = wordMatch.exec(lineText)) !== null) {
154
- if (match.index <= character && match.index + match[0].length >= character) {
155
- return match[0]
156
- }
157
- }
158
-
159
- return null
160
- } catch {
161
- return null
162
- }
163
- }
@@ -1,125 +0,0 @@
1
- /**
2
- * MCP Resource Tools
3
- *
4
- * ListMcpResources / ReadMcpResource - Access resources from MCP servers.
5
- */
6
-
7
- import type { ToolDefinition, ToolResult } from '../types.js'
8
- import type { MCPConnection } from '../mcp/client.js'
9
-
10
- // Registry of MCP connections (set by the agent)
11
- let mcpConnections: MCPConnection[] = []
12
-
13
- /**
14
- * Set MCP connections for resource access.
15
- */
16
- export function setMcpConnections(connections: MCPConnection[]): void {
17
- mcpConnections = connections
18
- }
19
-
20
- export const ListMcpResourcesTool: ToolDefinition = {
21
- name: 'ListMcpResources',
22
- description: 'List available resources from connected MCP servers. Resources can include files, databases, and other data sources.',
23
- inputSchema: {
24
- type: 'object',
25
- properties: {
26
- server: { type: 'string', description: 'Filter by MCP server name' },
27
- },
28
- },
29
- isReadOnly: () => true,
30
- isConcurrencySafe: () => true,
31
- isEnabled: () => true,
32
- async prompt() { return 'List MCP resources.' },
33
- async call(input: any): Promise<ToolResult> {
34
- const connections = input.server
35
- ? mcpConnections.filter(c => c.name === input.server)
36
- : mcpConnections
37
-
38
- if (connections.length === 0) {
39
- return {
40
- type: 'tool_result',
41
- tool_use_id: '',
42
- content: 'No MCP servers connected.',
43
- }
44
- }
45
-
46
- const results: string[] = []
47
-
48
- for (const conn of connections) {
49
- if (conn.status !== 'connected') continue
50
-
51
- try {
52
- // Access the underlying client to list resources
53
- const resources = (conn as any)._client?.listResources?.()
54
- if (resources) {
55
- results.push(`Server: ${conn.name}`)
56
- for (const r of resources) {
57
- results.push(` - ${r.name}: ${r.description || r.uri || ''}`)
58
- }
59
- } else {
60
- results.push(`Server: ${conn.name} (${conn.tools.length} tools available)`)
61
- }
62
- } catch {
63
- results.push(`Server: ${conn.name} (resource listing not supported)`)
64
- }
65
- }
66
-
67
- return {
68
- type: 'tool_result',
69
- tool_use_id: '',
70
- content: results.join('\n') || 'No resources found.',
71
- }
72
- },
73
- }
74
-
75
- export const ReadMcpResourceTool: ToolDefinition = {
76
- name: 'ReadMcpResource',
77
- description: 'Read a specific resource from an MCP server.',
78
- inputSchema: {
79
- type: 'object',
80
- properties: {
81
- server: { type: 'string', description: 'MCP server name' },
82
- uri: { type: 'string', description: 'Resource URI to read' },
83
- },
84
- required: ['server', 'uri'],
85
- },
86
- isReadOnly: () => true,
87
- isConcurrencySafe: () => true,
88
- isEnabled: () => true,
89
- async prompt() { return 'Read an MCP resource.' },
90
- async call(input: any): Promise<ToolResult> {
91
- const conn = mcpConnections.find(c => c.name === input.server)
92
- if (!conn) {
93
- return {
94
- type: 'tool_result',
95
- tool_use_id: '',
96
- content: `MCP server not found: ${input.server}`,
97
- is_error: true,
98
- }
99
- }
100
-
101
- try {
102
- const result = await (conn as any)._client?.readResource?.({ uri: input.uri })
103
- if (result?.contents) {
104
- const texts = result.contents.map((c: any) => c.text || JSON.stringify(c)).join('\n')
105
- return {
106
- type: 'tool_result',
107
- tool_use_id: '',
108
- content: texts,
109
- }
110
- }
111
- return {
112
- type: 'tool_result',
113
- tool_use_id: '',
114
- content: 'Resource read returned no content.',
115
- }
116
- } catch (err: any) {
117
- return {
118
- type: 'tool_result',
119
- tool_use_id: '',
120
- content: `Error reading resource: ${err.message}`,
121
- is_error: true,
122
- }
123
- }
124
- },
125
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * NotebookEditTool - Edit Jupyter notebooks
3
- */
4
-
5
- import { readFile, writeFile } from 'fs/promises'
6
- import { resolve } from 'path'
7
- import { defineTool } from './types.js'
8
-
9
- export const NotebookEditTool = defineTool({
10
- name: 'NotebookEdit',
11
- description: 'Edit Jupyter notebook (.ipynb) cells. Can insert, replace, or delete cells.',
12
- inputSchema: {
13
- type: 'object',
14
- properties: {
15
- file_path: {
16
- type: 'string',
17
- description: 'Path to the .ipynb file',
18
- },
19
- command: {
20
- type: 'string',
21
- enum: ['insert', 'replace', 'delete'],
22
- description: 'The edit operation to perform',
23
- },
24
- cell_number: {
25
- type: 'number',
26
- description: 'Cell index (0-based) to operate on',
27
- },
28
- cell_type: {
29
- type: 'string',
30
- enum: ['code', 'markdown'],
31
- description: 'Type of cell (for insert/replace)',
32
- },
33
- source: {
34
- type: 'string',
35
- description: 'Cell content (for insert/replace)',
36
- },
37
- },
38
- required: ['file_path', 'command', 'cell_number'],
39
- },
40
- isReadOnly: false,
41
- isConcurrencySafe: false,
42
- async call(input, context) {
43
- const filePath = resolve(context.cwd, input.file_path)
44
-
45
- try {
46
- const content = await readFile(filePath, 'utf-8')
47
- const notebook = JSON.parse(content)
48
-
49
- if (!notebook.cells || !Array.isArray(notebook.cells)) {
50
- return { data: 'Error: Invalid notebook format', is_error: true }
51
- }
52
-
53
- const { command, cell_number, cell_type, source } = input
54
-
55
- switch (command) {
56
- case 'insert': {
57
- const newCell = {
58
- cell_type: cell_type || 'code',
59
- source: (source || '').split('\n').map((l: string, i: number, arr: string[]) =>
60
- i < arr.length - 1 ? l + '\n' : l
61
- ),
62
- metadata: {},
63
- ...(cell_type !== 'markdown' ? { outputs: [], execution_count: null } : {}),
64
- }
65
- notebook.cells.splice(cell_number, 0, newCell)
66
- break
67
- }
68
- case 'replace': {
69
- if (cell_number >= notebook.cells.length) {
70
- return { data: `Error: Cell ${cell_number} does not exist`, is_error: true }
71
- }
72
- notebook.cells[cell_number].source = (source || '').split('\n').map(
73
- (l: string, i: number, arr: string[]) => i < arr.length - 1 ? l + '\n' : l
74
- )
75
- if (cell_type) notebook.cells[cell_number].cell_type = cell_type
76
- break
77
- }
78
- case 'delete': {
79
- if (cell_number >= notebook.cells.length) {
80
- return { data: `Error: Cell ${cell_number} does not exist`, is_error: true }
81
- }
82
- notebook.cells.splice(cell_number, 1)
83
- break
84
- }
85
- }
86
-
87
- await writeFile(filePath, JSON.stringify(notebook, null, 1), 'utf-8')
88
- return `Notebook ${command}: cell ${cell_number} in ${filePath}`
89
- } catch (err: any) {
90
- return { data: `Error: ${err.message}`, is_error: true }
91
- }
92
- },
93
- })
@@ -1,88 +0,0 @@
1
- /**
2
- * Plan Mode Tools
3
- *
4
- * EnterPlanMode / ExitPlanMode - Structured planning workflow.
5
- * Allows the agent to enter a design/planning phase before execution.
6
- */
7
-
8
- import type { ToolDefinition, ToolResult } from '../types.js'
9
-
10
- // Track plan mode state
11
- let planModeActive = false
12
- let currentPlan: string | null = null
13
-
14
- export function isPlanModeActive(): boolean {
15
- return planModeActive
16
- }
17
-
18
- export function getCurrentPlan(): string | null {
19
- return currentPlan
20
- }
21
-
22
- export const EnterPlanModeTool: ToolDefinition = {
23
- name: 'EnterPlanMode',
24
- description: 'Enter plan/design mode for complex tasks. In plan mode, the agent focuses on designing the approach before executing.',
25
- inputSchema: {
26
- type: 'object',
27
- properties: {},
28
- },
29
- isReadOnly: () => false,
30
- isConcurrencySafe: () => false,
31
- isEnabled: () => true,
32
- async prompt() { return 'Enter plan mode for structured planning.' },
33
- async call(): Promise<ToolResult> {
34
- if (planModeActive) {
35
- return {
36
- type: 'tool_result',
37
- tool_use_id: '',
38
- content: 'Already in plan mode.',
39
- }
40
- }
41
-
42
- planModeActive = true
43
- currentPlan = null
44
-
45
- return {
46
- type: 'tool_result',
47
- tool_use_id: '',
48
- content: 'Entered plan mode. Design your approach before executing. Use ExitPlanMode when the plan is ready.',
49
- }
50
- },
51
- }
52
-
53
- export const ExitPlanModeTool: ToolDefinition = {
54
- name: 'ExitPlanMode',
55
- description: 'Exit plan mode with a completed plan. The plan will be recorded and execution can proceed.',
56
- inputSchema: {
57
- type: 'object',
58
- properties: {
59
- plan: { type: 'string', description: 'The completed plan' },
60
- approved: { type: 'boolean', description: 'Whether the plan is approved for execution' },
61
- },
62
- },
63
- isReadOnly: () => false,
64
- isConcurrencySafe: () => false,
65
- isEnabled: () => true,
66
- async prompt() { return 'Exit plan mode with a completed plan.' },
67
- async call(input: any): Promise<ToolResult> {
68
- if (!planModeActive) {
69
- return {
70
- type: 'tool_result',
71
- tool_use_id: '',
72
- content: 'Not in plan mode.',
73
- is_error: true,
74
- }
75
- }
76
-
77
- planModeActive = false
78
- currentPlan = input.plan || null
79
-
80
- const status = input.approved !== false ? 'approved' : 'pending approval'
81
-
82
- return {
83
- type: 'tool_result',
84
- tool_use_id: '',
85
- content: `Plan mode exited. Plan status: ${status}.${currentPlan ? `\n\nPlan:\n${currentPlan}` : ''}`,
86
- }
87
- },
88
- }