@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/journal.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
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.Journal = void 0;
|
|
37
|
+
exports.eventsFile = eventsFile;
|
|
38
|
+
exports.readEvents = readEvents;
|
|
39
|
+
exports.lastSeq = lastSeq;
|
|
40
|
+
exports.readNewEvents = readNewEvents;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* Append-only event journal. events.jsonl is the single source of truth for a
|
|
45
|
+
* run: the executor writes it, the terminal renderer and the hub (web UI) read
|
|
46
|
+
* and tail it. Tolerant of a torn final line after a crash.
|
|
47
|
+
*/
|
|
48
|
+
class Journal {
|
|
49
|
+
file;
|
|
50
|
+
seq;
|
|
51
|
+
chain = Promise.resolve();
|
|
52
|
+
onEvent;
|
|
53
|
+
constructor(runDirPath, startSeq) {
|
|
54
|
+
this.file = path.join(runDirPath, "events.jsonl");
|
|
55
|
+
this.seq = startSeq ?? lastSeq(runDirPath) + 1;
|
|
56
|
+
}
|
|
57
|
+
append(type, payload = {}) {
|
|
58
|
+
const ev = { seq: this.seq++, t: Date.now(), type, ...payload };
|
|
59
|
+
const line = JSON.stringify(ev) + "\n";
|
|
60
|
+
this.chain = this.chain
|
|
61
|
+
.then(() => fs.promises.appendFile(this.file, line, "utf8"))
|
|
62
|
+
.catch(() => {
|
|
63
|
+
/* never break the run on journal IO; next append retries the chain */
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
this.onEvent?.(ev);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* renderer errors must not kill the run */
|
|
70
|
+
}
|
|
71
|
+
return ev;
|
|
72
|
+
}
|
|
73
|
+
flush() {
|
|
74
|
+
return this.chain;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.Journal = Journal;
|
|
78
|
+
function eventsFile(runDirPath) {
|
|
79
|
+
return path.join(runDirPath, "events.jsonl");
|
|
80
|
+
}
|
|
81
|
+
function readEvents(runDirPath) {
|
|
82
|
+
let raw;
|
|
83
|
+
try {
|
|
84
|
+
raw = fs.readFileSync(eventsFile(runDirPath), "utf8");
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return parseLines(raw).events;
|
|
90
|
+
}
|
|
91
|
+
function lastSeq(runDirPath) {
|
|
92
|
+
const evs = readEvents(runDirPath);
|
|
93
|
+
return evs.length ? evs[evs.length - 1].seq : 0;
|
|
94
|
+
}
|
|
95
|
+
/** Incremental read for tailing; handles partially written lines. */
|
|
96
|
+
function readNewEvents(file, state) {
|
|
97
|
+
let stat;
|
|
98
|
+
try {
|
|
99
|
+
stat = fs.statSync(file);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
if (stat.size < state.offset) {
|
|
105
|
+
// Truncated/rewritten (should not happen) — start over.
|
|
106
|
+
state.offset = 0;
|
|
107
|
+
state.carry = "";
|
|
108
|
+
}
|
|
109
|
+
if (stat.size === state.offset)
|
|
110
|
+
return [];
|
|
111
|
+
const fd = fs.openSync(file, "r");
|
|
112
|
+
try {
|
|
113
|
+
const len = stat.size - state.offset;
|
|
114
|
+
const buf = Buffer.alloc(Math.min(len, 8 * 1024 * 1024));
|
|
115
|
+
let read = 0;
|
|
116
|
+
const out = [];
|
|
117
|
+
while (read < len) {
|
|
118
|
+
const n = fs.readSync(fd, buf, 0, Math.min(buf.length, len - read), state.offset + read);
|
|
119
|
+
if (n <= 0)
|
|
120
|
+
break;
|
|
121
|
+
read += n;
|
|
122
|
+
const text = state.carry + buf.toString("utf8", 0, n);
|
|
123
|
+
const parsed = parseLines(text, true);
|
|
124
|
+
state.carry = parsed.carry;
|
|
125
|
+
out.push(...parsed.events);
|
|
126
|
+
}
|
|
127
|
+
state.offset += read;
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
fs.closeSync(fd);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function parseLines(text, keepCarry = false) {
|
|
135
|
+
const events = [];
|
|
136
|
+
const lines = text.split("\n");
|
|
137
|
+
const carry = keepCarry ? lines.pop() ?? "" : "";
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
const s = line.trim();
|
|
140
|
+
if (!s)
|
|
141
|
+
continue;
|
|
142
|
+
try {
|
|
143
|
+
const ev = JSON.parse(s);
|
|
144
|
+
if (ev && typeof ev.type === "string")
|
|
145
|
+
events.push(ev);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
/* torn line — skip */
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { events, carry };
|
|
152
|
+
}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
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.SYNTH_KICKOFF = exports.VERIFIER_KICKOFF = exports.STEP_LIMIT_FINAL = exports.NUDGE_USE_TOOLS = exports.WORKER_KICKOFF = void 0;
|
|
37
|
+
exports.conductorSystem = conductorSystem;
|
|
38
|
+
exports.conductorInitialUpdate = conductorInitialUpdate;
|
|
39
|
+
exports.conductorUpdate = conductorUpdate;
|
|
40
|
+
exports.taskTable = taskTable;
|
|
41
|
+
exports.reportBlock = reportBlock;
|
|
42
|
+
exports.workerSystem = workerSystem;
|
|
43
|
+
exports.forcedFinal = forcedFinal;
|
|
44
|
+
exports.verifierSystem = verifierSystem;
|
|
45
|
+
exports.synthSystem = synthSystem;
|
|
46
|
+
exports.compactorPrompt = compactorPrompt;
|
|
47
|
+
exports.budgetLine = budgetLine;
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const util_1 = require("./util");
|
|
50
|
+
// ============================================================ conductor
|
|
51
|
+
function conductorSystem(meta) {
|
|
52
|
+
const o = meta.options;
|
|
53
|
+
return `You are the Conductor of an agent swarm — the lead intelligence that decomposes a mission into tasks and orchestrates many parallel worker agents to accomplish it. You never do the work yourself; you direct the swarm.
|
|
54
|
+
|
|
55
|
+
MISSION
|
|
56
|
+
${meta.mission}
|
|
57
|
+
|
|
58
|
+
ENVIRONMENT
|
|
59
|
+
- Working directory: ${meta.cwd}${meta.sandbox ? " (isolated sandbox created for this run)" : " (the operator's real directory — be purposeful)"}
|
|
60
|
+
- OS: ${os.platform()} ${os.release()} · Node available · agents run shell with the operator's permissions
|
|
61
|
+
- Date: ${new Date(meta.createdAt).toISOString().slice(0, 10)}
|
|
62
|
+
|
|
63
|
+
HOW THE SWARM WORKS
|
|
64
|
+
- spawn_tasks creates worker agents. Each worker is autonomous, with shell / file / web tools in the working directory, and runs up to ${o.maxStepsPerTask} tool steps.
|
|
65
|
+
- Up to ${o.maxWorkers} tasks run in parallel. A task starts the moment all its deps are done.
|
|
66
|
+
- Workers share NOTHING except: (1) what you write in their objective/context, (2) the final reports of their deps, (3) the shared blackboard digest. There is no other communication.
|
|
67
|
+
- Task ids are assigned sequentially in the order you spawn them (you are told the next id). deps may reference any earlier-created task, including earlier tasks in the same spawn_tasks call.
|
|
68
|
+
- When tasks settle you are woken with their reports and the swarm state; you then spawn more tasks, wait, or finish.
|
|
69
|
+
|
|
70
|
+
DOCTRINE
|
|
71
|
+
1. PARALLELIZE AGGRESSIVELY. Independent work = separate simultaneous tasks. Prefer 3–6 parallel scouts over one serial mega-task: wide first, then a consolidation task with deps.
|
|
72
|
+
2. Make every task self-contained: crisp objective, explicit success criteria ("Done when …"), and every fact/path/URL the worker needs inlined in context. Workers know nothing you don't tell them.
|
|
73
|
+
3. Invent the right specialist role per task (researcher, coder, analyst, data-wrangler, reviewer, writer, …). One concern per task, roughly 5–25 tool steps of work. Bigger → split it. Trivial → batch it.
|
|
74
|
+
4. Software missions: scaffold first (one task), then parallel tasks on DISJOINT files/modules — never two writers on the same file — then an integration + test task that deps on all of them with verify:true.
|
|
75
|
+
5. Research missions: parallel scouts with distinct angles and sources, then a consolidation/analysis task that deps on the scouts.
|
|
76
|
+
6. Set verify:true on tasks whose failure would poison the mission (builds, integrations, data pipelines, final deliverables). A verification agent will adversarially check them and can fail them back for retry.
|
|
77
|
+
7. React to evidence. Failed/blocked task → diagnose from its report and spawn a corrected or alternative approach (never re-run a failed approach verbatim). Surprising findings → adapt the plan.
|
|
78
|
+
8. Watch the budget shown in every update. As it tightens, cut scope to what the mission truly needs — always deliver value before the cap, never run out mid-flight.
|
|
79
|
+
9. Operator messages override everything. Adjust the plan immediately when one appears.
|
|
80
|
+
10. finish only when the mission's success criteria are demonstrably met, or budget/feasibility forces it. Your finish notes steer the synthesizer that writes the final report.
|
|
81
|
+
|
|
82
|
+
RULES
|
|
83
|
+
- Respond ONLY by calling your tools (spawn_tasks / wait / finish). Plain-text replies are ignored.
|
|
84
|
+
- Never spawn a task whose deps are not yet all created.
|
|
85
|
+
- Keep the total task count within budget (max ${o.maxTasks} per run); make every task earn its place.`;
|
|
86
|
+
}
|
|
87
|
+
function conductorInitialUpdate(meta, nextId) {
|
|
88
|
+
return `The swarm is live. No tasks exist yet. Next task id: T${nextId}.
|
|
89
|
+
|
|
90
|
+
Decompose the mission and spawn the first wave now. Remember: maximize useful parallelism; self-contained objectives with success criteria; inline all context workers need.`;
|
|
91
|
+
}
|
|
92
|
+
function conductorUpdate(p) {
|
|
93
|
+
const sections = [];
|
|
94
|
+
if (p.operatorNotes?.length) {
|
|
95
|
+
sections.push(`⚑ OPERATOR MESSAGES (these override the current plan):\n${p.operatorNotes.map((n) => `- ${n}`).join("\n")}`);
|
|
96
|
+
}
|
|
97
|
+
if (p.reports?.length)
|
|
98
|
+
sections.push(`NEW REPORTS\n${p.reports.join("\n\n")}`);
|
|
99
|
+
if (p.blackboard)
|
|
100
|
+
sections.push(`BLACKBOARD (shared notes digest)\n${p.blackboard}`);
|
|
101
|
+
sections.push(`SWARM STATE\n${p.taskTable}`);
|
|
102
|
+
sections.push(p.budgetLine);
|
|
103
|
+
if (p.extra)
|
|
104
|
+
sections.push(p.extra);
|
|
105
|
+
sections.push(`Next task id: T${p.nextId}. Decide now: spawn_tasks, wait, or finish.`);
|
|
106
|
+
return sections.join("\n\n");
|
|
107
|
+
}
|
|
108
|
+
function taskTable(tasks) {
|
|
109
|
+
if (!tasks.length)
|
|
110
|
+
return "(no tasks yet)";
|
|
111
|
+
return tasks
|
|
112
|
+
.map((t) => {
|
|
113
|
+
const deps = t.deps.length ? ` deps:[${t.deps.join(",")}]` : "";
|
|
114
|
+
const extra = t.status === "failed" && t.error ? ` — ${(0, util_1.clip)(t.error, 80)}` : "";
|
|
115
|
+
return `${t.id} [${t.status}${t.attempt > 1 ? ` a${t.attempt}` : ""}] (${t.role})${deps} ${(0, util_1.clip)(t.title, 70)}${extra}`;
|
|
116
|
+
})
|
|
117
|
+
.join("\n");
|
|
118
|
+
}
|
|
119
|
+
function reportBlock(t) {
|
|
120
|
+
const head = `── ${t.id} (${t.role}) "${(0, util_1.clip)(t.title, 60)}" → ${t.status.toUpperCase()}${t.attempt > 1 ? ` (attempt ${t.attempt})` : ""}`;
|
|
121
|
+
const body = t.report ? (0, util_1.clip)(t.report, 1600) : t.error ? `error: ${(0, util_1.clip)(t.error, 400)}` : "(no report)";
|
|
122
|
+
const arts = t.artifacts.length ? `\nartifacts: ${t.artifacts.join(", ")}` : "";
|
|
123
|
+
const fb = t.feedback ? `\nverifier: ${(0, util_1.clip)(t.feedback, 300)}` : "";
|
|
124
|
+
return `${head}\n${body}${arts}${fb}`;
|
|
125
|
+
}
|
|
126
|
+
// ============================================================ workers
|
|
127
|
+
const ROLE_HINTS = {
|
|
128
|
+
researcher: "Research craft: triangulate across independent sources; prefer primary docs over blog spam; capture exact figures, dates, URLs. Search several distinct phrasings before concluding something is unfindable.",
|
|
129
|
+
coder: "Engineering craft: read existing code before changing it; match its conventions; build/run/test after every meaningful change and include the command + result in your report. Leave the tree compiling.",
|
|
130
|
+
analyst: "Analysis craft: quantify wherever possible; state assumptions explicitly; separate observation from interpretation; sanity-check numbers twice.",
|
|
131
|
+
writer: "Writing craft: structure before prose; concrete over abstract; cut filler. Match the audience and purpose given in the objective.",
|
|
132
|
+
reviewer: "Review craft: be adversarial; try to break it; check edge cases and the unhappy path; verify claims against the actual files, not the description.",
|
|
133
|
+
"data-wrangler": "Data craft: validate schema and row counts at every step; spot-check samples; never silently drop rows — report anomalies.",
|
|
134
|
+
};
|
|
135
|
+
function workerSystem(opts) {
|
|
136
|
+
const { meta, task } = opts;
|
|
137
|
+
const roleHint = ROLE_HINTS[opts.role.toLowerCase()] ?? "";
|
|
138
|
+
const retry = task.attempt > 1 && task.feedback
|
|
139
|
+
? `\nPREVIOUS ATTEMPT FAILED VERIFICATION — fix exactly this:\n${task.feedback}\n`
|
|
140
|
+
: task.attempt > 1 && task.error
|
|
141
|
+
? `\nPREVIOUS ATTEMPT FAILED: ${task.error}\nTake a different approach.\n`
|
|
142
|
+
: "";
|
|
143
|
+
return `You are ${opts.agentId}, a ${opts.role} agent in a swarm pursuing this mission:
|
|
144
|
+
${meta.mission}
|
|
145
|
+
|
|
146
|
+
YOUR TASK — ${task.id} (attempt ${task.attempt})
|
|
147
|
+
${task.title}
|
|
148
|
+
Objective: ${task.objective}
|
|
149
|
+
${task.context ? `Context from the conductor:\n${task.context}\n` : ""}${retry}
|
|
150
|
+
CONTEXT FROM THE SWARM
|
|
151
|
+
${opts.depReports || "(no dependency reports)"}
|
|
152
|
+
${opts.blackboard ? `Blackboard digest:\n${opts.blackboard}` : ""}
|
|
153
|
+
${opts.operatorNotes.length ? `Operator notes:\n${opts.operatorNotes.map((n) => `- ${n}`).join("\n")}` : ""}
|
|
154
|
+
Working directory: ${meta.cwd}
|
|
155
|
+
${opts.dirListing ? `Top of the working directory:\n${opts.dirListing}` : ""}
|
|
156
|
+
|
|
157
|
+
OPERATING PROTOCOL
|
|
158
|
+
- You are fully autonomous. Never ask questions; decide and act.
|
|
159
|
+
- Plan briefly, then act in small verified steps: after changing anything, prove it worked (run it, read it back, test it).
|
|
160
|
+
- Evidence over assumption: read before you edit; check outputs; cite concrete paths, commands and numbers.
|
|
161
|
+
- Be token-lean: targeted reads (line ranges, grep via shell) over wholesale dumps; don't re-read unchanged files.
|
|
162
|
+
- Post durable discoveries other agents will need to the blackboard with note(...) — facts only, used sparingly.
|
|
163
|
+
- Save deliverable files with save_artifact so the operator sees them.
|
|
164
|
+
- Genuinely impossible / missing prerequisite → report(status:"blocked", …) early instead of thrashing.
|
|
165
|
+
- You have at most ${opts.maxSteps} tool steps. Budget them.
|
|
166
|
+
- ALWAYS end by calling report(...). The conductor sees ONLY that report — it is the entire value of your work. Specific beats vague: what you did, what you verified, key findings, exact paths.
|
|
167
|
+
${roleHint ? "\n" + roleHint : ""}`;
|
|
168
|
+
}
|
|
169
|
+
exports.WORKER_KICKOFF = "Begin now. Work the task to completion, then call report(...).";
|
|
170
|
+
exports.NUDGE_USE_TOOLS = "Reminder: act via tool calls only. Continue the work; when complete (or truly blocked), call report(...). Do not reply with plain text.";
|
|
171
|
+
exports.STEP_LIMIT_FINAL = "You have hit the step limit. Call report(...) RIGHT NOW with your best honest account: what you completed, what you verified, what remains.";
|
|
172
|
+
function forcedFinal(reason) {
|
|
173
|
+
return `${reason} Stop working and call your terminal tool RIGHT NOW with your best honest account: what you completed, what you verified, what remains.`;
|
|
174
|
+
}
|
|
175
|
+
// ============================================================ verifier
|
|
176
|
+
function verifierSystem(meta, task) {
|
|
177
|
+
return `You are an adversarial verification agent. A worker claims it completed this task — your job is to try to falsify that claim with evidence.
|
|
178
|
+
|
|
179
|
+
MISSION (for context): ${(0, util_1.clip)(meta.mission, 400)}
|
|
180
|
+
|
|
181
|
+
TASK ${task.id}: ${task.title}
|
|
182
|
+
Objective (with success criteria): ${task.objective}
|
|
183
|
+
${task.context ? `Context: ${(0, util_1.clip)(task.context, 600)}` : ""}
|
|
184
|
+
Worker's report:
|
|
185
|
+
${(0, util_1.clip)(task.report ?? "", 2400)}
|
|
186
|
+
${task.artifacts.length ? `Claimed artifacts: ${task.artifacts.join(", ")}` : ""}
|
|
187
|
+
|
|
188
|
+
Working directory: ${meta.cwd}
|
|
189
|
+
|
|
190
|
+
PROTOCOL
|
|
191
|
+
- Do NOT trust the report. Verify concretely with tools: read the files it claims to have written, run the build/tests/commands, fetch the URLs, check the numbers.
|
|
192
|
+
- Check: objective met? success criteria satisfied? deliverables exist and are non-trivial (not stubs/placeholders)?
|
|
193
|
+
- Spot-check depth over exhaustive breadth; ~5-12 tool steps.
|
|
194
|
+
- Then call verdict(pass, feedback). On fail, feedback must be actionable: exactly what is wrong and where. On pass, one line citing the evidence you checked.`;
|
|
195
|
+
}
|
|
196
|
+
exports.VERIFIER_KICKOFF = "Verify now, then call verdict(...).";
|
|
197
|
+
// ============================================================ synthesizer
|
|
198
|
+
function synthSystem(opts) {
|
|
199
|
+
return `You are the synthesis agent for a completed agent-swarm run. Compose the definitive final deliverable for the operator.
|
|
200
|
+
|
|
201
|
+
MISSION
|
|
202
|
+
${opts.meta.mission}
|
|
203
|
+
|
|
204
|
+
RUN OUTCOME: ${opts.reason}
|
|
205
|
+
Conductor's closing notes: ${opts.finishNotes || "(none)"}
|
|
206
|
+
|
|
207
|
+
ALL TASK REPORTS
|
|
208
|
+
${opts.reports}
|
|
209
|
+
|
|
210
|
+
${opts.blackboard ? `BLACKBOARD\n${opts.blackboard}\n` : ""}${opts.artifactList ? `ARTIFACTS ON DISK\n${opts.artifactList}\n` : ""}
|
|
211
|
+
Working directory: ${opts.meta.cwd}
|
|
212
|
+
|
|
213
|
+
PROTOCOL
|
|
214
|
+
- You may read files (read_file / list_dir) to confirm specifics before writing — verify key claims you repeat.
|
|
215
|
+
- Then call submit_final with:
|
|
216
|
+
• report_markdown — the deliverable document. Structure: # title; **Outcome** first (did the mission succeed, headline results); then What was built/found with evidence and exact paths; How to use/run it (if applicable); Open issues & recommended next steps. Write for the operator: complete, concrete, zero filler.
|
|
217
|
+
• summary — ≤8 sentences for the console.
|
|
218
|
+
- The report stands alone: a reader who saw nothing else must understand what happened and where everything is.`;
|
|
219
|
+
}
|
|
220
|
+
exports.SYNTH_KICKOFF = "Compose and submit the final deliverable now via submit_final(...).";
|
|
221
|
+
// ============================================================ compaction
|
|
222
|
+
function compactorPrompt(serialized) {
|
|
223
|
+
return `Compress this agent conversation segment into a dense progress summary the agent can rely on to continue working. Preserve: decisions made, files created/modified (exact paths), commands run and their outcomes, key findings/numbers/URLs, errors hit and how they were resolved, current state of the work, and anything still pending. Omit pleasantries and dead ends unless they prevent repeating a mistake. Output the summary only.
|
|
224
|
+
|
|
225
|
+
SEGMENT
|
|
226
|
+
${serialized}`;
|
|
227
|
+
}
|
|
228
|
+
// ============================================================ misc
|
|
229
|
+
function budgetLine(spent, cap) {
|
|
230
|
+
const pct = cap > 0 ? Math.round((spent.total / cap) * 100) : 0;
|
|
231
|
+
return `BUDGET: ${(0, util_1.fmtTokens)(spent.total)} of ${(0, util_1.fmtTokens)(cap)} tokens used (${pct}%) · est. cost so far $${spent.cost.toFixed(2)}`;
|
|
232
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROVIDER_IDS = exports.PROVIDERS = void 0;
|
|
4
|
+
exports.isProviderId = isProviderId;
|
|
5
|
+
exports.mapEffort = mapEffort;
|
|
6
|
+
exports.PROVIDERS = {
|
|
7
|
+
deepseek: {
|
|
8
|
+
id: "deepseek",
|
|
9
|
+
label: "DeepSeek",
|
|
10
|
+
baseUrl: "https://api.deepseek.com",
|
|
11
|
+
keyRequired: true,
|
|
12
|
+
keyEnv: "DEEPSEEK_API_KEY",
|
|
13
|
+
keyUrl: "https://platform.deepseek.com",
|
|
14
|
+
deepseekThinking: true,
|
|
15
|
+
efforts: ["high", "max"],
|
|
16
|
+
maxTokensParam: "max_tokens",
|
|
17
|
+
defaultModel: "deepseek-v4-flash",
|
|
18
|
+
knownModels: ["deepseek-v4-flash", "deepseek-v4-pro"],
|
|
19
|
+
note: "Cheapest frontier-grade swarm workers; prompt caching makes long runs very cheap.",
|
|
20
|
+
},
|
|
21
|
+
openai: {
|
|
22
|
+
id: "openai",
|
|
23
|
+
label: "OpenAI",
|
|
24
|
+
baseUrl: "https://api.openai.com/v1",
|
|
25
|
+
keyRequired: true,
|
|
26
|
+
keyEnv: "OPENAI_API_KEY",
|
|
27
|
+
keyUrl: "https://platform.openai.com/api-keys",
|
|
28
|
+
deepseekThinking: false,
|
|
29
|
+
efforts: ["low", "medium", "high"],
|
|
30
|
+
maxTokensParam: "max_completion_tokens",
|
|
31
|
+
defaultModel: "gpt-5.1-mini",
|
|
32
|
+
knownModels: ["gpt-5.1", "gpt-5.1-mini", "gpt-4.1", "gpt-4.1-mini"],
|
|
33
|
+
},
|
|
34
|
+
anthropic: {
|
|
35
|
+
id: "anthropic",
|
|
36
|
+
label: "Anthropic",
|
|
37
|
+
baseUrl: "https://api.anthropic.com/v1",
|
|
38
|
+
keyRequired: true,
|
|
39
|
+
keyEnv: "ANTHROPIC_API_KEY",
|
|
40
|
+
keyUrl: "https://console.anthropic.com/settings/keys",
|
|
41
|
+
deepseekThinking: false,
|
|
42
|
+
efforts: [],
|
|
43
|
+
maxTokensParam: "max_tokens",
|
|
44
|
+
defaultModel: "claude-sonnet-4-6",
|
|
45
|
+
knownModels: ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
|
|
46
|
+
note: "Uses Anthropic's OpenAI-compatible endpoint.",
|
|
47
|
+
},
|
|
48
|
+
xai: {
|
|
49
|
+
id: "xai",
|
|
50
|
+
label: "xAI (Grok)",
|
|
51
|
+
baseUrl: "https://api.x.ai/v1",
|
|
52
|
+
keyRequired: true,
|
|
53
|
+
keyEnv: "XAI_API_KEY",
|
|
54
|
+
keyUrl: "https://console.x.ai",
|
|
55
|
+
deepseekThinking: false,
|
|
56
|
+
efforts: [],
|
|
57
|
+
maxTokensParam: "max_tokens",
|
|
58
|
+
defaultModel: "grok-4-fast",
|
|
59
|
+
knownModels: ["grok-4", "grok-4-fast", "grok-3-mini"],
|
|
60
|
+
},
|
|
61
|
+
minimax: {
|
|
62
|
+
id: "minimax",
|
|
63
|
+
label: "MiniMax",
|
|
64
|
+
baseUrl: "https://api.minimax.io/v1",
|
|
65
|
+
keyRequired: true,
|
|
66
|
+
keyEnv: "MINIMAX_API_KEY",
|
|
67
|
+
keyUrl: "https://platform.minimax.io",
|
|
68
|
+
deepseekThinking: false,
|
|
69
|
+
efforts: [],
|
|
70
|
+
maxTokensParam: "max_tokens",
|
|
71
|
+
defaultModel: "MiniMax-M2.1",
|
|
72
|
+
knownModels: ["MiniMax-M2.1", "MiniMax-M2"],
|
|
73
|
+
note: "Coding-plan / token-plan keys work here.",
|
|
74
|
+
},
|
|
75
|
+
openrouter: {
|
|
76
|
+
id: "openrouter",
|
|
77
|
+
label: "OpenRouter",
|
|
78
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
79
|
+
keyRequired: true,
|
|
80
|
+
keyEnv: "OPENROUTER_API_KEY",
|
|
81
|
+
keyUrl: "https://openrouter.ai/keys",
|
|
82
|
+
deepseekThinking: false,
|
|
83
|
+
efforts: [],
|
|
84
|
+
maxTokensParam: "max_tokens",
|
|
85
|
+
defaultModel: "deepseek/deepseek-chat",
|
|
86
|
+
knownModels: ["openrouter/auto", "deepseek/deepseek-chat", "anthropic/claude-sonnet-4.6", "openai/gpt-5.1-mini"],
|
|
87
|
+
note: "One key, every model. Use the full vendor/model id.",
|
|
88
|
+
},
|
|
89
|
+
ollama: {
|
|
90
|
+
id: "ollama",
|
|
91
|
+
label: "Ollama (local)",
|
|
92
|
+
baseUrl: "http://localhost:11434/v1",
|
|
93
|
+
keyRequired: false,
|
|
94
|
+
deepseekThinking: false,
|
|
95
|
+
efforts: [],
|
|
96
|
+
maxTokensParam: "max_tokens",
|
|
97
|
+
defaultModel: "qwen3",
|
|
98
|
+
knownModels: ["qwen3", "llama3.3", "deepseek-r1", "gpt-oss:20b"],
|
|
99
|
+
local: true,
|
|
100
|
+
note: "Free + private. The models list shows what you have pulled.",
|
|
101
|
+
},
|
|
102
|
+
lmstudio: {
|
|
103
|
+
id: "lmstudio",
|
|
104
|
+
label: "LM Studio (local)",
|
|
105
|
+
baseUrl: "http://localhost:1234/v1",
|
|
106
|
+
keyRequired: false,
|
|
107
|
+
deepseekThinking: false,
|
|
108
|
+
efforts: [],
|
|
109
|
+
maxTokensParam: "max_tokens",
|
|
110
|
+
defaultModel: "",
|
|
111
|
+
knownModels: [],
|
|
112
|
+
local: true,
|
|
113
|
+
note: "Start the LM Studio server, then pick a loaded model.",
|
|
114
|
+
},
|
|
115
|
+
custom: {
|
|
116
|
+
id: "custom",
|
|
117
|
+
label: "Custom endpoint",
|
|
118
|
+
baseUrl: "http://localhost:8000/v1",
|
|
119
|
+
keyRequired: false,
|
|
120
|
+
deepseekThinking: false,
|
|
121
|
+
efforts: [],
|
|
122
|
+
maxTokensParam: "max_tokens",
|
|
123
|
+
defaultModel: "",
|
|
124
|
+
knownModels: [],
|
|
125
|
+
note: "Any OpenAI-compatible /chat/completions server (vLLM, llama.cpp, …).",
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
exports.PROVIDER_IDS = Object.keys(exports.PROVIDERS);
|
|
129
|
+
function isProviderId(v) {
|
|
130
|
+
return typeof v === "string" && v in exports.PROVIDERS;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Map the configured effort onto what the provider's API accepts.
|
|
134
|
+
* Returns undefined when the provider has no effort knob (omit the field —
|
|
135
|
+
* unknown body params are hard errors on several APIs).
|
|
136
|
+
*/
|
|
137
|
+
function mapEffort(effort, p) {
|
|
138
|
+
if (!effort || p.efforts.length === 0)
|
|
139
|
+
return undefined;
|
|
140
|
+
if (p.efforts.includes(effort))
|
|
141
|
+
return effort;
|
|
142
|
+
const ladder = ["low", "medium", "high", "max"];
|
|
143
|
+
const want = ladder.indexOf(effort);
|
|
144
|
+
// Nearest supported value, preferring the strongest at or below the ask.
|
|
145
|
+
const supported = [...p.efforts].sort((a, b) => ladder.indexOf(a) - ladder.indexOf(b));
|
|
146
|
+
for (let i = supported.length - 1; i >= 0; i--) {
|
|
147
|
+
if (ladder.indexOf(supported[i]) <= want)
|
|
148
|
+
return supported[i];
|
|
149
|
+
}
|
|
150
|
+
return supported[0];
|
|
151
|
+
}
|