@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.
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -0
- package/dist/engine.js.map +1 -1
- package/package.json +5 -6
- package/src/agent.ts +0 -516
- package/src/engine.ts +0 -630
- package/src/hooks.ts +0 -261
- package/src/index.ts +0 -426
- package/src/mcp/client.ts +0 -150
- package/src/providers/anthropic.ts +0 -60
- package/src/providers/index.ts +0 -34
- package/src/providers/openai.ts +0 -315
- package/src/providers/types.ts +0 -85
- package/src/sdk-mcp-server.ts +0 -78
- package/src/session.ts +0 -227
- package/src/skills/bundled/commit.ts +0 -38
- package/src/skills/bundled/debug.ts +0 -48
- package/src/skills/bundled/index.ts +0 -28
- package/src/skills/bundled/review.ts +0 -41
- package/src/skills/bundled/simplify.ts +0 -51
- package/src/skills/bundled/test.ts +0 -43
- package/src/skills/index.ts +0 -25
- package/src/skills/registry.ts +0 -133
- package/src/skills/types.ts +0 -99
- package/src/tool-helper.ts +0 -127
- package/src/tools/agent-tool.ts +0 -164
- package/src/tools/ask-user.ts +0 -79
- package/src/tools/bash.ts +0 -75
- package/src/tools/config-tool.ts +0 -89
- package/src/tools/cron-tools.ts +0 -153
- package/src/tools/edit.ts +0 -74
- package/src/tools/glob.ts +0 -77
- package/src/tools/grep.ts +0 -168
- package/src/tools/index.ts +0 -240
- package/src/tools/lsp-tool.ts +0 -163
- package/src/tools/mcp-resource-tools.ts +0 -125
- package/src/tools/notebook-edit.ts +0 -93
- package/src/tools/plan-tools.ts +0 -88
- package/src/tools/read.ts +0 -73
- package/src/tools/send-message.ts +0 -96
- package/src/tools/skill-tool.ts +0 -133
- package/src/tools/task-tools.ts +0 -290
- package/src/tools/team-tools.ts +0 -128
- package/src/tools/todo-tool.ts +0 -112
- package/src/tools/tool-search.ts +0 -87
- package/src/tools/types.ts +0 -66
- package/src/tools/web-fetch.ts +0 -66
- package/src/tools/web-search.ts +0 -86
- package/src/tools/worktree-tools.ts +0 -140
- package/src/tools/write.ts +0 -42
- package/src/types.ts +0 -486
- package/src/utils/compact.ts +0 -207
- package/src/utils/context.ts +0 -191
- package/src/utils/fileCache.ts +0 -148
- package/src/utils/messages.ts +0 -195
- package/src/utils/retry.ts +0 -140
- package/src/utils/tokens.ts +0 -145
package/src/tools/team-tools.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Team Management Tools
|
|
3
|
-
*
|
|
4
|
-
* TeamCreate, TeamDelete - Multi-agent team coordination.
|
|
5
|
-
* Manages team composition, task lists, and inter-agent messaging.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ToolDefinition, ToolResult } from '../types.js'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Team definition.
|
|
12
|
-
*/
|
|
13
|
-
export interface Team {
|
|
14
|
-
id: string
|
|
15
|
-
name: string
|
|
16
|
-
members: string[]
|
|
17
|
-
leaderId: string
|
|
18
|
-
taskListId?: string
|
|
19
|
-
createdAt: string
|
|
20
|
-
status: 'active' | 'disbanded'
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Global team store.
|
|
25
|
-
*/
|
|
26
|
-
const teamStore = new Map<string, Team>()
|
|
27
|
-
let teamCounter = 0
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Get all teams.
|
|
31
|
-
*/
|
|
32
|
-
export function getAllTeams(): Team[] {
|
|
33
|
-
return Array.from(teamStore.values())
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get a team by ID.
|
|
38
|
-
*/
|
|
39
|
-
export function getTeam(id: string): Team | undefined {
|
|
40
|
-
return teamStore.get(id)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Clear all teams.
|
|
45
|
-
*/
|
|
46
|
-
export function clearTeams(): void {
|
|
47
|
-
teamStore.clear()
|
|
48
|
-
teamCounter = 0
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// TeamCreateTool
|
|
53
|
-
// ============================================================================
|
|
54
|
-
|
|
55
|
-
export const TeamCreateTool: ToolDefinition = {
|
|
56
|
-
name: 'TeamCreate',
|
|
57
|
-
description: 'Create a multi-agent team for coordinated work. Assigns a lead and manages member composition.',
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: 'object',
|
|
60
|
-
properties: {
|
|
61
|
-
name: { type: 'string', description: 'Team name' },
|
|
62
|
-
members: {
|
|
63
|
-
type: 'array',
|
|
64
|
-
items: { type: 'string' },
|
|
65
|
-
description: 'List of agent/teammate names',
|
|
66
|
-
},
|
|
67
|
-
task_description: { type: 'string', description: 'Description of the team\'s mission' },
|
|
68
|
-
},
|
|
69
|
-
required: ['name'],
|
|
70
|
-
},
|
|
71
|
-
isReadOnly: () => false,
|
|
72
|
-
isConcurrencySafe: () => false,
|
|
73
|
-
isEnabled: () => true,
|
|
74
|
-
async prompt() { return 'Create a team for multi-agent coordination.' },
|
|
75
|
-
async call(input: any): Promise<ToolResult> {
|
|
76
|
-
const id = `team_${++teamCounter}`
|
|
77
|
-
const team: Team = {
|
|
78
|
-
id,
|
|
79
|
-
name: input.name,
|
|
80
|
-
members: input.members || [],
|
|
81
|
-
leaderId: 'self',
|
|
82
|
-
createdAt: new Date().toISOString(),
|
|
83
|
-
status: 'active',
|
|
84
|
-
}
|
|
85
|
-
teamStore.set(id, team)
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
type: 'tool_result',
|
|
89
|
-
tool_use_id: '',
|
|
90
|
-
content: `Team created: ${id} "${team.name}" with ${team.members.length} members`,
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ============================================================================
|
|
96
|
-
// TeamDeleteTool
|
|
97
|
-
// ============================================================================
|
|
98
|
-
|
|
99
|
-
export const TeamDeleteTool: ToolDefinition = {
|
|
100
|
-
name: 'TeamDelete',
|
|
101
|
-
description: 'Disband a team and clean up resources.',
|
|
102
|
-
inputSchema: {
|
|
103
|
-
type: 'object',
|
|
104
|
-
properties: {
|
|
105
|
-
id: { type: 'string', description: 'Team ID to disband' },
|
|
106
|
-
},
|
|
107
|
-
required: ['id'],
|
|
108
|
-
},
|
|
109
|
-
isReadOnly: () => false,
|
|
110
|
-
isConcurrencySafe: () => false,
|
|
111
|
-
isEnabled: () => true,
|
|
112
|
-
async prompt() { return 'Delete/disband a team.' },
|
|
113
|
-
async call(input: any): Promise<ToolResult> {
|
|
114
|
-
const team = teamStore.get(input.id)
|
|
115
|
-
if (!team) {
|
|
116
|
-
return { type: 'tool_result', tool_use_id: '', content: `Team not found: ${input.id}`, is_error: true }
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
team.status = 'disbanded'
|
|
120
|
-
teamStore.delete(input.id)
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
type: 'tool_result',
|
|
124
|
-
tool_use_id: '',
|
|
125
|
-
content: `Team disbanded: ${team.name}`,
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
}
|
package/src/tools/todo-tool.ts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TodoWriteTool - Session todo/checklist management
|
|
3
|
-
*
|
|
4
|
-
* Manages a session-scoped todo list for tracking work items.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { ToolDefinition, ToolResult } from '../types.js'
|
|
8
|
-
|
|
9
|
-
export interface TodoItem {
|
|
10
|
-
id: number
|
|
11
|
-
text: string
|
|
12
|
-
done: boolean
|
|
13
|
-
priority?: 'high' | 'medium' | 'low'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const todoList: TodoItem[] = []
|
|
17
|
-
let todoCounter = 0
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Get all todos.
|
|
21
|
-
*/
|
|
22
|
-
export function getTodos(): TodoItem[] {
|
|
23
|
-
return [...todoList]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Clear all todos.
|
|
28
|
-
*/
|
|
29
|
-
export function clearTodos(): void {
|
|
30
|
-
todoList.length = 0
|
|
31
|
-
todoCounter = 0
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const TodoWriteTool: ToolDefinition = {
|
|
35
|
-
name: 'TodoWrite',
|
|
36
|
-
description: 'Manage a session todo/checklist. Supports add, toggle, remove, and list operations.',
|
|
37
|
-
inputSchema: {
|
|
38
|
-
type: 'object',
|
|
39
|
-
properties: {
|
|
40
|
-
action: {
|
|
41
|
-
type: 'string',
|
|
42
|
-
enum: ['add', 'toggle', 'remove', 'list', 'clear'],
|
|
43
|
-
description: 'Operation to perform',
|
|
44
|
-
},
|
|
45
|
-
text: { type: 'string', description: 'Todo item text (for add)' },
|
|
46
|
-
id: { type: 'number', description: 'Todo item ID (for toggle/remove)' },
|
|
47
|
-
priority: {
|
|
48
|
-
type: 'string',
|
|
49
|
-
enum: ['high', 'medium', 'low'],
|
|
50
|
-
description: 'Priority level (for add)',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
required: ['action'],
|
|
54
|
-
},
|
|
55
|
-
isReadOnly: () => false,
|
|
56
|
-
isConcurrencySafe: () => true,
|
|
57
|
-
isEnabled: () => true,
|
|
58
|
-
async prompt() { return 'Manage session todo list.' },
|
|
59
|
-
async call(input: any): Promise<ToolResult> {
|
|
60
|
-
switch (input.action) {
|
|
61
|
-
case 'add': {
|
|
62
|
-
if (!input.text) {
|
|
63
|
-
return { type: 'tool_result', tool_use_id: '', content: 'text required', is_error: true }
|
|
64
|
-
}
|
|
65
|
-
const item: TodoItem = {
|
|
66
|
-
id: ++todoCounter,
|
|
67
|
-
text: input.text,
|
|
68
|
-
done: false,
|
|
69
|
-
priority: input.priority,
|
|
70
|
-
}
|
|
71
|
-
todoList.push(item)
|
|
72
|
-
return { type: 'tool_result', tool_use_id: '', content: `Todo added: #${item.id} "${item.text}"` }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
case 'toggle': {
|
|
76
|
-
const item = todoList.find(t => t.id === input.id)
|
|
77
|
-
if (!item) {
|
|
78
|
-
return { type: 'tool_result', tool_use_id: '', content: `Todo #${input.id} not found`, is_error: true }
|
|
79
|
-
}
|
|
80
|
-
item.done = !item.done
|
|
81
|
-
return { type: 'tool_result', tool_use_id: '', content: `Todo #${item.id} ${item.done ? 'completed' : 'reopened'}` }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
case 'remove': {
|
|
85
|
-
const idx = todoList.findIndex(t => t.id === input.id)
|
|
86
|
-
if (idx === -1) {
|
|
87
|
-
return { type: 'tool_result', tool_use_id: '', content: `Todo #${input.id} not found`, is_error: true }
|
|
88
|
-
}
|
|
89
|
-
todoList.splice(idx, 1)
|
|
90
|
-
return { type: 'tool_result', tool_use_id: '', content: `Todo #${input.id} removed` }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
case 'list': {
|
|
94
|
-
if (todoList.length === 0) {
|
|
95
|
-
return { type: 'tool_result', tool_use_id: '', content: 'No todos.' }
|
|
96
|
-
}
|
|
97
|
-
const lines = todoList.map(t =>
|
|
98
|
-
`${t.done ? '[x]' : '[ ]'} #${t.id} ${t.text}${t.priority ? ` (${t.priority})` : ''}`
|
|
99
|
-
)
|
|
100
|
-
return { type: 'tool_result', tool_use_id: '', content: lines.join('\n') }
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
case 'clear': {
|
|
104
|
-
todoList.length = 0
|
|
105
|
-
return { type: 'tool_result', tool_use_id: '', content: 'All todos cleared.' }
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
default:
|
|
109
|
-
return { type: 'tool_result', tool_use_id: '', content: `Unknown action: ${input.action}`, is_error: true }
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
}
|
package/src/tools/tool-search.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ToolSearchTool - Discover deferred/lazy-loaded tools
|
|
3
|
-
*
|
|
4
|
-
* Allows the model to search for tools that haven't been loaded yet.
|
|
5
|
-
* Supports keyword search and exact name selection.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ToolDefinition, ToolResult } from '../types.js'
|
|
9
|
-
|
|
10
|
-
// Registry of deferred tools (set by the agent)
|
|
11
|
-
let deferredTools: ToolDefinition[] = []
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Set deferred tools available for search.
|
|
15
|
-
*/
|
|
16
|
-
export function setDeferredTools(tools: ToolDefinition[]): void {
|
|
17
|
-
deferredTools = tools
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const ToolSearchTool: ToolDefinition = {
|
|
21
|
-
name: 'ToolSearch',
|
|
22
|
-
description: 'Search for additional tools that may be available but not yet loaded. Use keyword search or exact name selection.',
|
|
23
|
-
inputSchema: {
|
|
24
|
-
type: 'object',
|
|
25
|
-
properties: {
|
|
26
|
-
query: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
description: 'Search query. Use "select:ToolName" for exact match or keywords for search.',
|
|
29
|
-
},
|
|
30
|
-
max_results: {
|
|
31
|
-
type: 'number',
|
|
32
|
-
description: 'Maximum results to return (default: 5)',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
required: ['query'],
|
|
36
|
-
},
|
|
37
|
-
isReadOnly: () => true,
|
|
38
|
-
isConcurrencySafe: () => true,
|
|
39
|
-
isEnabled: () => true,
|
|
40
|
-
async prompt() { return 'Search for available tools.' },
|
|
41
|
-
async call(input: any): Promise<ToolResult> {
|
|
42
|
-
const { query, max_results = 5 } = input
|
|
43
|
-
|
|
44
|
-
if (deferredTools.length === 0) {
|
|
45
|
-
return {
|
|
46
|
-
type: 'tool_result',
|
|
47
|
-
tool_use_id: '',
|
|
48
|
-
content: 'No deferred tools available.',
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let matches: ToolDefinition[]
|
|
53
|
-
|
|
54
|
-
if (query.startsWith('select:')) {
|
|
55
|
-
// Exact name selection
|
|
56
|
-
const names = query.slice(7).split(',').map((n: string) => n.trim())
|
|
57
|
-
matches = deferredTools.filter(t => names.includes(t.name))
|
|
58
|
-
} else {
|
|
59
|
-
// Keyword search
|
|
60
|
-
const keywords: string[] = query.toLowerCase().split(/\s+/)
|
|
61
|
-
matches = deferredTools
|
|
62
|
-
.filter(t => {
|
|
63
|
-
const searchText = `${t.name} ${t.description}`.toLowerCase()
|
|
64
|
-
return keywords.some((kw: string) => searchText.includes(kw))
|
|
65
|
-
})
|
|
66
|
-
.slice(0, max_results)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (matches.length === 0) {
|
|
70
|
-
return {
|
|
71
|
-
type: 'tool_result',
|
|
72
|
-
tool_use_id: '',
|
|
73
|
-
content: `No tools found matching "${query}"`,
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const lines = matches.map(t =>
|
|
78
|
-
`- ${t.name}: ${t.description.slice(0, 200)}`
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
type: 'tool_result',
|
|
83
|
-
tool_use_id: '',
|
|
84
|
-
content: `Found ${matches.length} tool(s):\n${lines.join('\n')}`,
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
}
|
package/src/tools/types.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool interface and helper utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ToolDefinition, ToolInputSchema, ToolContext, ToolResult } from '../types.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Helper to create a tool definition with sensible defaults.
|
|
9
|
-
*/
|
|
10
|
-
export function defineTool(config: {
|
|
11
|
-
name: string
|
|
12
|
-
description: string
|
|
13
|
-
inputSchema: ToolInputSchema
|
|
14
|
-
call: (input: any, context: ToolContext) => Promise<string | { data: string; is_error?: boolean }>
|
|
15
|
-
isReadOnly?: boolean
|
|
16
|
-
isConcurrencySafe?: boolean
|
|
17
|
-
prompt?: string | ((context: ToolContext) => Promise<string>)
|
|
18
|
-
}): ToolDefinition {
|
|
19
|
-
return {
|
|
20
|
-
name: config.name,
|
|
21
|
-
description: config.description,
|
|
22
|
-
inputSchema: config.inputSchema,
|
|
23
|
-
isReadOnly: () => config.isReadOnly ?? false,
|
|
24
|
-
isConcurrencySafe: () => config.isConcurrencySafe ?? false,
|
|
25
|
-
isEnabled: () => true,
|
|
26
|
-
prompt: typeof config.prompt === 'function'
|
|
27
|
-
? config.prompt
|
|
28
|
-
: async (_context: ToolContext) => (config.prompt as string) ?? config.description,
|
|
29
|
-
async call(input: any, context: ToolContext): Promise<ToolResult> {
|
|
30
|
-
try {
|
|
31
|
-
const result = await config.call(input, context)
|
|
32
|
-
const output = typeof result === 'string' ? result : result.data
|
|
33
|
-
const isError = typeof result === 'object' && result.is_error
|
|
34
|
-
return {
|
|
35
|
-
type: 'tool_result',
|
|
36
|
-
tool_use_id: '', // filled by engine
|
|
37
|
-
content: output,
|
|
38
|
-
is_error: isError || false,
|
|
39
|
-
}
|
|
40
|
-
} catch (err: any) {
|
|
41
|
-
return {
|
|
42
|
-
type: 'tool_result',
|
|
43
|
-
tool_use_id: '',
|
|
44
|
-
content: `Error: ${err.message}`,
|
|
45
|
-
is_error: true,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Convert a ToolDefinition to API-compatible tool format.
|
|
54
|
-
* Returns the normalized tool format used by providers.
|
|
55
|
-
*/
|
|
56
|
-
export function toApiTool(tool: ToolDefinition): {
|
|
57
|
-
name: string
|
|
58
|
-
description: string
|
|
59
|
-
input_schema: ToolInputSchema
|
|
60
|
-
} {
|
|
61
|
-
return {
|
|
62
|
-
name: tool.name,
|
|
63
|
-
description: tool.description,
|
|
64
|
-
input_schema: tool.inputSchema,
|
|
65
|
-
}
|
|
66
|
-
}
|
package/src/tools/web-fetch.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebFetchTool - Fetch web content
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { defineTool } from './types.js'
|
|
6
|
-
|
|
7
|
-
export const WebFetchTool = defineTool({
|
|
8
|
-
name: 'WebFetch',
|
|
9
|
-
description: 'Fetch content from a URL and return it as text. Supports HTML pages, JSON APIs, and plain text. Strips HTML tags for readability.',
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: 'object',
|
|
12
|
-
properties: {
|
|
13
|
-
url: {
|
|
14
|
-
type: 'string',
|
|
15
|
-
description: 'The URL to fetch content from',
|
|
16
|
-
},
|
|
17
|
-
headers: {
|
|
18
|
-
type: 'object',
|
|
19
|
-
description: 'Optional HTTP headers',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
required: ['url'],
|
|
23
|
-
},
|
|
24
|
-
isReadOnly: true,
|
|
25
|
-
isConcurrencySafe: true,
|
|
26
|
-
async call(input, _context) {
|
|
27
|
-
const { url, headers } = input
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
const response = await fetch(url, {
|
|
31
|
-
headers: {
|
|
32
|
-
'User-Agent': 'Mozilla/5.0 (compatible; AgentSDK/1.0)',
|
|
33
|
-
...headers,
|
|
34
|
-
},
|
|
35
|
-
signal: AbortSignal.timeout(30000),
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
if (!response.ok) {
|
|
39
|
-
return { data: `HTTP ${response.status}: ${response.statusText}`, is_error: true }
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const contentType = response.headers.get('content-type') || ''
|
|
43
|
-
let text = await response.text()
|
|
44
|
-
|
|
45
|
-
// Strip HTML tags for readability
|
|
46
|
-
if (contentType.includes('text/html')) {
|
|
47
|
-
// Remove script and style blocks
|
|
48
|
-
text = text.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
49
|
-
text = text.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
50
|
-
// Remove HTML tags
|
|
51
|
-
text = text.replace(/<[^>]+>/g, ' ')
|
|
52
|
-
// Clean up whitespace
|
|
53
|
-
text = text.replace(/\s+/g, ' ').trim()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Truncate very large responses
|
|
57
|
-
if (text.length > 100000) {
|
|
58
|
-
text = text.slice(0, 100000) + '\n...(truncated)'
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return text || '(empty response)'
|
|
62
|
-
} catch (err: any) {
|
|
63
|
-
return { data: `Error fetching ${url}: ${err.message}`, is_error: true }
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
})
|
package/src/tools/web-search.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSearchTool - Web search (via web fetch of search engines)
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { defineTool } from './types.js'
|
|
6
|
-
|
|
7
|
-
export const WebSearchTool = defineTool({
|
|
8
|
-
name: 'WebSearch',
|
|
9
|
-
description: 'Search the web for information. Returns search results with titles, URLs, and snippets.',
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: 'object',
|
|
12
|
-
properties: {
|
|
13
|
-
query: {
|
|
14
|
-
type: 'string',
|
|
15
|
-
description: 'The search query',
|
|
16
|
-
},
|
|
17
|
-
num_results: {
|
|
18
|
-
type: 'number',
|
|
19
|
-
description: 'Number of results to return (default: 5)',
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
required: ['query'],
|
|
23
|
-
},
|
|
24
|
-
isReadOnly: true,
|
|
25
|
-
isConcurrencySafe: true,
|
|
26
|
-
async call(input, _context) {
|
|
27
|
-
const { query } = input
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
// Use DuckDuckGo HTML search as a free fallback
|
|
31
|
-
const encoded = encodeURIComponent(query)
|
|
32
|
-
const url = `https://html.duckduckgo.com/html/?q=${encoded}`
|
|
33
|
-
|
|
34
|
-
const response = await fetch(url, {
|
|
35
|
-
headers: {
|
|
36
|
-
'User-Agent': 'Mozilla/5.0 (compatible; AgentSDK/1.0)',
|
|
37
|
-
},
|
|
38
|
-
signal: AbortSignal.timeout(15000),
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
if (!response.ok) {
|
|
42
|
-
return { data: `Search failed: HTTP ${response.status}`, is_error: true }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const html = await response.text()
|
|
46
|
-
|
|
47
|
-
// Parse search results from DuckDuckGo HTML
|
|
48
|
-
const results: string[] = []
|
|
49
|
-
const resultRegex = /<a rel="nofollow" class="result__a" href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi
|
|
50
|
-
const snippetRegex = /<a class="result__snippet"[^>]*>([\s\S]*?)<\/a>/gi
|
|
51
|
-
|
|
52
|
-
let match
|
|
53
|
-
const links: Array<{ title: string; url: string }> = []
|
|
54
|
-
|
|
55
|
-
while ((match = resultRegex.exec(html)) !== null) {
|
|
56
|
-
const href = match[1]
|
|
57
|
-
const title = match[2].replace(/<[^>]+>/g, '').trim()
|
|
58
|
-
if (href && title && !href.includes('duckduckgo.com')) {
|
|
59
|
-
links.push({ title, url: href })
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const snippets: string[] = []
|
|
64
|
-
while ((match = snippetRegex.exec(html)) !== null) {
|
|
65
|
-
snippets.push(match[1].replace(/<[^>]+>/g, '').trim())
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const numResults = Math.min(input.num_results || 5, links.length)
|
|
69
|
-
for (let i = 0; i < numResults; i++) {
|
|
70
|
-
const link = links[i]
|
|
71
|
-
if (!link) continue
|
|
72
|
-
let entry = `${i + 1}. ${link.title}\n ${link.url}`
|
|
73
|
-
if (snippets[i]) {
|
|
74
|
-
entry += `\n ${snippets[i]}`
|
|
75
|
-
}
|
|
76
|
-
results.push(entry)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return results.length > 0
|
|
80
|
-
? results.join('\n\n')
|
|
81
|
-
: `No results found for "${query}"`
|
|
82
|
-
} catch (err: any) {
|
|
83
|
-
return { data: `Search error: ${err.message}`, is_error: true }
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
})
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git Worktree Tools
|
|
3
|
-
*
|
|
4
|
-
* EnterWorktree / ExitWorktree - Isolated git worktree environments
|
|
5
|
-
* for parallel work without affecting the main working tree.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { execSync } from 'child_process'
|
|
9
|
-
import { existsSync } from 'fs'
|
|
10
|
-
import { join } from 'path'
|
|
11
|
-
import type { ToolDefinition, ToolResult } from '../types.js'
|
|
12
|
-
|
|
13
|
-
// Track active worktrees
|
|
14
|
-
const activeWorktrees = new Map<string, { path: string; branch: string; originalCwd: string }>()
|
|
15
|
-
|
|
16
|
-
export const EnterWorktreeTool: ToolDefinition = {
|
|
17
|
-
name: 'EnterWorktree',
|
|
18
|
-
description: 'Create an isolated git worktree for parallel work. The agent will work in the worktree without affecting the main working tree.',
|
|
19
|
-
inputSchema: {
|
|
20
|
-
type: 'object',
|
|
21
|
-
properties: {
|
|
22
|
-
branch: { type: 'string', description: 'Branch name for the worktree (auto-generated if not provided)' },
|
|
23
|
-
path: { type: 'string', description: 'Path for the worktree (auto-generated if not provided)' },
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
isReadOnly: () => false,
|
|
27
|
-
isConcurrencySafe: () => false,
|
|
28
|
-
isEnabled: () => true,
|
|
29
|
-
async prompt() { return 'Create an isolated git worktree for parallel work.' },
|
|
30
|
-
async call(input: any, context: { cwd: string }): Promise<ToolResult> {
|
|
31
|
-
try {
|
|
32
|
-
// Check if we're in a git repo
|
|
33
|
-
execSync('git rev-parse --git-dir', { cwd: context.cwd, encoding: 'utf-8' })
|
|
34
|
-
|
|
35
|
-
const branch = input.branch || `worktree-${Date.now()}`
|
|
36
|
-
const worktreePath = input.path || join(context.cwd, '..', `.worktree-${branch}`)
|
|
37
|
-
|
|
38
|
-
// Create the branch if it doesn't exist
|
|
39
|
-
try {
|
|
40
|
-
execSync(`git branch ${branch}`, { cwd: context.cwd, encoding: 'utf-8', stdio: 'pipe' })
|
|
41
|
-
} catch {
|
|
42
|
-
// Branch might already exist
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Create worktree
|
|
46
|
-
execSync(`git worktree add ${JSON.stringify(worktreePath)} ${branch}`, {
|
|
47
|
-
cwd: context.cwd,
|
|
48
|
-
encoding: 'utf-8',
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
const id = crypto.randomUUID()
|
|
52
|
-
activeWorktrees.set(id, {
|
|
53
|
-
path: worktreePath,
|
|
54
|
-
branch,
|
|
55
|
-
originalCwd: context.cwd,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
type: 'tool_result',
|
|
60
|
-
tool_use_id: '',
|
|
61
|
-
content: `Worktree created:\n ID: ${id}\n Path: ${worktreePath}\n Branch: ${branch}\n\nYou are now working in the isolated worktree.`,
|
|
62
|
-
}
|
|
63
|
-
} catch (err: any) {
|
|
64
|
-
return {
|
|
65
|
-
type: 'tool_result',
|
|
66
|
-
tool_use_id: '',
|
|
67
|
-
content: `Error creating worktree: ${err.message}`,
|
|
68
|
-
is_error: true,
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const ExitWorktreeTool: ToolDefinition = {
|
|
75
|
-
name: 'ExitWorktree',
|
|
76
|
-
description: 'Exit and optionally remove a git worktree. Use "keep" to preserve changes or "remove" to clean up.',
|
|
77
|
-
inputSchema: {
|
|
78
|
-
type: 'object',
|
|
79
|
-
properties: {
|
|
80
|
-
id: { type: 'string', description: 'Worktree ID' },
|
|
81
|
-
action: {
|
|
82
|
-
type: 'string',
|
|
83
|
-
enum: ['keep', 'remove'],
|
|
84
|
-
description: 'Whether to keep or remove the worktree (default: remove)',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
required: ['id'],
|
|
88
|
-
},
|
|
89
|
-
isReadOnly: () => false,
|
|
90
|
-
isConcurrencySafe: () => false,
|
|
91
|
-
isEnabled: () => true,
|
|
92
|
-
async prompt() { return 'Exit a git worktree.' },
|
|
93
|
-
async call(input: any): Promise<ToolResult> {
|
|
94
|
-
const worktree = activeWorktrees.get(input.id)
|
|
95
|
-
if (!worktree) {
|
|
96
|
-
return {
|
|
97
|
-
type: 'tool_result',
|
|
98
|
-
tool_use_id: '',
|
|
99
|
-
content: `Worktree not found: ${input.id}`,
|
|
100
|
-
is_error: true,
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const action = input.action || 'remove'
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
if (action === 'remove') {
|
|
108
|
-
execSync(`git worktree remove ${JSON.stringify(worktree.path)} --force`, {
|
|
109
|
-
cwd: worktree.originalCwd,
|
|
110
|
-
encoding: 'utf-8',
|
|
111
|
-
})
|
|
112
|
-
// Clean up branch
|
|
113
|
-
try {
|
|
114
|
-
execSync(`git branch -D ${worktree.branch}`, {
|
|
115
|
-
cwd: worktree.originalCwd,
|
|
116
|
-
encoding: 'utf-8',
|
|
117
|
-
stdio: 'pipe',
|
|
118
|
-
})
|
|
119
|
-
} catch {
|
|
120
|
-
// Branch might have commits
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
activeWorktrees.delete(input.id)
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
type: 'tool_result',
|
|
128
|
-
tool_use_id: '',
|
|
129
|
-
content: `Worktree ${action === 'remove' ? 'removed' : 'kept'}: ${worktree.path}`,
|
|
130
|
-
}
|
|
131
|
-
} catch (err: any) {
|
|
132
|
-
return {
|
|
133
|
-
type: 'tool_result',
|
|
134
|
-
tool_use_id: '',
|
|
135
|
-
content: `Error: ${err.message}`,
|
|
136
|
-
is_error: true,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
}
|