@shareai-lab/kode-sdk 1.0.0-beta.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/README.md +312 -0
- package/dist/core/agent.d.ts +85 -0
- package/dist/core/agent.js +687 -0
- package/dist/core/events.d.ts +19 -0
- package/dist/core/events.js +121 -0
- package/dist/core/hooks.d.ts +23 -0
- package/dist/core/hooks.js +71 -0
- package/dist/core/pool.d.ts +33 -0
- package/dist/core/pool.js +91 -0
- package/dist/core/room.d.ts +15 -0
- package/dist/core/room.js +57 -0
- package/dist/core/scheduler.d.ts +26 -0
- package/dist/core/scheduler.js +184 -0
- package/dist/core/types.d.ts +192 -0
- package/dist/core/types.js +13 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +57 -0
- package/dist/infra/provider.d.ts +58 -0
- package/dist/infra/provider.js +118 -0
- package/dist/infra/sandbox.d.ts +39 -0
- package/dist/infra/sandbox.js +77 -0
- package/dist/infra/store.d.ts +32 -0
- package/dist/infra/store.js +132 -0
- package/dist/tools/bash.d.ts +63 -0
- package/dist/tools/bash.js +99 -0
- package/dist/tools/builtin.d.ts +15 -0
- package/dist/tools/builtin.js +96 -0
- package/dist/tools/fs.d.ts +96 -0
- package/dist/tools/fs.js +96 -0
- package/dist/tools/task.d.ts +38 -0
- package/dist/tools/task.js +45 -0
- package/dist/utils/session-id.d.ts +21 -0
- package/dist/utils/session-id.js +64 -0
- package/package.json +47 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JSONStore = void 0;
|
|
4
|
+
class JSONStore {
|
|
5
|
+
constructor(baseDir) {
|
|
6
|
+
this.baseDir = baseDir;
|
|
7
|
+
}
|
|
8
|
+
getPath(sessionId, file) {
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const dir = path.join(this.baseDir, sessionId);
|
|
12
|
+
if (!fs.existsSync(dir)) {
|
|
13
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
return path.join(dir, file);
|
|
16
|
+
}
|
|
17
|
+
async saveMessages(sessionId, messages) {
|
|
18
|
+
const fs = require('fs').promises;
|
|
19
|
+
await fs.writeFile(this.getPath(sessionId, 'messages.json'), JSON.stringify(messages, null, 2));
|
|
20
|
+
}
|
|
21
|
+
async loadMessages(sessionId) {
|
|
22
|
+
const fs = require('fs').promises;
|
|
23
|
+
try {
|
|
24
|
+
const data = await fs.readFile(this.getPath(sessionId, 'messages.json'), 'utf-8');
|
|
25
|
+
return JSON.parse(data);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async appendEvent(sessionId, timeline) {
|
|
32
|
+
const fs = require('fs').promises;
|
|
33
|
+
const path = this.getPath(sessionId, 'events.jsonl');
|
|
34
|
+
await fs.appendFile(path, JSON.stringify(timeline) + '\n');
|
|
35
|
+
}
|
|
36
|
+
async *readEvents(sessionId, since) {
|
|
37
|
+
const fs = require('fs').promises;
|
|
38
|
+
try {
|
|
39
|
+
const data = await fs.readFile(this.getPath(sessionId, 'events.jsonl'), 'utf-8');
|
|
40
|
+
const events = data
|
|
41
|
+
.trim()
|
|
42
|
+
.split('\n')
|
|
43
|
+
.filter((line) => line)
|
|
44
|
+
.map((line) => JSON.parse(line));
|
|
45
|
+
const filtered = since !== undefined ? events.filter((e) => e.cursor >= since) : events;
|
|
46
|
+
for (const event of filtered) {
|
|
47
|
+
yield event;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// No events file, return empty
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async saveSnapshot(sessionId, snapshot) {
|
|
56
|
+
const fs = require('fs').promises;
|
|
57
|
+
const path = this.getPath(sessionId, `snapshot-${snapshot.id}.json`);
|
|
58
|
+
await fs.writeFile(path, JSON.stringify(snapshot, null, 2));
|
|
59
|
+
}
|
|
60
|
+
async loadSnapshot(sessionId, snapshotId) {
|
|
61
|
+
const fs = require('fs').promises;
|
|
62
|
+
try {
|
|
63
|
+
const data = await fs.readFile(this.getPath(sessionId, `snapshot-${snapshotId}.json`), 'utf-8');
|
|
64
|
+
return JSON.parse(data);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async listSnapshots(sessionId) {
|
|
71
|
+
const fs = require('fs').promises;
|
|
72
|
+
const path = require('path');
|
|
73
|
+
try {
|
|
74
|
+
const dir = path.join(this.baseDir, sessionId);
|
|
75
|
+
const files = await fs.readdir(dir);
|
|
76
|
+
const snapshots = [];
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
if (file.startsWith('snapshot-') && file.endsWith('.json')) {
|
|
79
|
+
const data = await fs.readFile(path.join(dir, file), 'utf-8');
|
|
80
|
+
snapshots.push(JSON.parse(data));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return snapshots;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async saveInfo(sessionId, info) {
|
|
90
|
+
const fs = require('fs').promises;
|
|
91
|
+
await fs.writeFile(this.getPath(sessionId, 'info.json'), JSON.stringify(info, null, 2));
|
|
92
|
+
}
|
|
93
|
+
async loadInfo(sessionId) {
|
|
94
|
+
const fs = require('fs').promises;
|
|
95
|
+
try {
|
|
96
|
+
const data = await fs.readFile(this.getPath(sessionId, 'info.json'), 'utf-8');
|
|
97
|
+
return JSON.parse(data);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async exists(sessionId) {
|
|
104
|
+
const fs = require('fs').promises;
|
|
105
|
+
const path = require('path');
|
|
106
|
+
try {
|
|
107
|
+
await fs.access(path.join(this.baseDir, sessionId));
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async delete(sessionId) {
|
|
115
|
+
const fs = require('fs').promises;
|
|
116
|
+
const path = require('path');
|
|
117
|
+
const dir = path.join(this.baseDir, sessionId);
|
|
118
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
async list(prefix) {
|
|
121
|
+
const fs = require('fs').promises;
|
|
122
|
+
const path = require('path');
|
|
123
|
+
try {
|
|
124
|
+
const dirs = await fs.readdir(this.baseDir);
|
|
125
|
+
return prefix ? dirs.filter((d) => d.startsWith(prefix)) : dirs;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.JSONStore = JSONStore;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ToolContext } from '../core/types';
|
|
2
|
+
import { Tool } from './fs';
|
|
3
|
+
export declare class BashRun implements Tool {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
input_schema: {
|
|
7
|
+
type: string;
|
|
8
|
+
properties: {
|
|
9
|
+
cmd: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
timeout_ms: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
background: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
required: string[];
|
|
23
|
+
};
|
|
24
|
+
exec(args: {
|
|
25
|
+
cmd: string;
|
|
26
|
+
timeout_ms?: number;
|
|
27
|
+
background?: boolean;
|
|
28
|
+
}, ctx: ToolContext): Promise<any>;
|
|
29
|
+
}
|
|
30
|
+
export declare class BashLogs implements Tool {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
input_schema: {
|
|
34
|
+
type: string;
|
|
35
|
+
properties: {
|
|
36
|
+
shell_id: {
|
|
37
|
+
type: string;
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
required: string[];
|
|
42
|
+
};
|
|
43
|
+
exec(args: {
|
|
44
|
+
shell_id: string;
|
|
45
|
+
}, ctx: ToolContext): Promise<any>;
|
|
46
|
+
}
|
|
47
|
+
export declare class BashKill implements Tool {
|
|
48
|
+
name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
input_schema: {
|
|
51
|
+
type: string;
|
|
52
|
+
properties: {
|
|
53
|
+
shell_id: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
required: string[];
|
|
59
|
+
};
|
|
60
|
+
exec(args: {
|
|
61
|
+
shell_id: string;
|
|
62
|
+
}, ctx: ToolContext): Promise<any>;
|
|
63
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BashKill = exports.BashLogs = exports.BashRun = void 0;
|
|
4
|
+
const processes = new Map();
|
|
5
|
+
class BashRun {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = 'bash_run';
|
|
8
|
+
this.description = 'Execute a bash command';
|
|
9
|
+
this.input_schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
cmd: { type: 'string', description: 'Command to execute' },
|
|
13
|
+
timeout_ms: { type: 'number', description: 'Timeout in milliseconds (default: 120000)' },
|
|
14
|
+
background: { type: 'boolean', description: 'Run in background and return shell_id' },
|
|
15
|
+
},
|
|
16
|
+
required: ['cmd'],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async exec(args, ctx) {
|
|
20
|
+
if (args.background) {
|
|
21
|
+
const id = `shell-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
22
|
+
const promise = ctx.sandbox.exec(args.cmd, { timeoutMs: args.timeout_ms });
|
|
23
|
+
const proc = {
|
|
24
|
+
id,
|
|
25
|
+
cmd: args.cmd,
|
|
26
|
+
startTime: Date.now(),
|
|
27
|
+
promise,
|
|
28
|
+
stdout: '',
|
|
29
|
+
stderr: '',
|
|
30
|
+
};
|
|
31
|
+
processes.set(id, proc);
|
|
32
|
+
promise.then((result) => {
|
|
33
|
+
proc.code = result.code;
|
|
34
|
+
proc.stdout = result.stdout;
|
|
35
|
+
proc.stderr = result.stderr;
|
|
36
|
+
});
|
|
37
|
+
return { shell_id: id, status: 'running' };
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const result = await ctx.sandbox.exec(args.cmd, { timeoutMs: args.timeout_ms });
|
|
41
|
+
return {
|
|
42
|
+
code: result.code,
|
|
43
|
+
stdout: result.stdout,
|
|
44
|
+
stderr: result.stderr,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.BashRun = BashRun;
|
|
50
|
+
class BashLogs {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.name = 'bash_logs';
|
|
53
|
+
this.description = 'Get output from a background bash shell';
|
|
54
|
+
this.input_schema = {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
shell_id: { type: 'string', description: 'Shell ID from bash_run' },
|
|
58
|
+
},
|
|
59
|
+
required: ['shell_id'],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async exec(args, ctx) {
|
|
63
|
+
const proc = processes.get(args.shell_id);
|
|
64
|
+
if (!proc) {
|
|
65
|
+
throw new Error(`Shell not found: ${args.shell_id}`);
|
|
66
|
+
}
|
|
67
|
+
const isRunning = proc.code === undefined;
|
|
68
|
+
return {
|
|
69
|
+
shell_id: args.shell_id,
|
|
70
|
+
status: isRunning ? 'running' : 'completed',
|
|
71
|
+
code: proc.code,
|
|
72
|
+
stdout: proc.stdout,
|
|
73
|
+
stderr: proc.stderr,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.BashLogs = BashLogs;
|
|
78
|
+
class BashKill {
|
|
79
|
+
constructor() {
|
|
80
|
+
this.name = 'bash_kill';
|
|
81
|
+
this.description = 'Kill a background bash shell';
|
|
82
|
+
this.input_schema = {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
shell_id: { type: 'string', description: 'Shell ID from Bash.Run' },
|
|
86
|
+
},
|
|
87
|
+
required: ['shell_id'],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async exec(args, ctx) {
|
|
91
|
+
const proc = processes.get(args.shell_id);
|
|
92
|
+
if (!proc) {
|
|
93
|
+
throw new Error(`Shell not found: ${args.shell_id}`);
|
|
94
|
+
}
|
|
95
|
+
processes.delete(args.shell_id);
|
|
96
|
+
return { shell_id: args.shell_id, status: 'killed' };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.BashKill = BashKill;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Tool } from './fs';
|
|
2
|
+
import { AgentTemplate } from './task';
|
|
3
|
+
export declare const builtin: {
|
|
4
|
+
fs(opts?: {
|
|
5
|
+
workDir?: string;
|
|
6
|
+
}): Tool[];
|
|
7
|
+
bash(opts?: {
|
|
8
|
+
allow?: RegExp[];
|
|
9
|
+
block?: RegExp[];
|
|
10
|
+
approval?: boolean;
|
|
11
|
+
}): Tool[];
|
|
12
|
+
task(opts?: {
|
|
13
|
+
subAgents?: AgentTemplate[];
|
|
14
|
+
}): Tool;
|
|
15
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.builtin = void 0;
|
|
4
|
+
const fs_1 = require("./fs");
|
|
5
|
+
const bash_1 = require("./bash");
|
|
6
|
+
const task_1 = require("./task");
|
|
7
|
+
exports.builtin = {
|
|
8
|
+
fs(opts) {
|
|
9
|
+
return [new fs_1.FsRead(), new fs_1.FsWrite(), new fs_1.FsEdit()].map((tool) => {
|
|
10
|
+
if (opts?.workDir) {
|
|
11
|
+
return (0, fs_1.toolTune)(tool, {
|
|
12
|
+
preToolUse(call, ctx) {
|
|
13
|
+
if ('file' in call.args) {
|
|
14
|
+
call.args.file = ctx.sandbox.fs.resolve(call.args.file);
|
|
15
|
+
if (!ctx.sandbox.fs.isInside(call.args.file)) {
|
|
16
|
+
return { decision: 'deny', reason: `Path outside sandbox: ${call.args.file}` };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
postToolUse(outcome, ctx) {
|
|
21
|
+
const content = String(outcome.content || '');
|
|
22
|
+
if (content.length > 100000) {
|
|
23
|
+
const tempPath = ctx.sandbox.fs.temp(`tool-${outcome.id}.log`);
|
|
24
|
+
ctx.sandbox.fs.write(tempPath, content);
|
|
25
|
+
return {
|
|
26
|
+
update: {
|
|
27
|
+
content: content.slice(0, 100000) + `\n\n[Full output at ./${tempPath}]`,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return tool;
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
bash(opts) {
|
|
38
|
+
const tools = [new bash_1.BashRun(), new bash_1.BashLogs(), new bash_1.BashKill()];
|
|
39
|
+
if (opts?.allow || opts?.block || opts?.approval) {
|
|
40
|
+
return tools.map((tool) => {
|
|
41
|
+
if (tool.name === 'bash_run') {
|
|
42
|
+
return (0, fs_1.toolTune)(tool, {
|
|
43
|
+
preToolUse(call, ctx) {
|
|
44
|
+
const cmd = call.args.cmd;
|
|
45
|
+
if (opts.block) {
|
|
46
|
+
for (const pattern of opts.block) {
|
|
47
|
+
if (pattern.test(cmd)) {
|
|
48
|
+
return { decision: 'deny', reason: `Blocked command pattern: ${pattern}` };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (opts.allow) {
|
|
53
|
+
let allowed = false;
|
|
54
|
+
for (const pattern of opts.allow) {
|
|
55
|
+
if (pattern.test(cmd)) {
|
|
56
|
+
allowed = true;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!allowed) {
|
|
61
|
+
return { decision: 'deny', reason: 'Command not in allow list' };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (opts.approval) {
|
|
65
|
+
return { decision: 'ask', meta: { title: 'Bash Command Approval', cmd } };
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
postToolUse(outcome, ctx) {
|
|
69
|
+
const stdout = String(outcome.content?.stdout || '');
|
|
70
|
+
const stderr = String(outcome.content?.stderr || '');
|
|
71
|
+
const combined = stdout + stderr;
|
|
72
|
+
if (combined.length > 100000) {
|
|
73
|
+
const tempPath = ctx.sandbox.fs.temp(`bash-${outcome.id}.log`);
|
|
74
|
+
ctx.sandbox.fs.write(tempPath, combined);
|
|
75
|
+
return {
|
|
76
|
+
update: {
|
|
77
|
+
content: {
|
|
78
|
+
...outcome.content,
|
|
79
|
+
stdout: stdout.slice(0, 50000) + `\n\n[Full stdout at ./${tempPath}]`,
|
|
80
|
+
stderr: stderr.slice(0, 50000),
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return tool;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return tools;
|
|
92
|
+
},
|
|
93
|
+
task(opts) {
|
|
94
|
+
return new task_1.TaskRun(opts?.subAgents || []);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ToolContext, ToolOutcome, HookDecision, PostHookResult } from '../core/types';
|
|
2
|
+
import { Hooks } from '../core/hooks';
|
|
3
|
+
export interface Tool {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
input_schema: any;
|
|
7
|
+
exec(args: any, ctx: ToolContext): Promise<any>;
|
|
8
|
+
hooks?: Hooks;
|
|
9
|
+
permissionDetails?: (call: any, ctx: ToolContext) => any;
|
|
10
|
+
}
|
|
11
|
+
export declare class FsRead implements Tool {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
input_schema: {
|
|
15
|
+
type: string;
|
|
16
|
+
properties: {
|
|
17
|
+
file: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
offset: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
limit: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
required: string[];
|
|
31
|
+
};
|
|
32
|
+
exec(args: {
|
|
33
|
+
file: string;
|
|
34
|
+
offset?: number;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}, ctx: ToolContext): Promise<any>;
|
|
37
|
+
}
|
|
38
|
+
export declare class FsWrite implements Tool {
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
input_schema: {
|
|
42
|
+
type: string;
|
|
43
|
+
properties: {
|
|
44
|
+
file: {
|
|
45
|
+
type: string;
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
content: {
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
required: string[];
|
|
54
|
+
};
|
|
55
|
+
exec(args: {
|
|
56
|
+
file: string;
|
|
57
|
+
content: string;
|
|
58
|
+
}, ctx: ToolContext): Promise<any>;
|
|
59
|
+
}
|
|
60
|
+
export declare class FsEdit implements Tool {
|
|
61
|
+
name: string;
|
|
62
|
+
description: string;
|
|
63
|
+
input_schema: {
|
|
64
|
+
type: string;
|
|
65
|
+
properties: {
|
|
66
|
+
file: {
|
|
67
|
+
type: string;
|
|
68
|
+
description: string;
|
|
69
|
+
};
|
|
70
|
+
old_string: {
|
|
71
|
+
type: string;
|
|
72
|
+
description: string;
|
|
73
|
+
};
|
|
74
|
+
new_string: {
|
|
75
|
+
type: string;
|
|
76
|
+
description: string;
|
|
77
|
+
};
|
|
78
|
+
replace_all: {
|
|
79
|
+
type: string;
|
|
80
|
+
description: string;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
required: string[];
|
|
84
|
+
};
|
|
85
|
+
exec(args: {
|
|
86
|
+
file: string;
|
|
87
|
+
old_string: string;
|
|
88
|
+
new_string: string;
|
|
89
|
+
replace_all?: boolean;
|
|
90
|
+
}, ctx: ToolContext): Promise<any>;
|
|
91
|
+
}
|
|
92
|
+
export declare function toolTune(tool: Tool, hooks: {
|
|
93
|
+
preToolUse?: (call: any, ctx: ToolContext) => HookDecision | Promise<HookDecision>;
|
|
94
|
+
postToolUse?: (outcome: ToolOutcome, ctx: ToolContext) => PostHookResult | Promise<PostHookResult>;
|
|
95
|
+
permissionDetails?: (call: any, ctx: ToolContext) => any;
|
|
96
|
+
}): Tool;
|
package/dist/tools/fs.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FsEdit = exports.FsWrite = exports.FsRead = void 0;
|
|
4
|
+
exports.toolTune = toolTune;
|
|
5
|
+
class FsRead {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = 'fs_read';
|
|
8
|
+
this.description = 'Read contents from a file';
|
|
9
|
+
this.input_schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
file: { type: 'string', description: 'Path to file' },
|
|
13
|
+
offset: { type: 'number', description: 'Line offset (optional)' },
|
|
14
|
+
limit: { type: 'number', description: 'Max lines to read (optional)' },
|
|
15
|
+
},
|
|
16
|
+
required: ['file'],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async exec(args, ctx) {
|
|
20
|
+
const content = await ctx.sandbox.fs.read(args.file);
|
|
21
|
+
const lines = content.split('\n');
|
|
22
|
+
const offset = args.offset || 0;
|
|
23
|
+
const limit = args.limit || lines.length;
|
|
24
|
+
const selected = lines.slice(offset, offset + limit);
|
|
25
|
+
return selected.join('\n');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.FsRead = FsRead;
|
|
29
|
+
class FsWrite {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.name = 'fs_write';
|
|
32
|
+
this.description = 'Write contents to a file (creates or overwrites)';
|
|
33
|
+
this.input_schema = {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
file: { type: 'string', description: 'Path to file' },
|
|
37
|
+
content: { type: 'string', description: 'Content to write' },
|
|
38
|
+
},
|
|
39
|
+
required: ['file', 'content'],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async exec(args, ctx) {
|
|
43
|
+
await ctx.sandbox.fs.write(args.file, args.content);
|
|
44
|
+
return { success: true, file: args.file };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.FsWrite = FsWrite;
|
|
48
|
+
class FsEdit {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.name = 'fs_edit';
|
|
51
|
+
this.description = 'Edit a file by replacing old_string with new_string';
|
|
52
|
+
this.input_schema = {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
file: { type: 'string', description: 'Path to file' },
|
|
56
|
+
old_string: { type: 'string', description: 'String to replace' },
|
|
57
|
+
new_string: { type: 'string', description: 'Replacement string' },
|
|
58
|
+
replace_all: { type: 'boolean', description: 'Replace all occurrences (default: false)' },
|
|
59
|
+
},
|
|
60
|
+
required: ['file', 'old_string', 'new_string'],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async exec(args, ctx) {
|
|
64
|
+
const content = await ctx.sandbox.fs.read(args.file);
|
|
65
|
+
if (args.replace_all) {
|
|
66
|
+
const updated = content.split(args.old_string).join(args.new_string);
|
|
67
|
+
await ctx.sandbox.fs.write(args.file, updated);
|
|
68
|
+
const count = content.split(args.old_string).length - 1;
|
|
69
|
+
return { success: true, file: args.file, replacements: count };
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const occurrences = content.split(args.old_string).length - 1;
|
|
73
|
+
if (occurrences === 0) {
|
|
74
|
+
throw new Error(`old_string not found in ${args.file}`);
|
|
75
|
+
}
|
|
76
|
+
if (occurrences > 1) {
|
|
77
|
+
throw new Error(`old_string appears ${occurrences} times; use replace_all=true or provide unique string`);
|
|
78
|
+
}
|
|
79
|
+
const updated = content.replace(args.old_string, args.new_string);
|
|
80
|
+
await ctx.sandbox.fs.write(args.file, updated);
|
|
81
|
+
return { success: true, file: args.file, replacements: 1 };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.FsEdit = FsEdit;
|
|
86
|
+
function toolTune(tool, hooks) {
|
|
87
|
+
return {
|
|
88
|
+
...tool,
|
|
89
|
+
hooks: {
|
|
90
|
+
...tool.hooks,
|
|
91
|
+
preToolUse: hooks.preToolUse,
|
|
92
|
+
postToolUse: hooks.postToolUse,
|
|
93
|
+
},
|
|
94
|
+
permissionDetails: hooks.permissionDetails || tool.permissionDetails,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ToolContext } from '../core/types';
|
|
2
|
+
import { Tool } from './fs';
|
|
3
|
+
export interface AgentTemplate {
|
|
4
|
+
id: string;
|
|
5
|
+
system?: string;
|
|
6
|
+
tools?: Tool[];
|
|
7
|
+
whenToUse?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class TaskRun implements Tool {
|
|
10
|
+
private subAgents;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
input_schema: {
|
|
14
|
+
type: string;
|
|
15
|
+
properties: {
|
|
16
|
+
task: {
|
|
17
|
+
type: string;
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
20
|
+
with: {
|
|
21
|
+
type: string;
|
|
22
|
+
description: string;
|
|
23
|
+
};
|
|
24
|
+
context: {
|
|
25
|
+
type: string;
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
required: string[];
|
|
30
|
+
};
|
|
31
|
+
constructor(subAgents: AgentTemplate[]);
|
|
32
|
+
exec(args: {
|
|
33
|
+
task: string;
|
|
34
|
+
with?: string;
|
|
35
|
+
context?: string;
|
|
36
|
+
}, ctx: ToolContext): Promise<any>;
|
|
37
|
+
private selectTemplate;
|
|
38
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskRun = void 0;
|
|
4
|
+
class TaskRun {
|
|
5
|
+
constructor(subAgents) {
|
|
6
|
+
this.subAgents = subAgents;
|
|
7
|
+
this.name = 'task_run';
|
|
8
|
+
this.description = 'Delegate a task to a sub-agent';
|
|
9
|
+
this.input_schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
task: { type: 'string', description: 'Task description for the sub-agent' },
|
|
13
|
+
with: { type: 'string', description: 'Template ID to use (optional, will auto-select if omitted)' },
|
|
14
|
+
context: { type: 'string', description: 'Additional context (optional)' },
|
|
15
|
+
},
|
|
16
|
+
required: ['task'],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async exec(args, ctx) {
|
|
20
|
+
// This will be implemented when Agent class is ready
|
|
21
|
+
// For now, return a placeholder
|
|
22
|
+
const template = args.with
|
|
23
|
+
? this.subAgents.find((t) => t.id === args.with)
|
|
24
|
+
: this.selectTemplate(args.task);
|
|
25
|
+
if (!template) {
|
|
26
|
+
throw new Error(`Template not found: ${args.with || 'auto'}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
status: 'delegated',
|
|
30
|
+
template: template.id,
|
|
31
|
+
task: args.task,
|
|
32
|
+
note: 'Sub-agent execution will be implemented in Agent class',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
selectTemplate(task) {
|
|
36
|
+
// Simple heuristic selection based on whenToUse hints
|
|
37
|
+
for (const template of this.subAgents) {
|
|
38
|
+
if (template.whenToUse && task.toLowerCase().includes(template.whenToUse.toLowerCase())) {
|
|
39
|
+
return template;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return this.subAgents[0];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.TaskRun = TaskRun;
|