@robzilla1738/agentswarm 0.2.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 +142 -0
- package/bin/swarm.js +10 -0
- package/dist/agent.js +211 -0
- package/dist/cli.js +667 -0
- package/dist/config.js +289 -0
- package/dist/control.js +96 -0
- package/dist/deepseek.js +321 -0
- package/dist/executor.js +988 -0
- package/dist/hub.js +553 -0
- package/dist/journal.js +152 -0
- package/dist/prompts.js +232 -0
- package/dist/providers.js +151 -0
- package/dist/run.js +309 -0
- package/dist/sandbox.js +505 -0
- package/dist/state.js +230 -0
- package/dist/terminal.js +298 -0
- package/dist/tools.js +491 -0
- package/dist/types.js +26 -0
- package/dist/util.js +209 -0
- package/dist/webtools.js +205 -0
- package/package.json +63 -0
- package/ui/out/404/index.html +1 -0
- package/ui/out/404.html +1 -0
- package/ui/out/_next/static/chunks/255-2aa030c9ba2867e3.js +1 -0
- package/ui/out/_next/static/chunks/383-289a866b246b41cc.js +1 -0
- package/ui/out/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- package/ui/out/_next/static/chunks/619-ba102abea3e3d0e4.js +1 -0
- package/ui/out/_next/static/chunks/677-b37981ba0eca75b2.js +1 -0
- package/ui/out/_next/static/chunks/app/_not-found/page-2d0982e372f7be41.js +1 -0
- package/ui/out/_next/static/chunks/app/layout-37ad32c5fdb26f29.js +1 -0
- package/ui/out/_next/static/chunks/app/page-0c9f35bd4aa8e370.js +1 -0
- package/ui/out/_next/static/chunks/app/run/page-13dc41a57e34da71.js +1 -0
- package/ui/out/_next/static/chunks/app/settings/page-a1763be7f6de888c.js +1 -0
- package/ui/out/_next/static/chunks/framework-2c534e0e662575a2.js +1 -0
- package/ui/out/_next/static/chunks/main-app-889ed884f8bc78e3.js +1 -0
- package/ui/out/_next/static/chunks/main-eb90ae3b35d2fd16.js +1 -0
- package/ui/out/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/ui/out/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/ui/out/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/ui/out/_next/static/chunks/webpack-38639c05c96dbeca.js +1 -0
- package/ui/out/_next/static/css/82edaa7a5942f894.css +3 -0
- package/ui/out/_next/static/eiQeDU9uBHNsBj0CFkp8M/_buildManifest.js +1 -0
- package/ui/out/_next/static/eiQeDU9uBHNsBj0CFkp8M/_ssgManifest.js +1 -0
- package/ui/out/_next/static/media/0aa834ed78bf6d07-s.woff2 +0 -0
- package/ui/out/_next/static/media/438aa629764e75f3-s.woff2 +0 -0
- package/ui/out/_next/static/media/4c9affa5bc8f420e-s.p.woff2 +0 -0
- package/ui/out/_next/static/media/51251f8b9793cdb3-s.woff2 +0 -0
- package/ui/out/_next/static/media/67957d42bae0796d-s.woff2 +0 -0
- package/ui/out/_next/static/media/875ae681bfde4580-s.woff2 +0 -0
- package/ui/out/_next/static/media/886030b0b59bc5a7-s.woff2 +0 -0
- package/ui/out/_next/static/media/939c4f875ee75fbb-s.woff2 +0 -0
- package/ui/out/_next/static/media/bb3ef058b751a6ad-s.p.woff2 +0 -0
- package/ui/out/_next/static/media/cc978ac5ee68c2b6-s.woff2 +0 -0
- package/ui/out/_next/static/media/e857b654a2caa584-s.woff2 +0 -0
- package/ui/out/_next/static/media/f911b923c6adde36-s.woff2 +0 -0
- package/ui/out/icon.png +0 -0
- package/ui/out/index.html +1 -0
- package/ui/out/index.txt +22 -0
- package/ui/out/run/index.html +1 -0
- package/ui/out/run/index.txt +22 -0
- package/ui/out/settings/index.html +1 -0
- package/ui/out/settings/index.txt +22 -0
- package/ui/out/swarm-mark.png +0 -0
package/dist/tools.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FINISH_TOOL = exports.WAIT_TOOL = exports.SPAWN_TASKS_TOOL = exports.SUBMIT_FINAL_TOOL = exports.VERDICT_TOOL = exports.REPORT_TOOL = void 0;
|
|
37
|
+
exports.workerToolset = workerToolset;
|
|
38
|
+
exports.verifierToolset = verifierToolset;
|
|
39
|
+
exports.synthToolset = synthToolset;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const util_1 = require("./util");
|
|
43
|
+
const webtools_1 = require("./webtools");
|
|
44
|
+
// ---------- safety ----------
|
|
45
|
+
const DANGEROUS = [
|
|
46
|
+
{ re: /\bsudo\b/, why: "sudo is not allowed" },
|
|
47
|
+
{ re: /\brm\s+(-[a-zA-Z]+\s+)*(\/|~)(\s|$|\/\*)/, why: "refusing to rm at filesystem root or home" },
|
|
48
|
+
{ re: /\b(shutdown|reboot|halt)\b/, why: "system power commands are not allowed" },
|
|
49
|
+
{ re: /\bmkfs\b|\bdiskutil\s+erase/i, why: "disk formatting is not allowed" },
|
|
50
|
+
{ re: /\bdd\s+[^|]*of=\/dev\//, why: "writing to raw devices is not allowed" },
|
|
51
|
+
{ re: /:\(\)\s*\{\s*:\|\s*:&\s*\}\s*;/, why: "fork bomb" },
|
|
52
|
+
];
|
|
53
|
+
function checkCommand(cmd, cfg) {
|
|
54
|
+
if (!cfg.safeMode)
|
|
55
|
+
return;
|
|
56
|
+
for (const d of DANGEROUS) {
|
|
57
|
+
if (d.re.test(cmd))
|
|
58
|
+
throw new Error(`blocked by safeMode: ${d.why}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function resolveRead(p, ctx) {
|
|
62
|
+
return path.resolve(ctx.workdir, p);
|
|
63
|
+
}
|
|
64
|
+
function resolveWrite(p, ctx) {
|
|
65
|
+
const abs = path.resolve(ctx.workdir, p);
|
|
66
|
+
const ok = (0, util_1.pathInside)(ctx.workdir, abs) || (0, util_1.pathInside)(ctx.runDirPath, abs) || !ctx.cfg.safeMode;
|
|
67
|
+
if (!ok) {
|
|
68
|
+
throw new Error(`safeMode: writes are restricted to the working directory (${ctx.workdir}). ` +
|
|
69
|
+
`Use a relative path, or save deliverables with save_artifact.`);
|
|
70
|
+
}
|
|
71
|
+
return abs;
|
|
72
|
+
}
|
|
73
|
+
// ---------- sandbox-aware file IO ----------
|
|
74
|
+
async function readFileVia(ctx, abs) {
|
|
75
|
+
return ctx.sandbox.localFs ? fs.readFileSync(abs, "utf8") : ctx.sandbox.readFile(abs);
|
|
76
|
+
}
|
|
77
|
+
async function writeFileVia(ctx, abs, content) {
|
|
78
|
+
if (ctx.sandbox.localFs) {
|
|
79
|
+
(0, util_1.ensureDir)(path.dirname(abs));
|
|
80
|
+
fs.writeFileSync(abs, content, "utf8");
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await ctx.sandbox.writeFile(abs, content);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ---------- tool definitions ----------
|
|
87
|
+
function workerToolset() {
|
|
88
|
+
const tools = {};
|
|
89
|
+
tools.shell = {
|
|
90
|
+
schema: {
|
|
91
|
+
name: "shell",
|
|
92
|
+
description: "Run a bash command in the working directory. Returns exit code, stdout and stderr (interleaved). Use for builds, tests, git, package managers, inspecting the system. Long-running servers will be killed at the timeout — do not start blocking daemons.",
|
|
93
|
+
parameters: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
command: { type: "string", description: "Bash command to run" },
|
|
97
|
+
cwd: { type: "string", description: "Optional subdirectory to run in (relative to the working directory)" },
|
|
98
|
+
timeout_sec: { type: "number", description: "Timeout in seconds (default 180, max 900)" },
|
|
99
|
+
},
|
|
100
|
+
required: ["command"],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
run: async (args, ctx) => {
|
|
104
|
+
const cmd = String(args.command ?? "");
|
|
105
|
+
if (!cmd.trim())
|
|
106
|
+
throw new Error("command is required");
|
|
107
|
+
checkCommand(cmd, ctx.cfg);
|
|
108
|
+
const cwd = args.cwd ? resolveRead(String(args.cwd), ctx) : ctx.workdir;
|
|
109
|
+
if (ctx.sandbox.localFs && !fs.existsSync(cwd))
|
|
110
|
+
throw new Error(`cwd does not exist: ${cwd}`);
|
|
111
|
+
const timeout = Math.min(Math.max(Number(args.timeout_sec) || 180, 5), 900);
|
|
112
|
+
const t0 = Date.now();
|
|
113
|
+
const r = await ctx.sandbox.exec(cmd, { cwd, timeoutSec: timeout, signal: ctx.signal });
|
|
114
|
+
const dur = ((Date.now() - t0) / 1000).toFixed(1);
|
|
115
|
+
const status = r.timedOut ? `TIMED OUT after ${timeout}s` : `exit ${r.code}`;
|
|
116
|
+
return `[${status} in ${dur}s]\n${r.out || "(no output)"}`;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
tools.read_file = {
|
|
120
|
+
schema: {
|
|
121
|
+
name: "read_file",
|
|
122
|
+
description: "Read a text file. Prefer reading specific line ranges of big files. Returns numbered lines.",
|
|
123
|
+
parameters: {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
path: { type: "string" },
|
|
127
|
+
start_line: { type: "number", description: "1-based, optional" },
|
|
128
|
+
end_line: { type: "number", description: "inclusive, optional" },
|
|
129
|
+
},
|
|
130
|
+
required: ["path"],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
run: async (args, ctx) => {
|
|
134
|
+
const abs = resolveRead(String(args.path), ctx);
|
|
135
|
+
const raw = await readFileVia(ctx, abs);
|
|
136
|
+
const lines = raw.split("\n");
|
|
137
|
+
const start = Math.max(1, Number(args.start_line) || 1);
|
|
138
|
+
const end = Math.min(lines.length, Number(args.end_line) || lines.length);
|
|
139
|
+
const slice = lines.slice(start - 1, end);
|
|
140
|
+
const numbered = slice
|
|
141
|
+
.map((l, i) => `${String(start + i).padStart(5)}│${l}`)
|
|
142
|
+
.join("\n");
|
|
143
|
+
const header = `${abs} (${lines.length} lines, showing ${start}-${end})\n`;
|
|
144
|
+
return header + numbered;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
tools.write_file = {
|
|
148
|
+
schema: {
|
|
149
|
+
name: "write_file",
|
|
150
|
+
description: "Create or overwrite a file with the given content. Creates parent directories. Paths are relative to the working directory.",
|
|
151
|
+
parameters: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
path: { type: "string" },
|
|
155
|
+
content: { type: "string" },
|
|
156
|
+
},
|
|
157
|
+
required: ["path", "content"],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
run: async (args, ctx) => {
|
|
161
|
+
const abs = resolveWrite(String(args.path), ctx);
|
|
162
|
+
const content = String(args.content ?? "");
|
|
163
|
+
if (content.length > 5_000_000)
|
|
164
|
+
throw new Error("content too large (>5MB)");
|
|
165
|
+
await writeFileVia(ctx, abs, content);
|
|
166
|
+
return `wrote ${abs} (${content.length} chars)`;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
tools.replace_in_file = {
|
|
170
|
+
schema: {
|
|
171
|
+
name: "replace_in_file",
|
|
172
|
+
description: "Exact string replacement in a file. `find` must match exactly (including whitespace). Fails if not found, or if ambiguous when all=false.",
|
|
173
|
+
parameters: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
path: { type: "string" },
|
|
177
|
+
find: { type: "string" },
|
|
178
|
+
replace: { type: "string" },
|
|
179
|
+
all: { type: "boolean", description: "Replace every occurrence (default false)" },
|
|
180
|
+
},
|
|
181
|
+
required: ["path", "find", "replace"],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
run: async (args, ctx) => {
|
|
185
|
+
const abs = resolveWrite(String(args.path), ctx);
|
|
186
|
+
const raw = await readFileVia(ctx, abs);
|
|
187
|
+
const find = String(args.find);
|
|
188
|
+
const replace = String(args.replace);
|
|
189
|
+
const count = raw.split(find).length - 1;
|
|
190
|
+
if (count === 0)
|
|
191
|
+
throw new Error("find string not found in file");
|
|
192
|
+
if (count > 1 && !args.all) {
|
|
193
|
+
throw new Error(`find string matches ${count} times; provide more context or set all=true`);
|
|
194
|
+
}
|
|
195
|
+
const next = args.all ? raw.split(find).join(replace) : raw.replace(find, replace);
|
|
196
|
+
await writeFileVia(ctx, abs, next);
|
|
197
|
+
return `replaced ${args.all ? count : 1} occurrence(s) in ${abs}`;
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
tools.list_dir = {
|
|
201
|
+
schema: {
|
|
202
|
+
name: "list_dir",
|
|
203
|
+
description: "List files and directories as a tree (skips node_modules, .git, build output).",
|
|
204
|
+
parameters: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
path: { type: "string", description: "Default: working directory" },
|
|
208
|
+
depth: { type: "number", description: "Max depth, default 2, max 4" },
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
run: async (args, ctx) => {
|
|
213
|
+
const root = args.path ? resolveRead(String(args.path), ctx) : ctx.workdir;
|
|
214
|
+
const maxDepth = Math.min(Math.max(Number(args.depth) || 2, 1), 4);
|
|
215
|
+
if (!ctx.sandbox.localFs) {
|
|
216
|
+
// Remote filesystem: one find(1) round-trip instead of a local walk.
|
|
217
|
+
const r = await ctx.sandbox.exec(`find . -maxdepth ${maxDepth} \\( -name node_modules -o -name .git -o -name dist -o -name .next -o -name build -o -name __pycache__ -o -name .venv \\) -prune -o -print | sed 's|^\\./||' | sort | head -400`, { cwd: root, timeoutSec: 30, signal: ctx.signal });
|
|
218
|
+
if (r.code !== 0)
|
|
219
|
+
throw new Error(`list failed: ${r.out.slice(0, 200)}`);
|
|
220
|
+
const body = r.out.split("\n").filter((l) => l.trim() && l.trim() !== ".").join("\n");
|
|
221
|
+
return `${root}/\n` + (body || "(empty)");
|
|
222
|
+
}
|
|
223
|
+
const SKIP = new Set([
|
|
224
|
+
"node_modules", ".git", "dist", ".next", "out", "build", "target",
|
|
225
|
+
"__pycache__", ".venv", "venv", ".cache", ".DS_Store",
|
|
226
|
+
]);
|
|
227
|
+
const lines = [];
|
|
228
|
+
const walk = (dir, depth, prefix) => {
|
|
229
|
+
if (lines.length > 400)
|
|
230
|
+
return;
|
|
231
|
+
let entries;
|
|
232
|
+
try {
|
|
233
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
lines.push(`${prefix}[unreadable: ${(0, util_1.errMsg)(e)}]`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
240
|
+
for (const e of entries) {
|
|
241
|
+
if (SKIP.has(e.name))
|
|
242
|
+
continue;
|
|
243
|
+
if (lines.length > 400) {
|
|
244
|
+
lines.push(`${prefix}… (truncated)`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (e.isDirectory()) {
|
|
248
|
+
lines.push(`${prefix}${e.name}/`);
|
|
249
|
+
if (depth < maxDepth)
|
|
250
|
+
walk(path.join(dir, e.name), depth + 1, prefix + " ");
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
let size = "";
|
|
254
|
+
try {
|
|
255
|
+
size = ` (${fs.statSync(path.join(dir, e.name)).size}b)`;
|
|
256
|
+
}
|
|
257
|
+
catch { /* race */ }
|
|
258
|
+
lines.push(`${prefix}${e.name}${size}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
walk(root, 1, "");
|
|
263
|
+
return `${root}/\n` + (lines.join("\n") || "(empty)");
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
tools.web_search = {
|
|
267
|
+
schema: {
|
|
268
|
+
name: "web_search",
|
|
269
|
+
description: "Search the web. Returns ranked results with title, URL and snippet. " +
|
|
270
|
+
"Set deep=true to also fetch top pages and return quotable passages (slower; use for claims that need grounding).",
|
|
271
|
+
parameters: {
|
|
272
|
+
type: "object",
|
|
273
|
+
properties: {
|
|
274
|
+
query: { type: "string" },
|
|
275
|
+
count: { type: "number", description: "Max results, default 6, max 10" },
|
|
276
|
+
deep: { type: "boolean", description: "Fetch page content for quotable passages" },
|
|
277
|
+
},
|
|
278
|
+
required: ["query"],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
run: async (args, ctx) => {
|
|
282
|
+
const count = Math.min(Math.max(Number(args.count) || 6, 1), 10);
|
|
283
|
+
const hits = await (0, webtools_1.webSearch)(ctx.cfg, String(args.query), count, ctx.signal, Boolean(args.deep), (msg) => ctx.log?.("warn", msg));
|
|
284
|
+
if (!hits.length)
|
|
285
|
+
return "no results";
|
|
286
|
+
return hits
|
|
287
|
+
.map((h, i) => {
|
|
288
|
+
const head = `${i + 1}. ${h.title}${h.date ? ` (${h.date})` : ""}\n ${h.url}\n ${h.snippet}`;
|
|
289
|
+
const quotes = (h.passages || []).map((p) => ` > ${p}`).join("\n");
|
|
290
|
+
return quotes ? `${head}\n${quotes}` : head;
|
|
291
|
+
})
|
|
292
|
+
.join("\n");
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
tools.fetch_url = {
|
|
296
|
+
schema: {
|
|
297
|
+
name: "fetch_url",
|
|
298
|
+
description: "Fetch a URL and return readable text (HTML is converted to text/markdown). Set raw=true for raw bodies like JSON or code.",
|
|
299
|
+
parameters: {
|
|
300
|
+
type: "object",
|
|
301
|
+
properties: {
|
|
302
|
+
url: { type: "string" },
|
|
303
|
+
raw: { type: "boolean" },
|
|
304
|
+
},
|
|
305
|
+
required: ["url"],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
run: async (args, ctx) => {
|
|
309
|
+
const url = String(args.url);
|
|
310
|
+
if (!/^https?:\/\//.test(url))
|
|
311
|
+
throw new Error("only http(s) URLs are supported");
|
|
312
|
+
return (0, webtools_1.fetchUrl)(ctx.cfg, url, Boolean(args.raw), 60_000, ctx.signal);
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
tools.note = {
|
|
316
|
+
schema: {
|
|
317
|
+
name: "note",
|
|
318
|
+
description: "Post a durable fact/discovery to the swarm's shared blackboard so the conductor and other agents can see it. Use sparingly — facts other tasks need, not progress chatter.",
|
|
319
|
+
parameters: {
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
text: { type: "string" },
|
|
323
|
+
key: { type: "string", description: "Optional short label" },
|
|
324
|
+
},
|
|
325
|
+
required: ["text"],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
run: async (args, ctx) => {
|
|
329
|
+
ctx.addNote(String(args.text), args.key ? String(args.key) : undefined);
|
|
330
|
+
return "noted on the blackboard";
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
tools.save_artifact = {
|
|
334
|
+
schema: {
|
|
335
|
+
name: "save_artifact",
|
|
336
|
+
description: "Save a deliverable into the run's artifacts folder (shown prominently to the operator). Provide content, or from_path to copy an existing file.",
|
|
337
|
+
parameters: {
|
|
338
|
+
type: "object",
|
|
339
|
+
properties: {
|
|
340
|
+
name: { type: "string", description: "Filename, may include subdirs like data/results.csv" },
|
|
341
|
+
content: { type: "string" },
|
|
342
|
+
from_path: { type: "string" },
|
|
343
|
+
},
|
|
344
|
+
required: ["name"],
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
run: async (args, ctx) => {
|
|
348
|
+
const name = String(args.name).replace(/^\/+/, "");
|
|
349
|
+
const dest = path.join(ctx.runDirPath, "artifacts", name);
|
|
350
|
+
if (!(0, util_1.pathInside)(path.join(ctx.runDirPath, "artifacts"), dest)) {
|
|
351
|
+
throw new Error("artifact name must stay inside the artifacts folder");
|
|
352
|
+
}
|
|
353
|
+
(0, util_1.ensureDir)(path.dirname(dest));
|
|
354
|
+
if (typeof args.content === "string") {
|
|
355
|
+
fs.writeFileSync(dest, args.content, "utf8");
|
|
356
|
+
}
|
|
357
|
+
else if (args.from_path) {
|
|
358
|
+
// Artifacts always land on the host so the operator can open them,
|
|
359
|
+
// even when the workspace lives in a remote sandbox.
|
|
360
|
+
await ctx.sandbox.pull(resolveRead(String(args.from_path), ctx), dest);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
throw new Error("provide content or from_path");
|
|
364
|
+
}
|
|
365
|
+
ctx.addArtifact(name);
|
|
366
|
+
return `saved artifacts/${name}`;
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
return tools;
|
|
370
|
+
}
|
|
371
|
+
function verifierToolset() {
|
|
372
|
+
const all = workerToolset();
|
|
373
|
+
return {
|
|
374
|
+
shell: all.shell,
|
|
375
|
+
read_file: all.read_file,
|
|
376
|
+
list_dir: all.list_dir,
|
|
377
|
+
fetch_url: all.fetch_url,
|
|
378
|
+
web_search: all.web_search,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function synthToolset() {
|
|
382
|
+
const all = workerToolset();
|
|
383
|
+
return {
|
|
384
|
+
read_file: all.read_file,
|
|
385
|
+
list_dir: all.list_dir,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
// ---------- terminal tool schemas (handled by the agent loop, not executed) ----------
|
|
389
|
+
exports.REPORT_TOOL = {
|
|
390
|
+
name: "report",
|
|
391
|
+
description: "End your task and report back to the conductor. This is the ONLY thing the conductor sees from your work — be specific: what you did, what you verified, key findings, exact file paths.",
|
|
392
|
+
parameters: {
|
|
393
|
+
type: "object",
|
|
394
|
+
properties: {
|
|
395
|
+
status: { type: "string", enum: ["done", "blocked"] },
|
|
396
|
+
report: {
|
|
397
|
+
type: "string",
|
|
398
|
+
description: "Concrete results with evidence. If blocked: exactly what is missing.",
|
|
399
|
+
},
|
|
400
|
+
artifacts: {
|
|
401
|
+
type: "array",
|
|
402
|
+
items: { type: "string" },
|
|
403
|
+
description: "Paths of files you created/changed that matter",
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
required: ["status", "report"],
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
exports.VERDICT_TOOL = {
|
|
410
|
+
name: "verdict",
|
|
411
|
+
description: "Deliver your verification verdict.",
|
|
412
|
+
parameters: {
|
|
413
|
+
type: "object",
|
|
414
|
+
properties: {
|
|
415
|
+
pass: { type: "boolean" },
|
|
416
|
+
feedback: {
|
|
417
|
+
type: "string",
|
|
418
|
+
description: "If fail: exactly what is wrong and where. If pass: one-line confirmation of the evidence.",
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
required: ["pass", "feedback"],
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
exports.SUBMIT_FINAL_TOOL = {
|
|
425
|
+
name: "submit_final",
|
|
426
|
+
description: "Submit the final mission deliverable.",
|
|
427
|
+
parameters: {
|
|
428
|
+
type: "object",
|
|
429
|
+
properties: {
|
|
430
|
+
report_markdown: {
|
|
431
|
+
type: "string",
|
|
432
|
+
description: "The definitive final report document (markdown).",
|
|
433
|
+
},
|
|
434
|
+
summary: { type: "string", description: "Short summary (≤8 sentences) for the console." },
|
|
435
|
+
},
|
|
436
|
+
required: ["report_markdown", "summary"],
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
exports.SPAWN_TASKS_TOOL = {
|
|
440
|
+
name: "spawn_tasks",
|
|
441
|
+
description: "Spawn new tasks; each becomes an autonomous worker agent. Tasks with no unmet deps start immediately, in parallel.",
|
|
442
|
+
parameters: {
|
|
443
|
+
type: "object",
|
|
444
|
+
properties: {
|
|
445
|
+
tasks: {
|
|
446
|
+
type: "array",
|
|
447
|
+
items: {
|
|
448
|
+
type: "object",
|
|
449
|
+
properties: {
|
|
450
|
+
title: { type: "string", description: "Short imperative title" },
|
|
451
|
+
objective: {
|
|
452
|
+
type: "string",
|
|
453
|
+
description: "Self-contained objective with explicit success criteria ('Done when …'). The worker sees nothing else except `context` and dep reports.",
|
|
454
|
+
},
|
|
455
|
+
role: { type: "string", description: "Specialist role, e.g. researcher | coder | analyst | writer | reviewer | data-wrangler" },
|
|
456
|
+
deps: {
|
|
457
|
+
type: "array",
|
|
458
|
+
items: { type: "string" },
|
|
459
|
+
description: "Task ids that must finish first; their reports are given to this worker",
|
|
460
|
+
},
|
|
461
|
+
verify: { type: "boolean", description: "Adversarially verify this task's result before accepting it" },
|
|
462
|
+
context: { type: "string", description: "Facts, paths, URLs, constraints the worker needs inlined" },
|
|
463
|
+
},
|
|
464
|
+
required: ["title", "objective"],
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
required: ["tasks"],
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
exports.WAIT_TOOL = {
|
|
472
|
+
name: "wait",
|
|
473
|
+
description: "Do nothing for now; wake again when running tasks report.",
|
|
474
|
+
parameters: {
|
|
475
|
+
type: "object",
|
|
476
|
+
properties: { reason: { type: "string" } },
|
|
477
|
+
},
|
|
478
|
+
};
|
|
479
|
+
exports.FINISH_TOOL = {
|
|
480
|
+
name: "finish",
|
|
481
|
+
description: "Declare the mission complete (or as complete as the budget allows). A synthesizer agent will then compose the final report from all task reports.",
|
|
482
|
+
parameters: {
|
|
483
|
+
type: "object",
|
|
484
|
+
properties: {
|
|
485
|
+
notes: {
|
|
486
|
+
type: "string",
|
|
487
|
+
description: "Guidance for the synthesizer: what matters most, what to highlight, any caveats.",
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Core shared types for the agentswarm engine.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ZERO_USAGE = void 0;
|
|
5
|
+
exports.addUsage = addUsage;
|
|
6
|
+
exports.usageCost = usageCost;
|
|
7
|
+
exports.ZERO_USAGE = {
|
|
8
|
+
promptTokens: 0,
|
|
9
|
+
completionTokens: 0,
|
|
10
|
+
cacheHitTokens: 0,
|
|
11
|
+
cacheMissTokens: 0,
|
|
12
|
+
};
|
|
13
|
+
function addUsage(a, b) {
|
|
14
|
+
return {
|
|
15
|
+
promptTokens: a.promptTokens + b.promptTokens,
|
|
16
|
+
completionTokens: a.completionTokens + b.completionTokens,
|
|
17
|
+
cacheHitTokens: a.cacheHitTokens + b.cacheHitTokens,
|
|
18
|
+
cacheMissTokens: a.cacheMissTokens + b.cacheMissTokens,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function usageCost(u, price) {
|
|
22
|
+
if (!price)
|
|
23
|
+
return 0;
|
|
24
|
+
const miss = u.cacheMissTokens || Math.max(0, u.promptTokens - u.cacheHitTokens);
|
|
25
|
+
return ((miss * price.inMiss + u.cacheHitTokens * price.inHit + u.completionTokens * price.out) / 1e6);
|
|
26
|
+
}
|