anyclaude-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.
- package/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/agent.d.ts +110 -0
- package/dist/agent.js +897 -0
- package/dist/background/index.d.ts +3 -0
- package/dist/background/index.js +9 -0
- package/dist/background/manager.d.ts +32 -0
- package/dist/background/manager.js +108 -0
- package/dist/background/tools.d.ts +5 -0
- package/dist/background/tools.js +98 -0
- package/dist/background/worker.d.ts +19 -0
- package/dist/background/worker.js +30 -0
- package/dist/commands/builtins.d.ts +2 -0
- package/dist/commands/builtins.js +306 -0
- package/dist/commands/index.d.ts +21 -0
- package/dist/commands/index.js +56 -0
- package/dist/commands/types.d.ts +110 -0
- package/dist/commands/types.js +5 -0
- package/dist/compact.d.ts +22 -0
- package/dist/compact.js +67 -0
- package/dist/fs/dexie.d.ts +57 -0
- package/dist/fs/dexie.js +243 -0
- package/dist/fs/index.d.ts +4 -0
- package/dist/fs/index.js +13 -0
- package/dist/fs/linuxTree.d.ts +11 -0
- package/dist/fs/linuxTree.js +43 -0
- package/dist/fs/opfs.d.ts +23 -0
- package/dist/fs/opfs.js +112 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +29 -0
- package/dist/llm/anthropic.d.ts +24 -0
- package/dist/llm/anthropic.js +280 -0
- package/dist/llm/index.d.ts +3 -0
- package/dist/llm/index.js +3 -0
- package/dist/llm/inlineTools.d.ts +11 -0
- package/dist/llm/inlineTools.js +72 -0
- package/dist/llm/openai.d.ts +29 -0
- package/dist/llm/openai.js +224 -0
- package/dist/llm/responses.d.ts +18 -0
- package/dist/llm/responses.js +256 -0
- package/dist/mcp/client.d.ts +20 -0
- package/dist/mcp/client.js +156 -0
- package/dist/mcp/index.d.ts +24 -0
- package/dist/mcp/index.js +157 -0
- package/dist/mcp/proxy.d.ts +3 -0
- package/dist/mcp/proxy.js +25 -0
- package/dist/mcp/sdkServer.d.ts +21 -0
- package/dist/mcp/sdkServer.js +28 -0
- package/dist/mcp/types.d.ts +92 -0
- package/dist/mcp/types.js +5 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/render.d.ts +7 -0
- package/dist/memory/render.js +46 -0
- package/dist/memory/store.d.ts +20 -0
- package/dist/memory/store.js +79 -0
- package/dist/memory/tools.d.ts +5 -0
- package/dist/memory/tools.js +95 -0
- package/dist/memory/types.d.ts +15 -0
- package/dist/memory/types.js +4 -0
- package/dist/permissions/dangerous.d.ts +4 -0
- package/dist/permissions/dangerous.js +24 -0
- package/dist/permissions/gate.d.ts +21 -0
- package/dist/permissions/gate.js +66 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/match.d.ts +19 -0
- package/dist/permissions/match.js +104 -0
- package/dist/permissions/planMode.d.ts +3 -0
- package/dist/permissions/planMode.js +33 -0
- package/dist/permissions/types.d.ts +19 -0
- package/dist/permissions/types.js +2 -0
- package/dist/persist.d.ts +15 -0
- package/dist/persist.js +58 -0
- package/dist/prompt.d.ts +6 -0
- package/dist/prompt.js +34 -0
- package/dist/query.d.ts +105 -0
- package/dist/query.js +115 -0
- package/dist/queue.d.ts +23 -0
- package/dist/queue.js +43 -0
- package/dist/sandbox/cloudflare.d.ts +48 -0
- package/dist/sandbox/cloudflare.js +124 -0
- package/dist/sandbox/daytona.d.ts +48 -0
- package/dist/sandbox/daytona.js +79 -0
- package/dist/sandbox/e2b.d.ts +54 -0
- package/dist/sandbox/e2b.js +87 -0
- package/dist/sandbox/index.d.ts +8 -0
- package/dist/sandbox/index.js +19 -0
- package/dist/sandbox/local.d.ts +51 -0
- package/dist/sandbox/local.js +155 -0
- package/dist/sandbox/types.d.ts +18 -0
- package/dist/sandbox/types.js +27 -0
- package/dist/sandbox/util.d.ts +15 -0
- package/dist/sandbox/util.js +100 -0
- package/dist/sandbox/vercel.d.ts +48 -0
- package/dist/sandbox/vercel.js +130 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +6 -0
- package/dist/session/store.d.ts +28 -0
- package/dist/session/store.js +122 -0
- package/dist/session/types.d.ts +22 -0
- package/dist/session/types.js +2 -0
- package/dist/settings/index.d.ts +3 -0
- package/dist/settings/index.js +3 -0
- package/dist/settings/load.d.ts +20 -0
- package/dist/settings/load.js +36 -0
- package/dist/settings/merge.d.ts +13 -0
- package/dist/settings/merge.js +65 -0
- package/dist/settings/types.d.ts +17 -0
- package/dist/settings/types.js +3 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +5 -0
- package/dist/skills/load.d.ts +23 -0
- package/dist/skills/load.js +54 -0
- package/dist/skills/parse.d.ts +7 -0
- package/dist/skills/parse.js +40 -0
- package/dist/skills/tool.d.ts +2 -0
- package/dist/skills/tool.js +39 -0
- package/dist/skills/types.d.ts +10 -0
- package/dist/skills/types.js +4 -0
- package/dist/team/dispatch.d.ts +2 -0
- package/dist/team/dispatch.js +41 -0
- package/dist/team/index.d.ts +9 -0
- package/dist/team/index.js +11 -0
- package/dist/team/mailbox.d.ts +24 -0
- package/dist/team/mailbox.js +33 -0
- package/dist/team/prompt.d.ts +1 -0
- package/dist/team/prompt.js +12 -0
- package/dist/team/runner.d.ts +20 -0
- package/dist/team/runner.js +45 -0
- package/dist/team/taskBoard.d.ts +41 -0
- package/dist/team/taskBoard.js +73 -0
- package/dist/team/tools.d.ts +7 -0
- package/dist/team/tools.js +190 -0
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.js +45 -0
- package/dist/tools/config.d.ts +2 -0
- package/dist/tools/config.js +44 -0
- package/dist/tools/define.d.ts +18 -0
- package/dist/tools/define.js +21 -0
- package/dist/tools/delete_file.d.ts +2 -0
- package/dist/tools/delete_file.js +33 -0
- package/dist/tools/edit_file.d.ts +2 -0
- package/dist/tools/edit_file.js +93 -0
- package/dist/tools/fileTypes.d.ts +32 -0
- package/dist/tools/fileTypes.js +166 -0
- package/dist/tools/glob.d.ts +2 -0
- package/dist/tools/glob.js +53 -0
- package/dist/tools/grep.d.ts +2 -0
- package/dist/tools/grep.js +110 -0
- package/dist/tools/imageProcessor.d.ts +15 -0
- package/dist/tools/imageProcessor.js +83 -0
- package/dist/tools/index.d.ts +28 -0
- package/dist/tools/index.js +45 -0
- package/dist/tools/list_files.d.ts +2 -0
- package/dist/tools/list_files.js +42 -0
- package/dist/tools/multi_edit.d.ts +2 -0
- package/dist/tools/multi_edit.js +112 -0
- package/dist/tools/notebook_edit.d.ts +2 -0
- package/dist/tools/notebook_edit.js +118 -0
- package/dist/tools/plan_mode.d.ts +4 -0
- package/dist/tools/plan_mode.js +44 -0
- package/dist/tools/read_file.d.ts +2 -0
- package/dist/tools/read_file.js +193 -0
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +77 -0
- package/dist/tools/todo_write.d.ts +2 -0
- package/dist/tools/todo_write.js +104 -0
- package/dist/tools/tool_search.d.ts +2 -0
- package/dist/tools/tool_search.js +49 -0
- package/dist/tools/types.d.ts +82 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/walk.d.ts +29 -0
- package/dist/tools/walk.js +82 -0
- package/dist/tools/web_fetch.d.ts +2 -0
- package/dist/tools/web_fetch.js +76 -0
- package/dist/tools/web_search.d.ts +22 -0
- package/dist/tools/web_search.js +195 -0
- package/dist/tools/write_file.d.ts +2 -0
- package/dist/tools/write_file.js +39 -0
- package/dist/types/index.d.ts +363 -0
- package/dist/types/index.js +9 -0
- package/dist/util/ids.d.ts +3 -0
- package/dist/util/ids.js +22 -0
- package/dist/util/paths.d.ts +16 -0
- package/dist/util/paths.js +72 -0
- package/dist/util/pricing.d.ts +15 -0
- package/dist/util/pricing.js +81 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.js +2 -0
- package/dist/workspace/memory.d.ts +28 -0
- package/dist/workspace/memory.js +97 -0
- package/dist/workspace/webcontainer.d.ts +65 -0
- package/dist/workspace/webcontainer.js +156 -0
- package/package.json +78 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// In-memory task board shared across the coordinator and its workers. Tasks
|
|
2
|
+
// carry ownership, status, and dependency edges (blocks / blockedBy) so the
|
|
3
|
+
// coordinator can decompose work and gate execution on prerequisites.
|
|
4
|
+
export class TaskBoard {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.tasks = new Map();
|
|
7
|
+
this.counter = 0;
|
|
8
|
+
}
|
|
9
|
+
create(input) {
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
const id = `task_${++this.counter}`;
|
|
12
|
+
const task = {
|
|
13
|
+
id,
|
|
14
|
+
subject: input.subject,
|
|
15
|
+
description: input.description,
|
|
16
|
+
owner: input.owner,
|
|
17
|
+
status: 'pending',
|
|
18
|
+
blocks: [],
|
|
19
|
+
blockedBy: input.blockedBy ? [...input.blockedBy] : [],
|
|
20
|
+
createdAt: now,
|
|
21
|
+
updatedAt: now,
|
|
22
|
+
};
|
|
23
|
+
this.tasks.set(id, task);
|
|
24
|
+
// Maintain the reverse edge on prerequisites.
|
|
25
|
+
for (const dep of task.blockedBy) {
|
|
26
|
+
const d = this.tasks.get(dep);
|
|
27
|
+
if (d && !d.blocks.includes(id))
|
|
28
|
+
d.blocks.push(id);
|
|
29
|
+
}
|
|
30
|
+
return task;
|
|
31
|
+
}
|
|
32
|
+
update(id, patch) {
|
|
33
|
+
const t = this.tasks.get(id);
|
|
34
|
+
if (!t)
|
|
35
|
+
return null;
|
|
36
|
+
Object.assign(t, patch);
|
|
37
|
+
t.updatedAt = Date.now();
|
|
38
|
+
return t;
|
|
39
|
+
}
|
|
40
|
+
get(id) {
|
|
41
|
+
return this.tasks.get(id) ?? null;
|
|
42
|
+
}
|
|
43
|
+
list(filter) {
|
|
44
|
+
let out = [...this.tasks.values()];
|
|
45
|
+
if (filter?.status)
|
|
46
|
+
out = out.filter((t) => t.status === filter.status);
|
|
47
|
+
if (filter?.owner)
|
|
48
|
+
out = out.filter((t) => t.owner === filter.owner);
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/** Atomically claim an unowned/pending task. Null if already claimed. */
|
|
52
|
+
claim(id, owner) {
|
|
53
|
+
const t = this.tasks.get(id);
|
|
54
|
+
if (!t)
|
|
55
|
+
return null;
|
|
56
|
+
if (t.owner || t.status !== 'pending')
|
|
57
|
+
return null;
|
|
58
|
+
t.owner = owner;
|
|
59
|
+
t.status = 'in_progress';
|
|
60
|
+
t.updatedAt = Date.now();
|
|
61
|
+
return t;
|
|
62
|
+
}
|
|
63
|
+
/** True if any prerequisite is not yet completed. */
|
|
64
|
+
isBlocked(id) {
|
|
65
|
+
const t = this.tasks.get(id);
|
|
66
|
+
if (!t)
|
|
67
|
+
return false;
|
|
68
|
+
return t.blockedBy.some((dep) => {
|
|
69
|
+
const d = this.tasks.get(dep);
|
|
70
|
+
return !d || d.status !== 'completed';
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Tool } from '../tools/types.js';
|
|
2
|
+
export declare const sendMessage: Tool;
|
|
3
|
+
export declare const taskCreate: Tool;
|
|
4
|
+
export declare const taskUpdate: Tool;
|
|
5
|
+
export declare const taskGet: Tool;
|
|
6
|
+
export declare const boardList: Tool;
|
|
7
|
+
export declare const TEAM_TOOLS: Tool[];
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Coordinator/teammate tools: inter-agent messaging + task board CRUD. The
|
|
2
|
+
// Mailbox and TaskBoard live on the ToolContext (injected by the agent loop as
|
|
3
|
+
// `ctx.mailbox` / `ctx.board`); accessed via casts here so this file compiles
|
|
4
|
+
// standalone before the loop wiring lands.
|
|
5
|
+
function getMailbox(ctx) {
|
|
6
|
+
return ctx.mailbox;
|
|
7
|
+
}
|
|
8
|
+
function getBoard(ctx) {
|
|
9
|
+
return ctx.board;
|
|
10
|
+
}
|
|
11
|
+
function agentName(ctx) {
|
|
12
|
+
return ctx.agentName ?? 'coordinator';
|
|
13
|
+
}
|
|
14
|
+
const NOT_ENABLED = { content: 'Teammates are not enabled for this session.', isError: true };
|
|
15
|
+
function renderTask(t) {
|
|
16
|
+
const deps = t.blockedBy.length ? ` blockedBy=[${t.blockedBy.join(',')}]` : '';
|
|
17
|
+
return `${t.id} [${t.status}]${t.owner ? ` @${t.owner}` : ''} — ${t.subject}${deps}`;
|
|
18
|
+
}
|
|
19
|
+
export const sendMessage = {
|
|
20
|
+
def: {
|
|
21
|
+
type: 'function',
|
|
22
|
+
function: {
|
|
23
|
+
name: 'send_message',
|
|
24
|
+
description: 'Send a message to a teammate (by name) via the shared mailbox. Use to direct workers, hand off context, or coordinate.',
|
|
25
|
+
parameters: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
to: { type: 'string', description: 'Recipient teammate name.' },
|
|
29
|
+
text: { type: 'string', description: 'Message body.' },
|
|
30
|
+
},
|
|
31
|
+
required: ['to', 'text'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async run(input, ctx) {
|
|
36
|
+
const mailbox = getMailbox(ctx);
|
|
37
|
+
if (!mailbox)
|
|
38
|
+
return { ...NOT_ENABLED };
|
|
39
|
+
const to = String(input.to ?? '').trim();
|
|
40
|
+
const text = String(input.text ?? '');
|
|
41
|
+
if (!to || !text)
|
|
42
|
+
return { content: 'Error: `to` and `text` are required.', isError: true };
|
|
43
|
+
const id = mailbox.send(agentName(ctx), to, text);
|
|
44
|
+
return { content: `Message ${id} sent to ${to}.` };
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export const taskCreate = {
|
|
48
|
+
def: {
|
|
49
|
+
type: 'function',
|
|
50
|
+
function: {
|
|
51
|
+
name: 'task_create',
|
|
52
|
+
description: 'Create a task on the shared board. Set blocked_by to gate this task on prerequisites (their ids).',
|
|
53
|
+
parameters: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
subject: { type: 'string', description: 'Short task title.' },
|
|
57
|
+
description: { type: 'string', description: 'What needs to be done.' },
|
|
58
|
+
owner: { type: 'string', description: 'Assign to a teammate name.' },
|
|
59
|
+
blocked_by: {
|
|
60
|
+
type: 'array',
|
|
61
|
+
items: { type: 'string' },
|
|
62
|
+
description: 'Task ids that must complete first.',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ['subject'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
async run(input, ctx) {
|
|
70
|
+
const board = getBoard(ctx);
|
|
71
|
+
if (!board)
|
|
72
|
+
return { ...NOT_ENABLED };
|
|
73
|
+
const subject = String(input.subject ?? '').trim();
|
|
74
|
+
if (!subject)
|
|
75
|
+
return { content: 'Error: `subject` is required.', isError: true };
|
|
76
|
+
const blockedBy = Array.isArray(input.blocked_by)
|
|
77
|
+
? input.blocked_by.map(String)
|
|
78
|
+
: undefined;
|
|
79
|
+
const t = board.create({
|
|
80
|
+
subject,
|
|
81
|
+
description: input.description ? String(input.description) : undefined,
|
|
82
|
+
owner: input.owner ? String(input.owner) : undefined,
|
|
83
|
+
blockedBy,
|
|
84
|
+
});
|
|
85
|
+
return { content: `Created ${renderTask(t)}` };
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
export const taskUpdate = {
|
|
89
|
+
def: {
|
|
90
|
+
type: 'function',
|
|
91
|
+
function: {
|
|
92
|
+
name: 'task_update',
|
|
93
|
+
description: 'Update a board task — status, owner, subject, or description.',
|
|
94
|
+
parameters: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
id: { type: 'string', description: 'Task id.' },
|
|
98
|
+
status: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
enum: ['pending', 'in_progress', 'completed', 'failed'],
|
|
101
|
+
description: 'New status.',
|
|
102
|
+
},
|
|
103
|
+
owner: { type: 'string' },
|
|
104
|
+
subject: { type: 'string' },
|
|
105
|
+
description: { type: 'string' },
|
|
106
|
+
},
|
|
107
|
+
required: ['id'],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
async run(input, ctx) {
|
|
112
|
+
const board = getBoard(ctx);
|
|
113
|
+
if (!board)
|
|
114
|
+
return { ...NOT_ENABLED };
|
|
115
|
+
const id = String(input.id ?? '');
|
|
116
|
+
const patch = {};
|
|
117
|
+
if (input.status)
|
|
118
|
+
patch.status = input.status;
|
|
119
|
+
if (input.owner)
|
|
120
|
+
patch.owner = String(input.owner);
|
|
121
|
+
if (input.subject)
|
|
122
|
+
patch.subject = String(input.subject);
|
|
123
|
+
if (input.description)
|
|
124
|
+
patch.description = String(input.description);
|
|
125
|
+
const t = board.update(id, patch);
|
|
126
|
+
if (!t)
|
|
127
|
+
return { content: `No such task: ${id}`, isError: true };
|
|
128
|
+
return { content: `Updated ${renderTask(t)}` };
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
export const taskGet = {
|
|
132
|
+
def: {
|
|
133
|
+
type: 'function',
|
|
134
|
+
function: {
|
|
135
|
+
name: 'task_get',
|
|
136
|
+
description: 'Get one board task by id (full detail).',
|
|
137
|
+
parameters: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: { id: { type: 'string', description: 'Task id.' } },
|
|
140
|
+
required: ['id'],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
async run(input, ctx) {
|
|
145
|
+
const board = getBoard(ctx);
|
|
146
|
+
if (!board)
|
|
147
|
+
return { ...NOT_ENABLED };
|
|
148
|
+
const t = board.get(String(input.id ?? ''));
|
|
149
|
+
if (!t)
|
|
150
|
+
return { content: `No such task: ${input.id}`, isError: true };
|
|
151
|
+
const blocked = board.isBlocked(t.id) ? ' (blocked)' : '';
|
|
152
|
+
return {
|
|
153
|
+
content: `${renderTask(t)}${blocked}\n` +
|
|
154
|
+
(t.description ? `description: ${t.description}\n` : '') +
|
|
155
|
+
(t.blocks.length ? `blocks: ${t.blocks.join(', ')}` : ''),
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
export const boardList = {
|
|
160
|
+
def: {
|
|
161
|
+
type: 'function',
|
|
162
|
+
function: {
|
|
163
|
+
name: 'board_list',
|
|
164
|
+
description: 'List tasks on the shared board, optionally filtered by status or owner.',
|
|
165
|
+
parameters: {
|
|
166
|
+
type: 'object',
|
|
167
|
+
properties: {
|
|
168
|
+
status: {
|
|
169
|
+
type: 'string',
|
|
170
|
+
enum: ['pending', 'in_progress', 'completed', 'failed'],
|
|
171
|
+
},
|
|
172
|
+
owner: { type: 'string' },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
async run(input, ctx) {
|
|
178
|
+
const board = getBoard(ctx);
|
|
179
|
+
if (!board)
|
|
180
|
+
return { ...NOT_ENABLED };
|
|
181
|
+
const tasks = board.list({
|
|
182
|
+
status: input.status,
|
|
183
|
+
owner: input.owner ? String(input.owner) : undefined,
|
|
184
|
+
});
|
|
185
|
+
if (!tasks.length)
|
|
186
|
+
return { content: 'Board is empty.' };
|
|
187
|
+
return { content: tasks.map(renderTask).join('\n') };
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
export const TEAM_TOOLS = [sendMessage, taskCreate, taskUpdate, taskGet, boardList];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const DESCRIPTION = `Executes a shell command in the workspace and returns its combined output (stdout + stderr).
|
|
2
|
+
|
|
3
|
+
The shell depends on the workspace: a POSIX shell (sh/bash) on Linux/macOS or in WebContainer's jsh, and cmd.exe on Windows. Use commands appropriate to the host OS. The working directory persists between commands; shell state does not.
|
|
4
|
+
|
|
5
|
+
Prefer the dedicated tools (read_file, write_file, edit_file, glob, grep, list_files) over shell equivalents (cat, sed, find, grep, ls) for a better experience.`;
|
|
6
|
+
export const bash = {
|
|
7
|
+
def: {
|
|
8
|
+
type: 'function',
|
|
9
|
+
function: {
|
|
10
|
+
name: 'bash',
|
|
11
|
+
description: DESCRIPTION,
|
|
12
|
+
parameters: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
command: { type: 'string', description: 'The bash command to run.' },
|
|
16
|
+
timeout_ms: {
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'Optional timeout in milliseconds (default 120000).',
|
|
19
|
+
},
|
|
20
|
+
description: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Short human-readable description of what the command does.',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['command'],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
async run(input, ctx) {
|
|
30
|
+
const command = String(input.command ?? '').trim();
|
|
31
|
+
if (!command) {
|
|
32
|
+
return { content: 'Error: `command` is required.', isError: true };
|
|
33
|
+
}
|
|
34
|
+
const timeout = typeof input.timeout_ms === 'number' ? input.timeout_ms : undefined;
|
|
35
|
+
const { output, exitCode } = await ctx.exec.exec(command, timeout);
|
|
36
|
+
if (exitCode !== 0) {
|
|
37
|
+
const body = output || '(no output)';
|
|
38
|
+
return {
|
|
39
|
+
content: `Command exited with code ${exitCode}:\n${body}`,
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { content: output || '(command produced no output)' };
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const DESCRIPTION = `Read or write session configuration key/value pairs.
|
|
2
|
+
|
|
3
|
+
actions: "get" (requires key), "set" (requires key + value), "list". Config is session-scoped and visible to subsequent tool calls; use it to remember small preferences or flags during a task.`;
|
|
4
|
+
export const config = {
|
|
5
|
+
def: {
|
|
6
|
+
type: 'function',
|
|
7
|
+
function: {
|
|
8
|
+
name: 'config',
|
|
9
|
+
description: DESCRIPTION,
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
action: { type: 'string', enum: ['get', 'set', 'list'], description: 'Operation to perform.' },
|
|
14
|
+
key: { type: 'string', description: 'Config key (for get/set).' },
|
|
15
|
+
value: { description: 'Value to store (for set). Any JSON value.' },
|
|
16
|
+
},
|
|
17
|
+
required: ['action'],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
async run(input, ctx) {
|
|
22
|
+
if (!ctx.store)
|
|
23
|
+
return { content: 'Session store unavailable.', isError: true };
|
|
24
|
+
const cfg = (ctx.store.config ??= {});
|
|
25
|
+
const action = String(input.action ?? '');
|
|
26
|
+
const key = input.key != null ? String(input.key) : '';
|
|
27
|
+
if (action === 'list') {
|
|
28
|
+
const keys = Object.keys(cfg);
|
|
29
|
+
return { content: keys.length ? JSON.stringify(cfg, null, 2) : '(empty config)' };
|
|
30
|
+
}
|
|
31
|
+
if (action === 'get') {
|
|
32
|
+
if (!key)
|
|
33
|
+
return { content: 'Error: `key` is required for get.', isError: true };
|
|
34
|
+
return { content: key in cfg ? JSON.stringify(cfg[key]) : `(unset: ${key})` };
|
|
35
|
+
}
|
|
36
|
+
if (action === 'set') {
|
|
37
|
+
if (!key)
|
|
38
|
+
return { content: 'Error: `key` is required for set.', isError: true };
|
|
39
|
+
cfg[key] = input.value;
|
|
40
|
+
return { content: `Set ${key} = ${JSON.stringify(input.value)}` };
|
|
41
|
+
}
|
|
42
|
+
return { content: `Unknown action "${action}". Use get/set/list.`, isError: true };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Tool, ToolContext, ToolResult } from './types.js';
|
|
2
|
+
export interface DefineToolSpec {
|
|
3
|
+
/** Tool name the model calls (snake_case recommended). */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Natural-language description shown to the model. */
|
|
6
|
+
description: string;
|
|
7
|
+
/** Argument schema: JSON-Schema `properties` + optional `required`. Defaults to no args. */
|
|
8
|
+
parameters?: {
|
|
9
|
+
properties: Record<string, unknown>;
|
|
10
|
+
required?: string[];
|
|
11
|
+
};
|
|
12
|
+
/** Execution method. Receives the parsed input + the tool context (fs/exec/cwd/signal/…). */
|
|
13
|
+
run: (input: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult> | ToolResult;
|
|
14
|
+
/** Optional: spill threshold for large outputs (see Tool.maxResultChars). */
|
|
15
|
+
maxResultChars?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Build a `Tool` from a friendly spec. */
|
|
18
|
+
export declare function defineTool(spec: DefineToolSpec): Tool;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Build a `Tool` from a friendly spec. */
|
|
2
|
+
export function defineTool(spec) {
|
|
3
|
+
const tool = {
|
|
4
|
+
def: {
|
|
5
|
+
type: 'function',
|
|
6
|
+
function: {
|
|
7
|
+
name: spec.name,
|
|
8
|
+
description: spec.description,
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: spec.parameters?.properties ?? {},
|
|
12
|
+
...(spec.parameters?.required ? { required: spec.parameters.required } : {}),
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
run: async (input, ctx) => spec.run(input, ctx),
|
|
17
|
+
};
|
|
18
|
+
if (spec.maxResultChars !== undefined)
|
|
19
|
+
tool.maxResultChars = spec.maxResultChars;
|
|
20
|
+
return tool;
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const DESCRIPTION = `Deletes a file or directory from the workspace filesystem (recursive, force).`;
|
|
2
|
+
export const deleteFile = {
|
|
3
|
+
def: {
|
|
4
|
+
type: 'function',
|
|
5
|
+
function: {
|
|
6
|
+
name: 'delete_file',
|
|
7
|
+
description: DESCRIPTION,
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
path: { type: 'string', description: 'Path to delete.' },
|
|
12
|
+
},
|
|
13
|
+
required: ['path'],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
async run(input, ctx) {
|
|
18
|
+
const path = String(input.path ?? '');
|
|
19
|
+
if (!path)
|
|
20
|
+
return { content: 'Error: `path` is required.', isError: true };
|
|
21
|
+
try {
|
|
22
|
+
await ctx.fs.deleteFile(path);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return {
|
|
26
|
+
content: `Error deleting ${path}: ${err.message}`,
|
|
27
|
+
isError: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
ctx.readFiles.delete(path);
|
|
31
|
+
return { content: `Deleted ${path}` };
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const DESCRIPTION = `Performs an exact string replacement in a file.
|
|
2
|
+
|
|
3
|
+
- You must read the file with read_file at least once before editing it; otherwise this tool errors.
|
|
4
|
+
- \`old_string\` must match the file contents exactly (including whitespace) and must be unique unless \`replace_all\` is set.
|
|
5
|
+
- If \`old_string\` is not unique, the edit fails — provide more surrounding context, or set \`replace_all: true\` to replace every occurrence.`;
|
|
6
|
+
/** Count non-overlapping occurrences of needle in haystack. */
|
|
7
|
+
function countOccurrences(haystack, needle) {
|
|
8
|
+
if (needle === '')
|
|
9
|
+
return 0;
|
|
10
|
+
let count = 0;
|
|
11
|
+
let idx = haystack.indexOf(needle);
|
|
12
|
+
while (idx !== -1) {
|
|
13
|
+
count++;
|
|
14
|
+
idx = haystack.indexOf(needle, idx + needle.length);
|
|
15
|
+
}
|
|
16
|
+
return count;
|
|
17
|
+
}
|
|
18
|
+
export const editFile = {
|
|
19
|
+
def: {
|
|
20
|
+
type: 'function',
|
|
21
|
+
function: {
|
|
22
|
+
name: 'edit_file',
|
|
23
|
+
description: DESCRIPTION,
|
|
24
|
+
parameters: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
path: { type: 'string', description: 'Path to the file to edit.' },
|
|
28
|
+
old_string: { type: 'string', description: 'Exact text to find.' },
|
|
29
|
+
new_string: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'Replacement text (must differ from old_string).',
|
|
32
|
+
},
|
|
33
|
+
replace_all: {
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
description: 'Replace every occurrence instead of requiring uniqueness.',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
required: ['path', 'old_string', 'new_string'],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
async run(input, ctx) {
|
|
43
|
+
const path = String(input.path ?? '');
|
|
44
|
+
if (!path)
|
|
45
|
+
return { content: 'Error: `path` is required.', isError: true };
|
|
46
|
+
if (!ctx.readFiles.has(path)) {
|
|
47
|
+
return {
|
|
48
|
+
content: `Error: you must read ${path} with read_file before editing it.`,
|
|
49
|
+
isError: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const oldString = typeof input.old_string === 'string' ? input.old_string : '';
|
|
53
|
+
const newString = typeof input.new_string === 'string' ? input.new_string : '';
|
|
54
|
+
const replaceAll = input.replace_all === true;
|
|
55
|
+
if (oldString === newString) {
|
|
56
|
+
return {
|
|
57
|
+
content: 'Error: `old_string` and `new_string` are identical.',
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const text = await ctx.fs.readFile(path);
|
|
62
|
+
if (text === null) {
|
|
63
|
+
return { content: `Error: file not found: ${path}`, isError: true };
|
|
64
|
+
}
|
|
65
|
+
const occurrences = countOccurrences(text, oldString);
|
|
66
|
+
if (occurrences === 0) {
|
|
67
|
+
return {
|
|
68
|
+
content: `Error: \`old_string\` not found in ${path}.`,
|
|
69
|
+
isError: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (!replaceAll && occurrences > 1) {
|
|
73
|
+
return {
|
|
74
|
+
content: `Error: \`old_string\` is not unique in ${path} (found ${occurrences} occurrences). Provide more context or set replace_all: true.`,
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const updated = replaceAll
|
|
79
|
+
? text.split(oldString).join(newString)
|
|
80
|
+
: text.replace(oldString, newString);
|
|
81
|
+
try {
|
|
82
|
+
await ctx.fs.writeFile(path, updated);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return {
|
|
86
|
+
content: `Error writing ${path}: ${err.message}`,
|
|
87
|
+
isError: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const replaced = replaceAll ? occurrences : 1;
|
|
91
|
+
return { content: `Edited ${path} (${replaced} replacement${replaced === 1 ? '' : 's'}).` };
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { FileReadLimits } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Binary file extensions to skip for text-based reads. Ported from Claude
|
|
4
|
+
* Code's constants/files.ts. `.pdf` and image types are included here, but
|
|
5
|
+
* read_file dispatches those to dedicated handlers before the binary check.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BINARY_EXTENSIONS: Set<string>;
|
|
8
|
+
export declare const IMAGE_EXTENSIONS: Set<string>;
|
|
9
|
+
export declare const NOTEBOOK_EXTENSION = ".ipynb";
|
|
10
|
+
export declare const PDF_EXTENSION = ".pdf";
|
|
11
|
+
/** Default read caps (mirrors Claude Code: 256 KB / 25k tokens / 20 PDF pages). */
|
|
12
|
+
export declare const DEFAULT_FILE_READ_LIMITS: FileReadLimits;
|
|
13
|
+
/** Lowercased extension including the leading dot, or '' if none. */
|
|
14
|
+
export declare function extOf(path: string): string;
|
|
15
|
+
export declare function hasBinaryExtension(path: string): boolean;
|
|
16
|
+
export declare function isImageExtension(path: string): boolean;
|
|
17
|
+
export declare function isPdfExtension(path: string): boolean;
|
|
18
|
+
export declare function isNotebookExtension(path: string): boolean;
|
|
19
|
+
/** Map an image extension to its MIME type. */
|
|
20
|
+
export declare function imageMediaType(ext: string): string;
|
|
21
|
+
/** Detect image MIME type from magic bytes, or null if unrecognized. */
|
|
22
|
+
export declare function detectImageFormatFromBytes(bytes: Uint8Array): string | null;
|
|
23
|
+
/** Heuristic: a NUL byte in the first 8 KB means the file is binary. */
|
|
24
|
+
export declare function looksBinary(bytes: Uint8Array): boolean;
|
|
25
|
+
/** Rough token estimate (~4 chars/token). */
|
|
26
|
+
export declare function roughTokenCount(text: string): number;
|
|
27
|
+
/** Base64-encode bytes. Works in browser (btoa) and Node (Buffer). */
|
|
28
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
29
|
+
/** Base64-decode to bytes. Works in browser (atob) and Node (Buffer). */
|
|
30
|
+
export declare function base64ToBytes(b64: string): Uint8Array;
|
|
31
|
+
/** Human-readable byte size. */
|
|
32
|
+
export declare function formatBytes(n: number): string;
|