@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/state.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RunState = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
/**
|
|
6
|
+
* Pure reducer over the journal. Both the live executor and the read-only hub
|
|
7
|
+
* build identical state from the same events — the journal is the truth.
|
|
8
|
+
*/
|
|
9
|
+
class RunState {
|
|
10
|
+
meta = null;
|
|
11
|
+
status = "planning";
|
|
12
|
+
statusReason = "";
|
|
13
|
+
tasks = new Map();
|
|
14
|
+
taskOrder = [];
|
|
15
|
+
agents = new Map();
|
|
16
|
+
notes = [];
|
|
17
|
+
conductorLog = [];
|
|
18
|
+
operatorNotes = [];
|
|
19
|
+
usageByModel = new Map();
|
|
20
|
+
totalUsage = { ...types_1.ZERO_USAGE };
|
|
21
|
+
cost = 0;
|
|
22
|
+
finalSummary;
|
|
23
|
+
finalReportPath;
|
|
24
|
+
lastSeq = 0;
|
|
25
|
+
lastT = 0;
|
|
26
|
+
createdAt = 0;
|
|
27
|
+
updatedAt = 0;
|
|
28
|
+
pricing;
|
|
29
|
+
constructor(pricing = {}) {
|
|
30
|
+
this.pricing = pricing;
|
|
31
|
+
}
|
|
32
|
+
apply(ev) {
|
|
33
|
+
this.lastSeq = ev.seq;
|
|
34
|
+
this.lastT = ev.t;
|
|
35
|
+
this.updatedAt = ev.t;
|
|
36
|
+
switch (ev.type) {
|
|
37
|
+
case "run.created": {
|
|
38
|
+
this.meta = ev.meta;
|
|
39
|
+
this.createdAt = this.meta.createdAt;
|
|
40
|
+
if (this.meta.options) {
|
|
41
|
+
// pricing may be passed through meta for the hub
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case "run.status":
|
|
46
|
+
this.status = ev.status;
|
|
47
|
+
if (ev.reason)
|
|
48
|
+
this.statusReason = String(ev.reason);
|
|
49
|
+
break;
|
|
50
|
+
case "run.resumed": {
|
|
51
|
+
// Tasks that were in flight when the engine died re-run from scratch;
|
|
52
|
+
// agents the dead process owned can no longer be running.
|
|
53
|
+
const resets = Array.isArray(ev.resets) ? ev.resets : [];
|
|
54
|
+
for (const id of resets) {
|
|
55
|
+
const t = this.tasks.get(id);
|
|
56
|
+
if (t) {
|
|
57
|
+
t.status = "pending";
|
|
58
|
+
t.startedAt = undefined;
|
|
59
|
+
t.endedAt = undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const a of this.agents.values()) {
|
|
63
|
+
if (a.status === "running") {
|
|
64
|
+
a.status = "done";
|
|
65
|
+
a.endedAt = ev.t;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.statusReason = "";
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case "task.created": {
|
|
72
|
+
const t = ev.task;
|
|
73
|
+
if (!this.tasks.has(t.id))
|
|
74
|
+
this.taskOrder.push(t.id);
|
|
75
|
+
this.tasks.set(t.id, { ...t });
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case "task.status": {
|
|
79
|
+
const t = this.tasks.get(ev.taskId);
|
|
80
|
+
if (t) {
|
|
81
|
+
t.status = ev.status;
|
|
82
|
+
if (typeof ev.attempt === "number")
|
|
83
|
+
t.attempt = ev.attempt;
|
|
84
|
+
if (ev.status === "running" && !t.startedAt)
|
|
85
|
+
t.startedAt = ev.t;
|
|
86
|
+
if (["done", "failed", "blocked"].includes(String(ev.status)))
|
|
87
|
+
t.endedAt = ev.t;
|
|
88
|
+
if (ev.reason)
|
|
89
|
+
t.error = String(ev.reason);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case "task.report": {
|
|
94
|
+
const t = this.tasks.get(ev.taskId);
|
|
95
|
+
if (t) {
|
|
96
|
+
t.report = ev.report;
|
|
97
|
+
t.reportStatus = ev.status;
|
|
98
|
+
t.artifacts = ev.artifacts ?? t.artifacts;
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "verify.result": {
|
|
103
|
+
const t = this.tasks.get(ev.taskId);
|
|
104
|
+
if (t)
|
|
105
|
+
t.feedback = ev.feedback;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case "agent.spawned":
|
|
109
|
+
this.agents.set(ev.agentId, {
|
|
110
|
+
id: ev.agentId,
|
|
111
|
+
taskId: ev.taskId,
|
|
112
|
+
role: ev.role ?? "agent",
|
|
113
|
+
model: ev.model ?? "",
|
|
114
|
+
purpose: ev.purpose ?? "",
|
|
115
|
+
status: "running",
|
|
116
|
+
steps: 0,
|
|
117
|
+
startedAt: ev.t,
|
|
118
|
+
lastText: "",
|
|
119
|
+
lastThink: "",
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
case "agent.delta": {
|
|
123
|
+
const a = this.agents.get(ev.agentId);
|
|
124
|
+
if (a) {
|
|
125
|
+
if (ev.channel === "text")
|
|
126
|
+
a.lastText = clipTail(a.lastText + ev.text, 4000);
|
|
127
|
+
else
|
|
128
|
+
a.lastThink = clipTail(a.lastThink + ev.text, 4000);
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case "agent.done": {
|
|
133
|
+
const a = this.agents.get(ev.agentId);
|
|
134
|
+
if (a) {
|
|
135
|
+
a.status = "done";
|
|
136
|
+
a.endedAt = ev.t;
|
|
137
|
+
a.steps = ev.steps ?? a.steps;
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case "tool.call": {
|
|
142
|
+
const a = this.agents.get(ev.agentId);
|
|
143
|
+
if (a) {
|
|
144
|
+
a.lastTool = ev.name;
|
|
145
|
+
a.steps++;
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "note.added":
|
|
150
|
+
this.notes.push({
|
|
151
|
+
t: ev.t,
|
|
152
|
+
taskId: ev.taskId,
|
|
153
|
+
agentId: ev.agentId,
|
|
154
|
+
key: ev.key,
|
|
155
|
+
text: ev.text,
|
|
156
|
+
});
|
|
157
|
+
// Reduced state is held live by the hub and the resume seed — keep
|
|
158
|
+
// only the tail that digests/views actually use.
|
|
159
|
+
if (this.notes.length > 1000)
|
|
160
|
+
this.notes.splice(0, this.notes.length - 1000);
|
|
161
|
+
break;
|
|
162
|
+
case "conductor.say":
|
|
163
|
+
this.conductorLog.push({ t: ev.t, text: ev.text });
|
|
164
|
+
if (this.conductorLog.length > 300)
|
|
165
|
+
this.conductorLog.splice(0, this.conductorLog.length - 300);
|
|
166
|
+
break;
|
|
167
|
+
case "operator.note":
|
|
168
|
+
this.operatorNotes.push({ t: ev.t, text: ev.text, consumed: false });
|
|
169
|
+
break;
|
|
170
|
+
case "operator.note.consumed": {
|
|
171
|
+
const idx = this.operatorNotes.findIndex((n) => !n.consumed);
|
|
172
|
+
if (idx >= 0)
|
|
173
|
+
this.operatorNotes[idx].consumed = true;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case "usage": {
|
|
177
|
+
const u = ev.usage;
|
|
178
|
+
const model = ev.model ?? "unknown";
|
|
179
|
+
this.usageByModel.set(model, (0, types_1.addUsage)(this.usageByModel.get(model) ?? { ...types_1.ZERO_USAGE }, u));
|
|
180
|
+
this.totalUsage = (0, types_1.addUsage)(this.totalUsage, u);
|
|
181
|
+
this.cost += (0, types_1.usageCost)(u, this.pricing[model]);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "run.final":
|
|
185
|
+
this.finalSummary = ev.summary;
|
|
186
|
+
this.finalReportPath = ev.reportPath;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
taskList() {
|
|
191
|
+
return this.taskOrder.map((id) => this.tasks.get(id)).filter(Boolean);
|
|
192
|
+
}
|
|
193
|
+
activeAgents() {
|
|
194
|
+
return [...this.agents.values()].filter((a) => a.status === "running");
|
|
195
|
+
}
|
|
196
|
+
pendingOperatorNotes() {
|
|
197
|
+
return this.operatorNotes.filter((n) => !n.consumed).map((n) => n.text);
|
|
198
|
+
}
|
|
199
|
+
summary() {
|
|
200
|
+
const tasks = this.taskList();
|
|
201
|
+
const count = (s) => tasks.filter((t) => t.status === s).length;
|
|
202
|
+
return {
|
|
203
|
+
id: this.meta?.id ?? "",
|
|
204
|
+
mission: this.meta?.mission ?? "",
|
|
205
|
+
status: this.status,
|
|
206
|
+
statusReason: this.statusReason || undefined,
|
|
207
|
+
createdAt: this.createdAt,
|
|
208
|
+
updatedAt: this.updatedAt,
|
|
209
|
+
heartbeatAt: this.lastT,
|
|
210
|
+
pid: null,
|
|
211
|
+
model: this.meta?.options.model ?? "",
|
|
212
|
+
tasks: {
|
|
213
|
+
total: tasks.length,
|
|
214
|
+
done: count("done"),
|
|
215
|
+
failed: count("failed"),
|
|
216
|
+
running: count("running") + count("verifying"),
|
|
217
|
+
pending: count("pending"),
|
|
218
|
+
blocked: count("blocked"),
|
|
219
|
+
},
|
|
220
|
+
agentsActive: this.activeAgents().length,
|
|
221
|
+
usage: this.totalUsage,
|
|
222
|
+
cost: this.cost,
|
|
223
|
+
finalSummary: this.finalSummary,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.RunState = RunState;
|
|
228
|
+
function clipTail(s, max) {
|
|
229
|
+
return s.length <= max ? s : s.slice(s.length - max);
|
|
230
|
+
}
|
package/dist/terminal.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TerminalRenderer = void 0;
|
|
4
|
+
exports.watchRun = watchRun;
|
|
5
|
+
const journal_1 = require("./journal");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
const state_1 = require("./state");
|
|
8
|
+
const util_1 = require("./util");
|
|
9
|
+
const STATUS_STYLE = {
|
|
10
|
+
pending: util_1.ansi.gray,
|
|
11
|
+
running: util_1.ansi.cyan,
|
|
12
|
+
verifying: util_1.ansi.magenta,
|
|
13
|
+
done: util_1.ansi.green,
|
|
14
|
+
failed: util_1.ansi.red,
|
|
15
|
+
blocked: util_1.ansi.yellow,
|
|
16
|
+
};
|
|
17
|
+
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
18
|
+
class TerminalRenderer {
|
|
19
|
+
state;
|
|
20
|
+
frame = 0;
|
|
21
|
+
timer = null;
|
|
22
|
+
active = false;
|
|
23
|
+
tty;
|
|
24
|
+
height = 40;
|
|
25
|
+
width = 100;
|
|
26
|
+
constructor(pricing) {
|
|
27
|
+
this.state = new state_1.RunState(pricing);
|
|
28
|
+
this.tty = Boolean(process.stdout.isTTY);
|
|
29
|
+
}
|
|
30
|
+
ingest(ev) {
|
|
31
|
+
this.state.apply(ev);
|
|
32
|
+
if (!this.tty)
|
|
33
|
+
this.streamLine(ev);
|
|
34
|
+
}
|
|
35
|
+
start() {
|
|
36
|
+
if (!this.tty) {
|
|
37
|
+
process.stdout.write("agentswarm: streaming (no TTY)\n");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.active = true;
|
|
41
|
+
process.stdout.write("\x1b[?25l"); // hide cursor
|
|
42
|
+
this.timer = setInterval(() => this.render(), 250);
|
|
43
|
+
}
|
|
44
|
+
stop() {
|
|
45
|
+
if (this.timer)
|
|
46
|
+
clearInterval(this.timer);
|
|
47
|
+
this.timer = null;
|
|
48
|
+
if (this.tty && this.active) {
|
|
49
|
+
this.render();
|
|
50
|
+
process.stdout.write("\x1b[?25h"); // show cursor
|
|
51
|
+
}
|
|
52
|
+
this.active = false;
|
|
53
|
+
}
|
|
54
|
+
getState() {
|
|
55
|
+
return this.state;
|
|
56
|
+
}
|
|
57
|
+
// ---------- non-tty streaming ----------
|
|
58
|
+
streamLine(ev) {
|
|
59
|
+
let line = "";
|
|
60
|
+
switch (ev.type) {
|
|
61
|
+
case "run.status":
|
|
62
|
+
line = `[run] ${ev.status}${ev.reason ? ` — ${ev.reason}` : ""}`;
|
|
63
|
+
break;
|
|
64
|
+
case "task.created":
|
|
65
|
+
line = `[task+] ${ev.task.id} ${ev.task.title}`;
|
|
66
|
+
break;
|
|
67
|
+
case "task.status":
|
|
68
|
+
line = `[task] ${ev.taskId} → ${ev.status}${ev.reason ? ` (${(0, util_1.oneLine)(String(ev.reason), 80)})` : ""}`;
|
|
69
|
+
break;
|
|
70
|
+
case "agent.spawned":
|
|
71
|
+
line = `[agent+] ${ev.agentId} (${ev.role}) ${(0, util_1.oneLine)(String(ev.purpose ?? ""), 60)}`;
|
|
72
|
+
break;
|
|
73
|
+
case "tool.call":
|
|
74
|
+
line = ` ${ev.agentId} · ${ev.name}`;
|
|
75
|
+
break;
|
|
76
|
+
case "conductor.action":
|
|
77
|
+
line = `[conductor] ${ev.kind}`;
|
|
78
|
+
break;
|
|
79
|
+
case "note.added":
|
|
80
|
+
line = `[note] ${(0, util_1.oneLine)(String(ev.text), 100)}`;
|
|
81
|
+
break;
|
|
82
|
+
case "operator.note":
|
|
83
|
+
line = `[operator] ${(0, util_1.oneLine)(String(ev.text), 100)}`;
|
|
84
|
+
break;
|
|
85
|
+
case "run.final":
|
|
86
|
+
line = `[final] ${(0, util_1.oneLine)(String(ev.summary), 200)}`;
|
|
87
|
+
break;
|
|
88
|
+
case "log":
|
|
89
|
+
if (ev.level !== "info")
|
|
90
|
+
line = `[${ev.level}] ${ev.msg}`;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
if (line)
|
|
94
|
+
process.stdout.write(line + "\n");
|
|
95
|
+
}
|
|
96
|
+
// ---------- tty dashboard ----------
|
|
97
|
+
render() {
|
|
98
|
+
if (!this.tty)
|
|
99
|
+
return;
|
|
100
|
+
this.frame++;
|
|
101
|
+
this.height = process.stdout.rows || 40;
|
|
102
|
+
this.width = Math.min(process.stdout.columns || 100, 120);
|
|
103
|
+
const lines = this.compose();
|
|
104
|
+
const clipped = lines.slice(0, this.height - 1);
|
|
105
|
+
let out = "\x1b[H"; // cursor home
|
|
106
|
+
out += clipped.map((l) => l + "\x1b[K").join("\n"); // each line clears to EOL
|
|
107
|
+
out += "\x1b[J"; // clear below
|
|
108
|
+
process.stdout.write(out);
|
|
109
|
+
}
|
|
110
|
+
compose() {
|
|
111
|
+
const s = this.state;
|
|
112
|
+
const spin = SPINNER[this.frame % SPINNER.length];
|
|
113
|
+
const L = [];
|
|
114
|
+
const W = this.width;
|
|
115
|
+
const live = ["planning", "running", "synthesizing"].includes(s.status);
|
|
116
|
+
const statusIcon = live ? util_1.ansi.cyan(spin) : s.status === "done" ? util_1.ansi.green("●") : util_1.ansi.red("●");
|
|
117
|
+
L.push(`${statusIcon} ${util_1.ansi.bold("agentswarm")} ${util_1.ansi.gray("·")} ${statusColor(s.status)} ${util_1.ansi.gray("·")} ${util_1.ansi.gray(s.meta?.id ?? "")}`);
|
|
118
|
+
L.push(util_1.ansi.gray(" mission: ") + (0, util_1.clip)((0, util_1.oneLine)(s.meta?.mission ?? "", W - 12), W - 12));
|
|
119
|
+
// budget bar
|
|
120
|
+
const cap = s.meta?.options.maxTokens ?? 1;
|
|
121
|
+
const pct = Math.min(100, Math.round((s.totalUsage.promptTokens + s.totalUsage.completionTokens) / cap * 100));
|
|
122
|
+
const barW = Math.max(10, Math.min(40, W - 50));
|
|
123
|
+
const filled = Math.round((pct / 100) * barW);
|
|
124
|
+
const bar = util_1.ansi.cyan("█".repeat(filled)) + util_1.ansi.gray("░".repeat(barW - filled));
|
|
125
|
+
const spent = s.totalUsage.promptTokens + s.totalUsage.completionTokens;
|
|
126
|
+
L.push(` ${bar} ${pct}% ${util_1.ansi.gray((0, util_1.fmtTokens)(spent) + "/" + (0, util_1.fmtTokens)(cap) + " tok")} ${util_1.ansi.green((0, util_1.fmtMoney)(s.cost))}`);
|
|
127
|
+
const tasks = s.taskList();
|
|
128
|
+
const counts = {
|
|
129
|
+
done: tasks.filter((t) => t.status === "done").length,
|
|
130
|
+
running: tasks.filter((t) => t.status === "running" || t.status === "verifying").length,
|
|
131
|
+
pending: tasks.filter((t) => t.status === "pending").length,
|
|
132
|
+
failed: tasks.filter((t) => t.status === "failed").length,
|
|
133
|
+
blocked: tasks.filter((t) => t.status === "blocked").length,
|
|
134
|
+
};
|
|
135
|
+
L.push(util_1.ansi.gray(" ") +
|
|
136
|
+
`${util_1.ansi.green(counts.done + " done")} ${util_1.ansi.cyan(counts.running + " running")} ${util_1.ansi.gray(counts.pending + " pending")} ${counts.failed ? util_1.ansi.red(counts.failed + " failed") : util_1.ansi.gray("0 failed")} ${counts.blocked ? util_1.ansi.yellow(counts.blocked + " blocked") : util_1.ansi.gray("0 blocked")}`);
|
|
137
|
+
L.push("");
|
|
138
|
+
// active agents
|
|
139
|
+
const agents = s.activeAgents();
|
|
140
|
+
if (agents.length) {
|
|
141
|
+
L.push(util_1.ansi.bold(` Active agents (${agents.length})`));
|
|
142
|
+
for (const a of agents.slice(0, 8)) {
|
|
143
|
+
L.push(this.agentLine(a, spin, W));
|
|
144
|
+
}
|
|
145
|
+
if (agents.length > 8)
|
|
146
|
+
L.push(util_1.ansi.gray(` …and ${agents.length - 8} more`));
|
|
147
|
+
L.push("");
|
|
148
|
+
}
|
|
149
|
+
// task table
|
|
150
|
+
L.push(util_1.ansi.bold(" Tasks"));
|
|
151
|
+
const visibleTasks = this.pickTasks(tasks, Math.max(6, this.height - L.length - 10));
|
|
152
|
+
for (const t of visibleTasks) {
|
|
153
|
+
L.push(this.taskLine(t, spin, W));
|
|
154
|
+
}
|
|
155
|
+
if (tasks.length > visibleTasks.length) {
|
|
156
|
+
L.push(util_1.ansi.gray(` …${tasks.length - visibleTasks.length} more tasks`));
|
|
157
|
+
}
|
|
158
|
+
// conductor latest
|
|
159
|
+
const lastSay = s.conductorLog[s.conductorLog.length - 1];
|
|
160
|
+
if (lastSay) {
|
|
161
|
+
L.push("");
|
|
162
|
+
L.push(util_1.ansi.bold(" Conductor"));
|
|
163
|
+
for (const ln of wrap((0, util_1.oneLine)(lastSay.text, 600), W - 6).slice(0, 3)) {
|
|
164
|
+
L.push(util_1.ansi.gray(" ") + util_1.ansi.italic(ln));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// notes
|
|
168
|
+
if (s.notes.length) {
|
|
169
|
+
L.push("");
|
|
170
|
+
L.push(util_1.ansi.bold(` Blackboard (${s.notes.length})`));
|
|
171
|
+
for (const n of s.notes.slice(-3)) {
|
|
172
|
+
L.push(util_1.ansi.gray(" • ") + (0, util_1.clip)((0, util_1.oneLine)((n.key ? `[${n.key}] ` : "") + n.text, W - 8), W - 8));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// operator hint
|
|
176
|
+
if (live) {
|
|
177
|
+
L.push("");
|
|
178
|
+
L.push(util_1.ansi.gray(" Ctrl-C to detach (run keeps going) · ") + util_1.ansi.gray(`swarm note ${s.meta?.id} "…" to steer · swarm cancel ${s.meta?.id}`));
|
|
179
|
+
}
|
|
180
|
+
else if (s.finalSummary) {
|
|
181
|
+
L.push("");
|
|
182
|
+
L.push(util_1.ansi.bold(util_1.ansi.green(" ✓ Final summary")));
|
|
183
|
+
for (const ln of wrap(s.finalSummary, W - 6).slice(0, 6))
|
|
184
|
+
L.push(" " + ln);
|
|
185
|
+
if (s.finalReportPath)
|
|
186
|
+
L.push(util_1.ansi.gray(" report: ") + s.finalReportPath);
|
|
187
|
+
}
|
|
188
|
+
return L;
|
|
189
|
+
}
|
|
190
|
+
agentLine(a, spin, W) {
|
|
191
|
+
const role = util_1.ansi.magenta(`${a.role}`);
|
|
192
|
+
const head = ` ${util_1.ansi.cyan(spin)} ${util_1.ansi.gray(a.taskId)} ${role} ${util_1.ansi.gray("·")} `;
|
|
193
|
+
const tool = a.lastTool ? util_1.ansi.yellow(a.lastTool) + " " : "";
|
|
194
|
+
const txt = (0, util_1.oneLine)(a.lastText || a.lastThink || a.purpose, W - 40);
|
|
195
|
+
return (0, util_1.clip)(head + tool + util_1.ansi.gray(txt), W + 40); // +40 accounts for ansi codes roughly
|
|
196
|
+
}
|
|
197
|
+
taskLine(t, spin, W) {
|
|
198
|
+
const style = STATUS_STYLE[t.status] ?? util_1.ansi.white;
|
|
199
|
+
const icon = t.status === "running" || t.status === "verifying"
|
|
200
|
+
? util_1.ansi.cyan(spin)
|
|
201
|
+
: t.status === "done"
|
|
202
|
+
? util_1.ansi.green("✓")
|
|
203
|
+
: t.status === "failed"
|
|
204
|
+
? util_1.ansi.red("✗")
|
|
205
|
+
: t.status === "blocked"
|
|
206
|
+
? util_1.ansi.yellow("⊘")
|
|
207
|
+
: util_1.ansi.gray("○");
|
|
208
|
+
const id = style(t.id.padEnd(4));
|
|
209
|
+
const role = util_1.ansi.gray(`(${t.role})`);
|
|
210
|
+
const deps = t.deps.length ? util_1.ansi.gray(` ⇠${t.deps.join(",")}`) : "";
|
|
211
|
+
const v = t.verify ? util_1.ansi.magenta(" ⊛") : "";
|
|
212
|
+
const dur = t.startedAt ? util_1.ansi.gray(" " + (0, util_1.fmtDur)((t.endedAt ?? Date.now()) - t.startedAt)) : "";
|
|
213
|
+
const title = (0, util_1.clip)(t.title, Math.max(20, W - 38));
|
|
214
|
+
return ` ${icon} ${id} ${title} ${role}${deps}${v}${dur}`;
|
|
215
|
+
}
|
|
216
|
+
pickTasks(tasks, budget) {
|
|
217
|
+
if (tasks.length <= budget)
|
|
218
|
+
return tasks;
|
|
219
|
+
// Prioritize active + recently settled.
|
|
220
|
+
const active = tasks.filter((t) => ["running", "verifying", "pending"].includes(t.status));
|
|
221
|
+
const settled = tasks.filter((t) => ["done", "failed", "blocked"].includes(t.status));
|
|
222
|
+
const keepSettled = Math.max(0, budget - active.length);
|
|
223
|
+
return [...settled.slice(-keepSettled), ...active].slice(-budget);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.TerminalRenderer = TerminalRenderer;
|
|
227
|
+
function statusColor(s) {
|
|
228
|
+
switch (s) {
|
|
229
|
+
case "done":
|
|
230
|
+
return util_1.ansi.green(util_1.ansi.bold("done"));
|
|
231
|
+
case "failed":
|
|
232
|
+
return util_1.ansi.red(util_1.ansi.bold("failed"));
|
|
233
|
+
case "cancelled":
|
|
234
|
+
return util_1.ansi.yellow("cancelled");
|
|
235
|
+
case "synthesizing":
|
|
236
|
+
return util_1.ansi.magenta("synthesizing");
|
|
237
|
+
case "running":
|
|
238
|
+
return util_1.ansi.cyan("running");
|
|
239
|
+
case "planning":
|
|
240
|
+
return util_1.ansi.cyan("planning");
|
|
241
|
+
default:
|
|
242
|
+
return s;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function wrap(text, width) {
|
|
246
|
+
const words = text.split(/\s+/);
|
|
247
|
+
const lines = [];
|
|
248
|
+
let cur = "";
|
|
249
|
+
for (const w of words) {
|
|
250
|
+
if ((cur + " " + w).trim().length > width) {
|
|
251
|
+
if (cur)
|
|
252
|
+
lines.push(cur);
|
|
253
|
+
cur = w;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
cur = (cur + " " + w).trim();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (cur)
|
|
260
|
+
lines.push(cur);
|
|
261
|
+
return lines;
|
|
262
|
+
}
|
|
263
|
+
/** Tail a run's journal into a renderer until it goes terminal (for `watch`). */
|
|
264
|
+
async function watchRun(id, pricing) {
|
|
265
|
+
const renderer = new TerminalRenderer(pricing);
|
|
266
|
+
const file = (0, journal_1.eventsFile)((0, config_1.runDir)(id));
|
|
267
|
+
const tail = { offset: 0, carry: "" };
|
|
268
|
+
renderer.start();
|
|
269
|
+
return new Promise((resolve) => {
|
|
270
|
+
const tick = () => {
|
|
271
|
+
let evs = [];
|
|
272
|
+
try {
|
|
273
|
+
evs = (0, journal_1.readNewEvents)(file, tail);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
/* file not ready */
|
|
277
|
+
}
|
|
278
|
+
for (const ev of evs)
|
|
279
|
+
renderer.ingest(ev);
|
|
280
|
+
const st = renderer.getState().status;
|
|
281
|
+
if (["done", "failed", "cancelled"].includes(st)) {
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
// one last drain
|
|
284
|
+
try {
|
|
285
|
+
for (const ev of (0, journal_1.readNewEvents)(file, tail))
|
|
286
|
+
renderer.ingest(ev);
|
|
287
|
+
}
|
|
288
|
+
catch { /* ignore */ }
|
|
289
|
+
renderer.stop();
|
|
290
|
+
resolve();
|
|
291
|
+
}, 500);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
setTimeout(tick, 300);
|
|
295
|
+
};
|
|
296
|
+
tick();
|
|
297
|
+
});
|
|
298
|
+
}
|