@wrongstack/cli 0.6.7 → 0.7.2
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/dist/index.js +736 -79
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
package/dist/index.js
CHANGED
|
@@ -3,11 +3,11 @@ import * as path23 from 'path';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import * as fsp2 from 'fs/promises';
|
|
5
5
|
import { readdir, readFile } from 'fs/promises';
|
|
6
|
-
import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director,
|
|
6
|
+
import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, AutoApprovePermissionPolicy, makeLLMClassifier, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, makeAgentSubagentRunner, NULL_FLEET_BUS, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, AGENTS_BY_PHASE, dispatchAgent, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, renderProgress, renderTaskGraph, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, InputBuilder, FsError, ERROR_CODES, projectHash, WrongStackError, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1, SpecVersioning, ParallelEternalEngine, allServers as allServers$1 } from '@wrongstack/core';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
import * as os6 from 'os';
|
|
9
9
|
import os6__default from 'os';
|
|
10
|
-
import * as
|
|
10
|
+
import * as crypto2 from 'crypto';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
12
|
import { DefaultSecretVault as DefaultSecretVault$1, encryptConfigSecrets, decryptConfigSecrets as decryptConfigSecrets$1 } from '@wrongstack/core/security';
|
|
13
13
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
@@ -18,6 +18,9 @@ import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
|
|
|
18
18
|
import * as readline from 'readline';
|
|
19
19
|
import { spawn } from 'child_process';
|
|
20
20
|
import { SkillInstaller } from '@wrongstack/core/skills';
|
|
21
|
+
import { WrongStackACPServer } from '@wrongstack/acp/agent';
|
|
22
|
+
import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
|
|
23
|
+
import { ACP_AGENTS, SubagentBudget } from '@wrongstack/core/coordination';
|
|
21
24
|
import { allServers } from '@wrongstack/core/infrastructure';
|
|
22
25
|
import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
|
|
23
26
|
import { ToolExecutor } from '@wrongstack/core/execution';
|
|
@@ -1244,12 +1247,12 @@ ${lines.join("\n")}`
|
|
|
1244
1247
|
if (analysis.bottlenecks.length > 0) {
|
|
1245
1248
|
lines.push("");
|
|
1246
1249
|
lines.push(` \u{1F6AB} Bottlenecks (blocking most downstream):`);
|
|
1247
|
-
analysis.bottlenecks
|
|
1250
|
+
for (const bt of analysis.bottlenecks) {
|
|
1248
1251
|
const node = graph.nodes.get(bt.taskId);
|
|
1249
1252
|
if (node) {
|
|
1250
1253
|
lines.push(` \u2022 ${node.title} (blocks ${bt.blockedCount} task(s))`);
|
|
1251
1254
|
}
|
|
1252
|
-
}
|
|
1255
|
+
}
|
|
1253
1256
|
}
|
|
1254
1257
|
if (analysis.parallelGroups.length > 0) {
|
|
1255
1258
|
lines.push("");
|
|
@@ -1262,12 +1265,12 @@ ${lines.join("\n")}`
|
|
|
1262
1265
|
if (analysis.readyTasks.length > 0) {
|
|
1263
1266
|
lines.push("");
|
|
1264
1267
|
lines.push(` \u2705 Ready to start now:`);
|
|
1265
|
-
analysis.readyTasks
|
|
1268
|
+
for (const taskId of analysis.readyTasks) {
|
|
1266
1269
|
const node = graph.nodes.get(taskId);
|
|
1267
1270
|
if (node) {
|
|
1268
1271
|
lines.push(` \u2022 ${node.title}`);
|
|
1269
1272
|
}
|
|
1270
|
-
}
|
|
1273
|
+
}
|
|
1271
1274
|
}
|
|
1272
1275
|
lines.push(`\u2570${"\u2500".repeat(55)}\u256F`);
|
|
1273
1276
|
return { message: lines.join("\n") };
|
|
@@ -1468,7 +1471,10 @@ var init_sdd = __esm({
|
|
|
1468
1471
|
return Date.now() - this.phaseStartTime;
|
|
1469
1472
|
}
|
|
1470
1473
|
getVersioning() {
|
|
1471
|
-
|
|
1474
|
+
if (this.versioning === null) {
|
|
1475
|
+
this.versioning = new SpecVersioning();
|
|
1476
|
+
}
|
|
1477
|
+
return this.versioning;
|
|
1472
1478
|
}
|
|
1473
1479
|
clearTaskState() {
|
|
1474
1480
|
this.taskStore = null;
|
|
@@ -1554,7 +1560,7 @@ function currentVersion() {
|
|
|
1554
1560
|
return "dev";
|
|
1555
1561
|
}
|
|
1556
1562
|
function isNewer(a, b) {
|
|
1557
|
-
const parse = (v) => v.replace(/^v/i, "").split(".").map((p) => parseInt(p, 10) || 0);
|
|
1563
|
+
const parse = (v) => v.replace(/^v/i, "").split(".").map((p) => Number.parseInt(p, 10) || 0);
|
|
1558
1564
|
const [ap, bp] = [parse(a), parse(b)];
|
|
1559
1565
|
for (let i = 0; i < Math.max(ap.length, bp.length); i++) {
|
|
1560
1566
|
const ai = ap[i] ?? 0;
|
|
@@ -1663,7 +1669,7 @@ async function runWebUI(opts) {
|
|
|
1663
1669
|
const port = opts.port ?? 3457;
|
|
1664
1670
|
const clients = /* @__PURE__ */ new Map();
|
|
1665
1671
|
let abortController = null;
|
|
1666
|
-
const authToken =
|
|
1672
|
+
const authToken = crypto2.randomBytes(16).toString("hex");
|
|
1667
1673
|
const wss = new WebSocketServer({ port, host: "127.0.0.1", maxPayload: 1 * 1024 * 1024 });
|
|
1668
1674
|
console.log(`[WebUI] WebSocket server starting on ws://127.0.0.1:${port}`);
|
|
1669
1675
|
const eventUnsubscribers = [];
|
|
@@ -2244,6 +2250,35 @@ ${diff}`;
|
|
|
2244
2250
|
}
|
|
2245
2251
|
return "chore: update";
|
|
2246
2252
|
}
|
|
2253
|
+
function makeProviderClassifier(provider, model) {
|
|
2254
|
+
return makeLLMClassifier(async (prompt) => {
|
|
2255
|
+
const ctrl = new AbortController();
|
|
2256
|
+
const timeout = setTimeout(() => ctrl.abort(), 15e3);
|
|
2257
|
+
try {
|
|
2258
|
+
const resp = await provider.complete(
|
|
2259
|
+
{
|
|
2260
|
+
model,
|
|
2261
|
+
system: [
|
|
2262
|
+
{
|
|
2263
|
+
type: "text",
|
|
2264
|
+
text: 'You are an agent router. Choose the single best agent for the task. Reply with ONLY a compact JSON object {"role":"...","reason":"..."}.'
|
|
2265
|
+
}
|
|
2266
|
+
],
|
|
2267
|
+
messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
|
|
2268
|
+
maxTokens: 120,
|
|
2269
|
+
temperature: 0
|
|
2270
|
+
},
|
|
2271
|
+
{ signal: ctrl.signal }
|
|
2272
|
+
);
|
|
2273
|
+
const content = resp.content;
|
|
2274
|
+
return Array.isArray(content) ? content[0]?.text ?? "" : "";
|
|
2275
|
+
} catch {
|
|
2276
|
+
return "";
|
|
2277
|
+
} finally {
|
|
2278
|
+
clearTimeout(timeout);
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2247
2282
|
|
|
2248
2283
|
// src/arg-parser.ts
|
|
2249
2284
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -2753,7 +2788,16 @@ function entryId(ts) {
|
|
|
2753
2788
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
2754
2789
|
}
|
|
2755
2790
|
async function ensureHistoryDir(homeFn = defaultHomeDir) {
|
|
2756
|
-
|
|
2791
|
+
try {
|
|
2792
|
+
await fsp2.mkdir(historyDir(homeFn), { recursive: true });
|
|
2793
|
+
} catch (err) {
|
|
2794
|
+
throw new FsError({
|
|
2795
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2796
|
+
code: ERROR_CODES.FS_MKDIR_FAILED,
|
|
2797
|
+
path: historyDir(homeFn),
|
|
2798
|
+
cause: err
|
|
2799
|
+
});
|
|
2800
|
+
}
|
|
2757
2801
|
}
|
|
2758
2802
|
async function readIndex(homeFn = defaultHomeDir) {
|
|
2759
2803
|
try {
|
|
@@ -2765,7 +2809,16 @@ async function readIndex(homeFn = defaultHomeDir) {
|
|
|
2765
2809
|
}
|
|
2766
2810
|
async function writeIndex(idx, homeFn = defaultHomeDir) {
|
|
2767
2811
|
await ensureHistoryDir(homeFn);
|
|
2768
|
-
|
|
2812
|
+
try {
|
|
2813
|
+
await atomicWrite(historyIndexPath(homeFn), JSON.stringify(idx, null, 2));
|
|
2814
|
+
} catch (err) {
|
|
2815
|
+
throw new FsError({
|
|
2816
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2817
|
+
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
2818
|
+
path: historyIndexPath(homeFn),
|
|
2819
|
+
cause: err
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2769
2822
|
}
|
|
2770
2823
|
async function backupCurrent(homeFn = defaultHomeDir) {
|
|
2771
2824
|
const cfg = configPath(homeFn);
|
|
@@ -2810,11 +2863,20 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
2810
2863
|
snapshotMasked: maskConfigSecrets(newCfg),
|
|
2811
2864
|
diffSummary: diffSummary(oldCfg, newCfg)
|
|
2812
2865
|
};
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2866
|
+
try {
|
|
2867
|
+
await fsp2.writeFile(
|
|
2868
|
+
path23.join(historyDir(homeFn), `${id}.json`),
|
|
2869
|
+
JSON.stringify(entry, null, 2),
|
|
2870
|
+
"utf8"
|
|
2871
|
+
);
|
|
2872
|
+
} catch (err) {
|
|
2873
|
+
throw new FsError({
|
|
2874
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2875
|
+
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
2876
|
+
path: path23.join(historyDir(homeFn), `${id}.json`),
|
|
2877
|
+
cause: err
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2818
2880
|
const idx = await readIndex(homeFn);
|
|
2819
2881
|
idx.entries.unshift({ id, timestamp, description });
|
|
2820
2882
|
await writeIndex(idx, homeFn);
|
|
@@ -3476,17 +3538,41 @@ function buildClearCommand(opts) {
|
|
|
3476
3538
|
};
|
|
3477
3539
|
}
|
|
3478
3540
|
async function runGit(args, cwd) {
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3541
|
+
try {
|
|
3542
|
+
return await new Promise((resolve4, reject) => {
|
|
3543
|
+
const child = spawn("git", args, {
|
|
3544
|
+
cwd,
|
|
3545
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3546
|
+
});
|
|
3547
|
+
let stdout = "";
|
|
3548
|
+
let stderr = "";
|
|
3549
|
+
child.stdout?.on("data", (d) => {
|
|
3550
|
+
stdout += d;
|
|
3551
|
+
});
|
|
3552
|
+
child.stderr?.on("data", (d) => {
|
|
3553
|
+
stderr += d;
|
|
3554
|
+
});
|
|
3555
|
+
child.on("error", (err) => {
|
|
3556
|
+
reject(new WrongStackError({
|
|
3557
|
+
message: `Failed to run git: ${err.message}`,
|
|
3558
|
+
code: ERROR_CODES.TOOL_EXECUTION_FAILED,
|
|
3559
|
+
subsystem: "tool",
|
|
3560
|
+
context: { command: "git", args, cwd },
|
|
3561
|
+
cause: err
|
|
3562
|
+
}));
|
|
3563
|
+
});
|
|
3564
|
+
child.on("close", (code) => resolve4({ stdout, stderr, code: code ?? 0 }));
|
|
3483
3565
|
});
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3566
|
+
} catch (err) {
|
|
3567
|
+
if (err instanceof WrongStackError) throw err;
|
|
3568
|
+
throw new WrongStackError({
|
|
3569
|
+
message: err instanceof Error ? err.message : String(err),
|
|
3570
|
+
code: ERROR_CODES.TOOL_EXECUTION_FAILED,
|
|
3571
|
+
subsystem: "tool",
|
|
3572
|
+
context: { command: "git", args, cwd },
|
|
3573
|
+
cause: err
|
|
3574
|
+
});
|
|
3575
|
+
}
|
|
3490
3576
|
}
|
|
3491
3577
|
function detectCommitType(stats) {
|
|
3492
3578
|
const lines = stats.split("\n");
|
|
@@ -3799,6 +3885,17 @@ function buildStatsCommand(opts) {
|
|
|
3799
3885
|
}
|
|
3800
3886
|
};
|
|
3801
3887
|
}
|
|
3888
|
+
var PHASE_ORDER = [
|
|
3889
|
+
{ phase: "discovery", label: "1 \xB7 Discovery" },
|
|
3890
|
+
{ phase: "planning", label: "2 \xB7 Planning" },
|
|
3891
|
+
{ phase: "build", label: "3 \xB7 Build" },
|
|
3892
|
+
{ phase: "verify", label: "4 \xB7 Verify" },
|
|
3893
|
+
{ phase: "review", label: "5 \xB7 Review" },
|
|
3894
|
+
{ phase: "domain", label: "6 \xB7 Domain" },
|
|
3895
|
+
{ phase: "knowledge", label: "7 \xB7 Knowledge" },
|
|
3896
|
+
{ phase: "delivery", label: "8 \xB7 Delivery & Ops" },
|
|
3897
|
+
{ phase: "meta", label: "9 \xB7 Meta" }
|
|
3898
|
+
];
|
|
3802
3899
|
function buildFleetCommand(opts) {
|
|
3803
3900
|
return {
|
|
3804
3901
|
name: "fleet",
|
|
@@ -3807,12 +3904,15 @@ function buildFleetCommand(opts) {
|
|
|
3807
3904
|
"Usage:",
|
|
3808
3905
|
" /fleet Show fleet status (default)",
|
|
3809
3906
|
" /fleet status Same as /fleet (verbose status)",
|
|
3907
|
+
" /fleet list List the agent roster grouped by phase",
|
|
3908
|
+
" /fleet dispatch <task> Route a task to the best agent and spawn it",
|
|
3810
3909
|
" /fleet spawn <role> [count] Spawn N subagents of a role (default 1)",
|
|
3811
3910
|
" /fleet terminate <subagentId> Stop a specific subagent by id",
|
|
3812
3911
|
" /fleet kill Stop all running subagents",
|
|
3813
3912
|
" /fleet usage Token and cost breakdown across the fleet",
|
|
3814
3913
|
" /fleet journal Show recent journal entries from /goal journal",
|
|
3815
3914
|
"",
|
|
3915
|
+
"In the TUI, press Ctrl+F to open the graphical fleet monitor.",
|
|
3816
3916
|
"Works during /autonomy parallel mode and standalone director sessions."
|
|
3817
3917
|
].join("\n"),
|
|
3818
3918
|
async run(args) {
|
|
@@ -3846,8 +3946,9 @@ function buildFleetCommand(opts) {
|
|
|
3846
3946
|
const id = sa.id?.padEnd(36) ?? "".padEnd(36);
|
|
3847
3947
|
const name = (sa.name ?? "worker").padEnd(16);
|
|
3848
3948
|
const statusColor = sa.status === "running" ? color.green(sa.status.padEnd(10)) : sa.status === "idle" ? color.dim(sa.status.padEnd(10)) : color.dim(sa.status.padEnd(10));
|
|
3949
|
+
const ext = sa.extensions && sa.extensions > 0 ? `${color.yellow(`\u26A1\xD7${sa.extensions}`)} ` : "";
|
|
3849
3950
|
const task = sa.currentTask ?? color.dim("\u2014");
|
|
3850
|
-
lines.push(` ${id} ${name} ${statusColor} ${task}`);
|
|
3951
|
+
lines.push(` ${id} ${name} ${statusColor} ${ext}${task}`);
|
|
3851
3952
|
}
|
|
3852
3953
|
}
|
|
3853
3954
|
const msg3 = lines.join("\n");
|
|
@@ -3953,29 +4054,28 @@ function buildFleetCommand(opts) {
|
|
|
3953
4054
|
if (cmd === "terminate" || cmd === "stop") {
|
|
3954
4055
|
const targetId = subargs[0];
|
|
3955
4056
|
if (!targetId) {
|
|
3956
|
-
const
|
|
3957
|
-
opts.renderer.writeWarning(
|
|
3958
|
-
return { message:
|
|
4057
|
+
const msg3 = `${color.amber("\u26A0 /fleet terminate requires a subagentId.")} Use /fleet to see active ids.`;
|
|
4058
|
+
opts.renderer.writeWarning(msg3);
|
|
4059
|
+
return { message: msg3 };
|
|
3959
4060
|
}
|
|
3960
4061
|
if (!opts.onFleetTerminate) {
|
|
3961
|
-
const
|
|
3962
|
-
opts.renderer.writeWarning(
|
|
3963
|
-
return { message:
|
|
4062
|
+
const msg3 = `${color.amber("\u26A0 /fleet terminate is not wired in this session.")}`;
|
|
4063
|
+
opts.renderer.writeWarning(msg3);
|
|
4064
|
+
return { message: msg3 };
|
|
3964
4065
|
}
|
|
3965
4066
|
const ok = opts.onFleetTerminate(targetId);
|
|
3966
4067
|
if (ok) {
|
|
3967
|
-
const
|
|
3968
|
-
opts.renderer.write(
|
|
3969
|
-
return { message:
|
|
3970
|
-
} else {
|
|
3971
|
-
const msg2 = `${color.red("\u2717 Failed")} to terminate ${color.bold(targetId)}. Subagent may already be stopped.`;
|
|
3972
|
-
opts.renderer.writeWarning(msg2);
|
|
3973
|
-
return { message: msg2 };
|
|
4068
|
+
const msg3 = `${color.green("\u2713 Terminated")} subagent ${color.bold(targetId)}.`;
|
|
4069
|
+
opts.renderer.write(msg3);
|
|
4070
|
+
return { message: msg3 };
|
|
3974
4071
|
}
|
|
4072
|
+
const msg2 = `${color.red("\u2717 Failed")} to terminate ${color.bold(targetId)}. Subagent may already be stopped.`;
|
|
4073
|
+
opts.renderer.writeWarning(msg2);
|
|
4074
|
+
return { message: msg2 };
|
|
3975
4075
|
}
|
|
3976
4076
|
if (cmd === "spawn" || cmd === "add") {
|
|
3977
4077
|
const role = subargs[0] ?? "worker";
|
|
3978
|
-
const count = Math.min(16, Math.max(1, parseInt(subargs[1] ?? "1", 10) || 1));
|
|
4078
|
+
const count = Math.min(16, Math.max(1, Number.parseInt(subargs[1] ?? "1", 10) || 1));
|
|
3979
4079
|
if (!opts.onFleetSpawn) {
|
|
3980
4080
|
const msg3 = `${color.amber("\u26A0 /fleet spawn is not wired in this session.")}`;
|
|
3981
4081
|
opts.renderer.writeWarning(msg3);
|
|
@@ -4001,11 +4101,64 @@ function buildFleetCommand(opts) {
|
|
|
4001
4101
|
}
|
|
4002
4102
|
return { message: msg2 };
|
|
4003
4103
|
}
|
|
4104
|
+
if (cmd === "list" || cmd === "roster" || cmd === "agents") {
|
|
4105
|
+
const lines = [`${color.bold("Agent Roster")} ${color.dim("(spawn with /fleet spawn <role>)")}`];
|
|
4106
|
+
for (const { phase, label } of PHASE_ORDER) {
|
|
4107
|
+
const defs = AGENTS_BY_PHASE[phase];
|
|
4108
|
+
if (!defs || defs.length === 0) continue;
|
|
4109
|
+
lines.push("");
|
|
4110
|
+
lines.push(color.cyan(` Phase ${label}`));
|
|
4111
|
+
for (const def of defs) {
|
|
4112
|
+
const role = (def.config.role ?? "").padEnd(18);
|
|
4113
|
+
lines.push(` ${color.bold(role)} ${color.dim(def.capability.summary)}`);
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
const msg2 = lines.join("\n");
|
|
4117
|
+
opts.renderer.write(msg2);
|
|
4118
|
+
return { message: msg2 };
|
|
4119
|
+
}
|
|
4120
|
+
if (cmd === "dispatch" || cmd === "route") {
|
|
4121
|
+
const task = subargs.join(" ").trim();
|
|
4122
|
+
if (!task) {
|
|
4123
|
+
const msg3 = `Usage: /fleet dispatch <task description> \u2014 routes the task to the best agent.`;
|
|
4124
|
+
opts.renderer.writeWarning(msg3);
|
|
4125
|
+
return { message: msg3 };
|
|
4126
|
+
}
|
|
4127
|
+
const decision = await dispatchAgent(task, { classifier: opts.onDispatchClassify });
|
|
4128
|
+
const pct2 = Math.round(decision.confidence * 100);
|
|
4129
|
+
const lines = [];
|
|
4130
|
+
lines.push(
|
|
4131
|
+
`${color.bold("\u2192 " + decision.role)} ${color.dim(`(${decision.method}, ${pct2}% confidence)`)}`
|
|
4132
|
+
);
|
|
4133
|
+
lines.push(` ${color.dim(decision.definition.capability.summary)}`);
|
|
4134
|
+
lines.push(` ${color.dim("why:")} ${decision.reason}`);
|
|
4135
|
+
if (decision.alternatives.length > 0) {
|
|
4136
|
+
const alts = decision.alternatives.slice(0, 3).map((a) => a.role).join(", ");
|
|
4137
|
+
lines.push(` ${color.dim("alternatives:")} ${alts}`);
|
|
4138
|
+
}
|
|
4139
|
+
if (opts.onFleetSpawn) {
|
|
4140
|
+
try {
|
|
4141
|
+
const id = await opts.onFleetSpawn(decision.role);
|
|
4142
|
+
lines.push(` ${color.green("\u2713 spawned")} ${color.bold(decision.role)} as ${color.dim(id)}`);
|
|
4143
|
+
} catch (err) {
|
|
4144
|
+
lines.push(
|
|
4145
|
+
` ${color.amber("\u26A0 spawn failed:")} ${err instanceof Error ? err.message : String(err)}`
|
|
4146
|
+
);
|
|
4147
|
+
}
|
|
4148
|
+
} else {
|
|
4149
|
+
lines.push(` ${color.dim("(no fleet active \u2014 run /autonomy parallel or --director to spawn)")}`);
|
|
4150
|
+
}
|
|
4151
|
+
const msg2 = lines.join("\n");
|
|
4152
|
+
opts.renderer.write(msg2);
|
|
4153
|
+
return { message: msg2 };
|
|
4154
|
+
}
|
|
4004
4155
|
if (cmd === "help" || cmd === "?") {
|
|
4005
4156
|
const msg2 = [
|
|
4006
4157
|
`${color.bold("Fleet Commands")}`,
|
|
4007
4158
|
` ${color.dim("/fleet")} Show fleet status (default)`,
|
|
4008
4159
|
` ${color.dim("/fleet status")} Same as /fleet (verbose status)`,
|
|
4160
|
+
` ${color.dim("/fleet list")} List the agent roster grouped by phase`,
|
|
4161
|
+
` ${color.dim("/fleet dispatch <task>")} Route a task to the best agent and spawn it`,
|
|
4009
4162
|
` ${color.dim("/fleet spawn <role> [count]")} Spawn N subagents of a role (default 1)`,
|
|
4010
4163
|
` ${color.dim("/fleet terminate <subagentId>")} Stop a specific subagent by id`,
|
|
4011
4164
|
` ${color.dim("/fleet kill")} Stop all running subagents`,
|
|
@@ -4015,7 +4168,7 @@ function buildFleetCommand(opts) {
|
|
|
4015
4168
|
opts.renderer.write(msg2);
|
|
4016
4169
|
return { message: msg2 };
|
|
4017
4170
|
}
|
|
4018
|
-
const valid = ["status", "usage", "spawn", "terminate", "kill", "retry", "journal"];
|
|
4171
|
+
const valid = ["status", "list", "dispatch", "usage", "spawn", "terminate", "kill", "retry", "journal"];
|
|
4019
4172
|
const msg = `Unknown subcommand "${cmd}". Valid subcommands: ${valid.join(", ")}. Run /fleet with no args to see status, or /fleet help for usage.`;
|
|
4020
4173
|
opts.renderer.writeWarning(msg);
|
|
4021
4174
|
return { message: msg };
|
|
@@ -5424,8 +5577,8 @@ ${list}
|
|
|
5424
5577
|
|
|
5425
5578
|
Use \`/security report <number>\` to view a specific report.` };
|
|
5426
5579
|
}
|
|
5427
|
-
const index = parseInt(reportId, 10) - 1;
|
|
5428
|
-
if (!isNaN(index) && reports[index]) {
|
|
5580
|
+
const index = Number.parseInt(reportId, 10) - 1;
|
|
5581
|
+
if (!Number.isNaN(index) && reports[index]) {
|
|
5429
5582
|
const content = await readFile(join(reportsDir, reports[index]), "utf-8");
|
|
5430
5583
|
return { message: `# Security Report
|
|
5431
5584
|
|
|
@@ -5505,8 +5658,17 @@ async function loadStatuslineConfig() {
|
|
|
5505
5658
|
}
|
|
5506
5659
|
async function saveStatuslineConfig(cfg) {
|
|
5507
5660
|
const p = resolveConfigPath();
|
|
5508
|
-
|
|
5509
|
-
|
|
5661
|
+
try {
|
|
5662
|
+
await fsp2.mkdir(path23.dirname(p), { recursive: true });
|
|
5663
|
+
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
5664
|
+
} catch (err) {
|
|
5665
|
+
throw new FsError({
|
|
5666
|
+
message: err instanceof Error ? err.message : String(err),
|
|
5667
|
+
code: err instanceof Error && err.message.includes("mkdir") ? ERROR_CODES.FS_MKDIR_FAILED : ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
5668
|
+
path: p,
|
|
5669
|
+
cause: err
|
|
5670
|
+
});
|
|
5671
|
+
}
|
|
5510
5672
|
}
|
|
5511
5673
|
function buildStatuslineCommand(deps) {
|
|
5512
5674
|
return {
|
|
@@ -6708,6 +6870,7 @@ async function runProjectCheck(opts) {
|
|
|
6708
6870
|
const { spawn: spawn3 } = await import('child_process');
|
|
6709
6871
|
await new Promise((resolve4, reject) => {
|
|
6710
6872
|
const child = spawn3("git", ["init"], { cwd: projectRoot });
|
|
6873
|
+
child.on("error", reject);
|
|
6711
6874
|
child.on("close", (code) => code === 0 ? resolve4() : reject(new Error(`git init failed with ${code}`)));
|
|
6712
6875
|
});
|
|
6713
6876
|
renderer.write(` ${color.green("\u2713")} Git repository initialized
|
|
@@ -7090,6 +7253,162 @@ function summarize(value, name) {
|
|
|
7090
7253
|
}
|
|
7091
7254
|
return "";
|
|
7092
7255
|
}
|
|
7256
|
+
var acpCmd = async (args, deps) => {
|
|
7257
|
+
const sub = args[0];
|
|
7258
|
+
if (!sub || sub === "server" || sub === "serve") {
|
|
7259
|
+
return runACPServer(deps);
|
|
7260
|
+
}
|
|
7261
|
+
if (sub === "help") {
|
|
7262
|
+
deps.renderer.write(`wstack acp \u2014 ACP (Agent Client Protocol) integration
|
|
7263
|
+
|
|
7264
|
+
Usage:
|
|
7265
|
+
wstack acp Start WrongStack as an ACP server (blocks)
|
|
7266
|
+
wstack acp server Same as above
|
|
7267
|
+
wstack acp list List available ACP agents
|
|
7268
|
+
wstack acp spawn <id> <task>
|
|
7269
|
+
Spawn an ACP agent as a subagent and wait for result
|
|
7270
|
+
wstack acp help Show this help
|
|
7271
|
+
|
|
7272
|
+
ACP Mode:
|
|
7273
|
+
When run as \`wstack acp\`. WrongStack acts as an ACP-compatible agent.
|
|
7274
|
+
ACP clients (Zed, JetBrains, VS Code) spawn it as a subprocess and
|
|
7275
|
+
communicate over stdio JSON-RPC.
|
|
7276
|
+
Press Ctrl+C to stop.
|
|
7277
|
+
|
|
7278
|
+
spawn:
|
|
7279
|
+
Spawns a named ACP agent (cline, gemini-cli, copilot, openhands, goose)
|
|
7280
|
+
with the given task and waits for its result.
|
|
7281
|
+
Example: wstack acp spawn cline "fix the login bug"
|
|
7282
|
+
`);
|
|
7283
|
+
return 0;
|
|
7284
|
+
}
|
|
7285
|
+
if (sub === "list") {
|
|
7286
|
+
return listACPAgents(deps);
|
|
7287
|
+
}
|
|
7288
|
+
if (sub === "spawn") {
|
|
7289
|
+
return spawnACPAgent(args.slice(1), deps);
|
|
7290
|
+
}
|
|
7291
|
+
deps.renderer.writeError(`Unknown acp subcommand: ${sub}
|
|
7292
|
+
`);
|
|
7293
|
+
deps.renderer.write("Run `wstack acp help` for usage.\n");
|
|
7294
|
+
return 1;
|
|
7295
|
+
};
|
|
7296
|
+
async function runACPServer(deps) {
|
|
7297
|
+
const toolRegistry = deps.toolRegistry;
|
|
7298
|
+
const tools = toolRegistry?.list() ?? [];
|
|
7299
|
+
deps.renderer.writeInfo("Starting WrongStack ACP server...\n");
|
|
7300
|
+
deps.renderer.writeInfo(`Exposing ${tools.length} tool(s) via ACP protocol.
|
|
7301
|
+
`);
|
|
7302
|
+
deps.renderer.writeInfo("Waiting for ACP client connection on stdin/stdout...\n");
|
|
7303
|
+
deps.renderer.writeInfo("Press Ctrl+C to stop.\n");
|
|
7304
|
+
const server = new WrongStackACPServer({ tools });
|
|
7305
|
+
const shutdown = () => {
|
|
7306
|
+
deps.renderer.writeWarning("\nShutting down ACP server...");
|
|
7307
|
+
server.stop();
|
|
7308
|
+
process.exit(0);
|
|
7309
|
+
};
|
|
7310
|
+
process.on("SIGINT", shutdown);
|
|
7311
|
+
process.on("SIGTERM", shutdown);
|
|
7312
|
+
try {
|
|
7313
|
+
await server.start();
|
|
7314
|
+
} catch (err) {
|
|
7315
|
+
deps.renderer.writeError(`ACP server error: ${err instanceof Error ? err.message : String(err)}
|
|
7316
|
+
`);
|
|
7317
|
+
return 1;
|
|
7318
|
+
}
|
|
7319
|
+
return 0;
|
|
7320
|
+
}
|
|
7321
|
+
function listACPAgents(deps) {
|
|
7322
|
+
deps.renderer.write("Available ACP agents:\n\n");
|
|
7323
|
+
for (const a of ACP_AGENTS) {
|
|
7324
|
+
const id = a.id ?? a.role;
|
|
7325
|
+
const name = a.name ?? a.role ?? "";
|
|
7326
|
+
const desc = a.prompt?.slice(0, 50) ?? "";
|
|
7327
|
+
deps.renderer.write(` ${id.padEnd(16)} ${name.padEnd(20)} ${desc}\u2026
|
|
7328
|
+
`);
|
|
7329
|
+
}
|
|
7330
|
+
deps.renderer.write("\nUse `wstack acp spawn <agent> <task>` to delegate a task.\n");
|
|
7331
|
+
return 0;
|
|
7332
|
+
}
|
|
7333
|
+
async function spawnACPAgent(args, deps) {
|
|
7334
|
+
const [subagentId, ...taskParts] = args;
|
|
7335
|
+
if (!subagentId) {
|
|
7336
|
+
deps.renderer.writeError("Usage: wstack acp spawn <agent-id> <task>\n");
|
|
7337
|
+
deps.renderer.write("Run `wstack acp list` to see available agents.\n");
|
|
7338
|
+
return 1;
|
|
7339
|
+
}
|
|
7340
|
+
const task = taskParts.join(" ");
|
|
7341
|
+
if (!task) {
|
|
7342
|
+
deps.renderer.writeError("Usage: wstack acp spawn <agent-id> <task>\n");
|
|
7343
|
+
deps.renderer.write("Task description is required.\n");
|
|
7344
|
+
return 1;
|
|
7345
|
+
}
|
|
7346
|
+
const cmd = ACP_AGENT_COMMANDS[subagentId];
|
|
7347
|
+
if (!cmd) {
|
|
7348
|
+
deps.renderer.writeError(`Unknown ACP agent: ${subagentId}
|
|
7349
|
+
`);
|
|
7350
|
+
deps.renderer.write("Run `wstack acp list` to see available agents.\n");
|
|
7351
|
+
return 1;
|
|
7352
|
+
}
|
|
7353
|
+
deps.renderer.writeInfo(`Spawning ACP agent '${subagentId}'\u2026
|
|
7354
|
+
`);
|
|
7355
|
+
const cleanup = () => {
|
|
7356
|
+
if (stop) {
|
|
7357
|
+
try {
|
|
7358
|
+
stop();
|
|
7359
|
+
} catch {
|
|
7360
|
+
}
|
|
7361
|
+
}
|
|
7362
|
+
};
|
|
7363
|
+
process.on("SIGINT", cleanup);
|
|
7364
|
+
process.on("SIGTERM", cleanup);
|
|
7365
|
+
let stop = null;
|
|
7366
|
+
try {
|
|
7367
|
+
const { runner, stop: runStop } = await makeACPSubagentRunnerWithStop(cmd);
|
|
7368
|
+
stop = runStop;
|
|
7369
|
+
const taskId = `acp-${crypto.randomUUID()}`;
|
|
7370
|
+
const budget = new SubagentBudget({
|
|
7371
|
+
timeoutMs: 5 * 60 * 1e3,
|
|
7372
|
+
maxIterations: 2e3,
|
|
7373
|
+
maxToolCalls: 5e3
|
|
7374
|
+
});
|
|
7375
|
+
const ctx = {
|
|
7376
|
+
subagentId,
|
|
7377
|
+
config: {
|
|
7378
|
+
id: subagentId,
|
|
7379
|
+
name: cmd.role ?? subagentId,
|
|
7380
|
+
role: subagentId,
|
|
7381
|
+
provider: "acp",
|
|
7382
|
+
prompt: ""
|
|
7383
|
+
},
|
|
7384
|
+
budget,
|
|
7385
|
+
signal: new AbortController().signal,
|
|
7386
|
+
bridge: null
|
|
7387
|
+
};
|
|
7388
|
+
budget.start();
|
|
7389
|
+
deps.renderer.writeInfo("Running task\u2026\n");
|
|
7390
|
+
const result = await runner(
|
|
7391
|
+
{ id: taskId, description: task },
|
|
7392
|
+
ctx
|
|
7393
|
+
);
|
|
7394
|
+
deps.renderer.write("\n--- Result ---\n");
|
|
7395
|
+
deps.renderer.write(String(result.result ?? "no result"));
|
|
7396
|
+
deps.renderer.write("\n---------------\n");
|
|
7397
|
+
deps.renderer.writeInfo(
|
|
7398
|
+
`Done. iterations=${result.iterations} toolCalls=${result.toolCalls}
|
|
7399
|
+
`
|
|
7400
|
+
);
|
|
7401
|
+
return 0;
|
|
7402
|
+
} catch (err) {
|
|
7403
|
+
deps.renderer.writeError(`ACP agent error: ${err instanceof Error ? err.message : String(err)}
|
|
7404
|
+
`);
|
|
7405
|
+
return 1;
|
|
7406
|
+
} finally {
|
|
7407
|
+
cleanup();
|
|
7408
|
+
process.off("SIGINT", cleanup);
|
|
7409
|
+
process.off("SIGTERM", cleanup);
|
|
7410
|
+
}
|
|
7411
|
+
}
|
|
7093
7412
|
|
|
7094
7413
|
// src/auth-menu.ts
|
|
7095
7414
|
init_provider_config_utils();
|
|
@@ -7786,7 +8105,7 @@ var updateCmd = async (args, deps) => {
|
|
|
7786
8105
|
deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
|
|
7787
8106
|
`);
|
|
7788
8107
|
try {
|
|
7789
|
-
const result = await new Promise((resolve4) => {
|
|
8108
|
+
const result = await new Promise((resolve4, reject) => {
|
|
7790
8109
|
const child = spawn("npm", ["install", "-g", "wrongstack@latest"], {
|
|
7791
8110
|
cwd,
|
|
7792
8111
|
stdio: "pipe"
|
|
@@ -7795,6 +8114,7 @@ var updateCmd = async (args, deps) => {
|
|
|
7795
8114
|
child.stderr?.on("data", (d) => {
|
|
7796
8115
|
stderr += d;
|
|
7797
8116
|
});
|
|
8117
|
+
child.on("error", reject);
|
|
7798
8118
|
child.on("close", (code) => resolve4({ code: code ?? 0 }));
|
|
7799
8119
|
});
|
|
7800
8120
|
if (result.code === 0) {
|
|
@@ -8957,8 +9277,8 @@ var rewindCmd = async (args, deps) => {
|
|
|
8957
9277
|
deps.renderer.write("Rewinding to session start...\n");
|
|
8958
9278
|
result = await rewind.rewindToStart(sessionId);
|
|
8959
9279
|
} else if (flags.last) {
|
|
8960
|
-
const n = parseInt(flags.last, 10);
|
|
8961
|
-
if (isNaN(n) || n < 1) {
|
|
9280
|
+
const n = Number.parseInt(flags.last, 10);
|
|
9281
|
+
if (Number.isNaN(n) || n < 1) {
|
|
8962
9282
|
deps.renderer.writeError("--last requires a positive number");
|
|
8963
9283
|
return 1;
|
|
8964
9284
|
}
|
|
@@ -8966,8 +9286,8 @@ var rewindCmd = async (args, deps) => {
|
|
|
8966
9286
|
`);
|
|
8967
9287
|
result = await rewind.rewindLastN(sessionId, n);
|
|
8968
9288
|
} else if (flags.to) {
|
|
8969
|
-
const idx = parseInt(flags.to, 10);
|
|
8970
|
-
if (isNaN(idx) || idx < 0) {
|
|
9289
|
+
const idx = Number.parseInt(flags.to, 10);
|
|
9290
|
+
if (Number.isNaN(idx) || idx < 0) {
|
|
8971
9291
|
deps.renderer.writeError("--to requires a non-negative number");
|
|
8972
9292
|
return 1;
|
|
8973
9293
|
}
|
|
@@ -9088,6 +9408,7 @@ var helpCmd = async (_args, deps) => {
|
|
|
9088
9408
|
|
|
9089
9409
|
// src/subcommands/index.ts
|
|
9090
9410
|
var subcommands = {
|
|
9411
|
+
acp: acpCmd,
|
|
9091
9412
|
init: initCmd,
|
|
9092
9413
|
auth: authCmd,
|
|
9093
9414
|
update: updateCmd,
|
|
@@ -9130,22 +9451,22 @@ function fmtDuration(ms) {
|
|
|
9130
9451
|
const remMin = m - h * 60;
|
|
9131
9452
|
return `${h}h${remMin}m`;
|
|
9132
9453
|
}
|
|
9133
|
-
function fmtTaskResultLine(r,
|
|
9454
|
+
function fmtTaskResultLine(r, color39) {
|
|
9134
9455
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
9135
9456
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
9136
9457
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
9137
9458
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
9138
|
-
const errKindChip = errKind ?
|
|
9139
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
9459
|
+
const errKindChip = errKind ? color39.dim(` [${errKind}]`) : "";
|
|
9460
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color39.dim(errTail)}` : "";
|
|
9140
9461
|
switch (r.status) {
|
|
9141
9462
|
case "success":
|
|
9142
|
-
return { mark:
|
|
9463
|
+
return { mark: color39.green("\u2713"), stats, tail: "" };
|
|
9143
9464
|
case "timeout":
|
|
9144
|
-
return { mark:
|
|
9465
|
+
return { mark: color39.yellow("\u23F1"), stats: `${color39.yellow("timeout")} ${stats}`, tail: errSnip };
|
|
9145
9466
|
case "stopped":
|
|
9146
|
-
return { mark:
|
|
9467
|
+
return { mark: color39.dim("\u2298"), stats: `${color39.dim("stopped")} ${stats}`, tail: errSnip };
|
|
9147
9468
|
case "failed":
|
|
9148
|
-
return { mark:
|
|
9469
|
+
return { mark: color39.red("\u2717"), stats: `${color39.red("failed")} ${stats}`, tail: errSnip };
|
|
9149
9470
|
}
|
|
9150
9471
|
}
|
|
9151
9472
|
|
|
@@ -9304,6 +9625,214 @@ async function boot(argv) {
|
|
|
9304
9625
|
updateInfo
|
|
9305
9626
|
};
|
|
9306
9627
|
}
|
|
9628
|
+
function fmtElapsed(ms) {
|
|
9629
|
+
const s = Math.floor(ms / 1e3);
|
|
9630
|
+
if (s < 60) return `${s}s`;
|
|
9631
|
+
const m = Math.floor(s / 60);
|
|
9632
|
+
const rem = s % 60;
|
|
9633
|
+
if (m < 60) return `${m}m${rem.toString().padStart(2, "0")}s`;
|
|
9634
|
+
const h = Math.floor(m / 60);
|
|
9635
|
+
return `${h}h${(m % 60).toString().padStart(2, "0")}m`;
|
|
9636
|
+
}
|
|
9637
|
+
function renderFleetLine(states, now, columns, version) {
|
|
9638
|
+
const all = [...states.values()];
|
|
9639
|
+
if (all.length === 0) return "";
|
|
9640
|
+
const running = all.filter((a) => a.status === "running");
|
|
9641
|
+
const done = all.filter((a) => a.status === "done").length;
|
|
9642
|
+
const failed = all.filter((a) => a.status === "failed").length;
|
|
9643
|
+
const versionChip = version ? `${color.bold("WS")}${color.dim(` v${version}`)} ${color.dim("\u2502")} ` : "";
|
|
9644
|
+
const counts = versionChip + `${color.cyan("\u27F3 fleet")} ${color.yellow(`\u25B6${running.length}`)} ${color.green(`\u2713${done}`)}` + (failed > 0 ? ` ${color.red(`\u2717${failed}`)}` : "");
|
|
9645
|
+
const shown = running.sort((a, b) => a.startedAt - b.startedAt).slice(0, 4).map((a) => {
|
|
9646
|
+
const elapsed = fmtElapsed(Math.max(0, now - a.startedAt));
|
|
9647
|
+
const tool = a.lastTool ? ` ${color.dim(a.lastTool)}` : "";
|
|
9648
|
+
const ext = a.extensions && a.extensions > 0 ? ` ${color.yellow(`\u26A1${a.extensions}`)}` : "";
|
|
9649
|
+
return `${color.bold(a.name)} ${color.yellow("\u25B6")} ${color.dim(elapsed)} ${color.dim(`L${a.iterations}`)} ${color.dim(`${a.toolCalls}t`)}${tool}${ext}`;
|
|
9650
|
+
});
|
|
9651
|
+
let line = shown.length > 0 ? `${counts} ${color.dim("\u2502")} ${shown.join(color.dim(" \xB7 "))}` : counts;
|
|
9652
|
+
const max = Math.max(20, columns - 1);
|
|
9653
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
9654
|
+
if (visible.length > max) {
|
|
9655
|
+
let count = 0;
|
|
9656
|
+
let out = "";
|
|
9657
|
+
let i = 0;
|
|
9658
|
+
while (i < line.length && count < max - 1) {
|
|
9659
|
+
if (line[i] === "\x1B") {
|
|
9660
|
+
const end = line.indexOf("m", i);
|
|
9661
|
+
if (end !== -1) {
|
|
9662
|
+
out += line.slice(i, end + 1);
|
|
9663
|
+
i = end + 1;
|
|
9664
|
+
continue;
|
|
9665
|
+
}
|
|
9666
|
+
}
|
|
9667
|
+
out += line[i];
|
|
9668
|
+
count++;
|
|
9669
|
+
i++;
|
|
9670
|
+
}
|
|
9671
|
+
line = out + "\u2026";
|
|
9672
|
+
}
|
|
9673
|
+
return line;
|
|
9674
|
+
}
|
|
9675
|
+
var FleetStatusLine = class {
|
|
9676
|
+
events;
|
|
9677
|
+
out;
|
|
9678
|
+
throttleMs;
|
|
9679
|
+
now;
|
|
9680
|
+
version;
|
|
9681
|
+
states = /* @__PURE__ */ new Map();
|
|
9682
|
+
unsubs = [];
|
|
9683
|
+
active = false;
|
|
9684
|
+
rows = 0;
|
|
9685
|
+
repaintTimer = null;
|
|
9686
|
+
tickTimer = null;
|
|
9687
|
+
lastPaint = 0;
|
|
9688
|
+
onResize = () => {
|
|
9689
|
+
if (this.active) {
|
|
9690
|
+
this.rows = this.out.rows ?? 24;
|
|
9691
|
+
this.out.write(`\x1B[1;${this.rows - 1}r`);
|
|
9692
|
+
this.paint(true);
|
|
9693
|
+
}
|
|
9694
|
+
};
|
|
9695
|
+
constructor(opts) {
|
|
9696
|
+
this.events = opts.events;
|
|
9697
|
+
this.out = opts.out ?? process.stdout;
|
|
9698
|
+
this.throttleMs = opts.throttleMs ?? 150;
|
|
9699
|
+
this.now = opts.now ?? Date.now;
|
|
9700
|
+
this.version = opts.version;
|
|
9701
|
+
}
|
|
9702
|
+
/** Subscribe to host fleet events. No terminal output until a subagent spawns. */
|
|
9703
|
+
start() {
|
|
9704
|
+
const ensure = (id, name) => {
|
|
9705
|
+
let s = this.states.get(id);
|
|
9706
|
+
if (!s) {
|
|
9707
|
+
s = {
|
|
9708
|
+
id,
|
|
9709
|
+
name: name ?? id,
|
|
9710
|
+
status: "running",
|
|
9711
|
+
iterations: 0,
|
|
9712
|
+
toolCalls: 0,
|
|
9713
|
+
startedAt: this.now()
|
|
9714
|
+
};
|
|
9715
|
+
this.states.set(id, s);
|
|
9716
|
+
} else if (name && s.name === s.id) {
|
|
9717
|
+
s.name = name;
|
|
9718
|
+
}
|
|
9719
|
+
return s;
|
|
9720
|
+
};
|
|
9721
|
+
this.unsubs.push(
|
|
9722
|
+
this.events.on("subagent.spawned", (e) => {
|
|
9723
|
+
ensure(e.subagentId, e.name);
|
|
9724
|
+
this.activate();
|
|
9725
|
+
this.schedulePaint();
|
|
9726
|
+
}),
|
|
9727
|
+
this.events.on("subagent.task_started", (e) => {
|
|
9728
|
+
const s = ensure(e.subagentId);
|
|
9729
|
+
s.status = "running";
|
|
9730
|
+
this.activate();
|
|
9731
|
+
this.schedulePaint();
|
|
9732
|
+
}),
|
|
9733
|
+
this.events.on("subagent.tool_executed", (e) => {
|
|
9734
|
+
const s = ensure(e.subagentId);
|
|
9735
|
+
s.toolCalls++;
|
|
9736
|
+
s.lastTool = e.name;
|
|
9737
|
+
this.activate();
|
|
9738
|
+
this.schedulePaint();
|
|
9739
|
+
}),
|
|
9740
|
+
this.events.on("subagent.iteration_summary", (e) => {
|
|
9741
|
+
const s = ensure(e.subagentId);
|
|
9742
|
+
s.iterations = e.iteration;
|
|
9743
|
+
if (typeof e.toolCalls === "number") s.toolCalls = e.toolCalls;
|
|
9744
|
+
if (e.currentTool) s.lastTool = e.currentTool;
|
|
9745
|
+
this.schedulePaint();
|
|
9746
|
+
}),
|
|
9747
|
+
this.events.on("subagent.budget_extended", (e) => {
|
|
9748
|
+
const s = ensure(e.subagentId);
|
|
9749
|
+
s.extensions = e.totalExtensions;
|
|
9750
|
+
this.activate();
|
|
9751
|
+
this.schedulePaint();
|
|
9752
|
+
}),
|
|
9753
|
+
this.events.on("subagent.task_completed", (e) => {
|
|
9754
|
+
const s = ensure(e.subagentId);
|
|
9755
|
+
s.status = e.status === "success" ? "done" : "failed";
|
|
9756
|
+
s.iterations = e.iterations;
|
|
9757
|
+
s.toolCalls = e.toolCalls;
|
|
9758
|
+
s.endedAt = this.now();
|
|
9759
|
+
this.schedulePaint();
|
|
9760
|
+
if (![...this.states.values()].some((a) => a.status === "running")) {
|
|
9761
|
+
setTimeout(() => {
|
|
9762
|
+
if (![...this.states.values()].some((a) => a.status === "running")) {
|
|
9763
|
+
this.deactivate();
|
|
9764
|
+
}
|
|
9765
|
+
}, 800);
|
|
9766
|
+
}
|
|
9767
|
+
})
|
|
9768
|
+
);
|
|
9769
|
+
}
|
|
9770
|
+
/** Unsubscribe and restore the terminal. Idempotent. */
|
|
9771
|
+
stop() {
|
|
9772
|
+
for (const u of this.unsubs.splice(0)) u();
|
|
9773
|
+
this.deactivate();
|
|
9774
|
+
}
|
|
9775
|
+
isTty() {
|
|
9776
|
+
return !!this.out.isTTY;
|
|
9777
|
+
}
|
|
9778
|
+
activate() {
|
|
9779
|
+
if (this.active || !this.isTty()) return;
|
|
9780
|
+
this.active = true;
|
|
9781
|
+
this.rows = this.out.rows ?? 24;
|
|
9782
|
+
this.out.write("\n");
|
|
9783
|
+
this.out.write(`\x1B[1;${this.rows - 1}r`);
|
|
9784
|
+
this.out.write(`\x1B[${this.rows - 1};1H`);
|
|
9785
|
+
this.out.on("resize", this.onResize);
|
|
9786
|
+
this.tickTimer = setInterval(() => this.paint(true), 1e3);
|
|
9787
|
+
if (this.tickTimer.unref) this.tickTimer.unref();
|
|
9788
|
+
this.paint(true);
|
|
9789
|
+
}
|
|
9790
|
+
deactivate() {
|
|
9791
|
+
if (!this.active) return;
|
|
9792
|
+
this.active = false;
|
|
9793
|
+
if (this.tickTimer) {
|
|
9794
|
+
clearInterval(this.tickTimer);
|
|
9795
|
+
this.tickTimer = null;
|
|
9796
|
+
}
|
|
9797
|
+
if (this.repaintTimer) {
|
|
9798
|
+
clearTimeout(this.repaintTimer);
|
|
9799
|
+
this.repaintTimer = null;
|
|
9800
|
+
}
|
|
9801
|
+
this.out.off("resize", this.onResize);
|
|
9802
|
+
if (this.isTty()) {
|
|
9803
|
+
this.out.write("\x1B7");
|
|
9804
|
+
this.out.write(`\x1B[${this.rows};1H`);
|
|
9805
|
+
this.out.write("\x1B[2K");
|
|
9806
|
+
this.out.write("\x1B8");
|
|
9807
|
+
this.out.write("\x1B[r");
|
|
9808
|
+
}
|
|
9809
|
+
}
|
|
9810
|
+
schedulePaint() {
|
|
9811
|
+
if (!this.active) return;
|
|
9812
|
+
const since = this.now() - this.lastPaint;
|
|
9813
|
+
if (since >= this.throttleMs) {
|
|
9814
|
+
this.paint(false);
|
|
9815
|
+
return;
|
|
9816
|
+
}
|
|
9817
|
+
if (this.repaintTimer) return;
|
|
9818
|
+
this.repaintTimer = setTimeout(() => {
|
|
9819
|
+
this.repaintTimer = null;
|
|
9820
|
+
this.paint(false);
|
|
9821
|
+
}, this.throttleMs - since);
|
|
9822
|
+
if (this.repaintTimer.unref) this.repaintTimer.unref();
|
|
9823
|
+
}
|
|
9824
|
+
paint(force) {
|
|
9825
|
+
if (!this.active || !this.isTty()) return;
|
|
9826
|
+
if (!force) this.lastPaint = this.now();
|
|
9827
|
+
else this.lastPaint = this.now();
|
|
9828
|
+
const line = renderFleetLine(this.states, this.now(), this.out.columns ?? 80, this.version);
|
|
9829
|
+
this.out.write("\x1B7");
|
|
9830
|
+
this.out.write(`\x1B[${this.rows};1H`);
|
|
9831
|
+
this.out.write("\x1B[2K");
|
|
9832
|
+
this.out.write(line);
|
|
9833
|
+
this.out.write("\x1B8");
|
|
9834
|
+
}
|
|
9835
|
+
};
|
|
9307
9836
|
|
|
9308
9837
|
// src/repl.ts
|
|
9309
9838
|
init_sdd();
|
|
@@ -9382,9 +9911,15 @@ async function runRepl(opts) {
|
|
|
9382
9911
|
} else {
|
|
9383
9912
|
const beforeGoal = await loadGoalSafe(opts);
|
|
9384
9913
|
const beforeIter = beforeGoal?.iterations ?? 0;
|
|
9914
|
+
const coord = engine.getCoordinator();
|
|
9915
|
+
if (coord) {
|
|
9916
|
+
const stats = coord.getStats();
|
|
9917
|
+
opts.renderer.write(
|
|
9918
|
+
color.dim(` \u250C\u2500 Fleet: ${stats.running} running, ${stats.idle} idle, ${stats.pending} pending, ${stats.completed} done`) + "\n"
|
|
9919
|
+
);
|
|
9920
|
+
}
|
|
9385
9921
|
opts.renderer.write(
|
|
9386
|
-
color.magenta(`
|
|
9387
|
-
\u21B3 [parallel #${beforeIter + 1}] launching fan-out\u2026
|
|
9922
|
+
color.magenta(` \u21B3 [parallel #${beforeIter + 1}] launching fan-out\u2026
|
|
9388
9923
|
`)
|
|
9389
9924
|
);
|
|
9390
9925
|
interrupts = 0;
|
|
@@ -9392,6 +9927,13 @@ async function runRepl(opts) {
|
|
|
9392
9927
|
const ok = await engine.runOneIteration();
|
|
9393
9928
|
const afterGoal = await loadGoalSafe(opts);
|
|
9394
9929
|
const last = afterGoal?.journal[afterGoal.journal.length - 1];
|
|
9930
|
+
if (coord) {
|
|
9931
|
+
const stats = coord.getStats();
|
|
9932
|
+
opts.renderer.write(
|
|
9933
|
+
color.dim(` \u2514\u2500 Fleet: ${stats.running} running, ${stats.idle} idle, ${stats.completed} done
|
|
9934
|
+
`)
|
|
9935
|
+
);
|
|
9936
|
+
}
|
|
9395
9937
|
if (last) {
|
|
9396
9938
|
const mark = last.status === "success" ? color.green("\u2713") : last.status === "failure" ? color.red("\u2717") : color.amber("\u2298");
|
|
9397
9939
|
const tail = last.note ? color.dim(` \u2014 ${last.note.slice(0, 80)}`) : "";
|
|
@@ -9743,9 +10285,17 @@ async function renderGoalBanner(opts) {
|
|
|
9743
10285
|
const goal = await loadGoalSafe(opts);
|
|
9744
10286
|
if (!goal) return;
|
|
9745
10287
|
const summary = goal.goal.length > 80 ? `${goal.goal.slice(0, 77)}\u2026` : goal.goal;
|
|
10288
|
+
const stateColor = goal.goalState === "active" ? color.green : goal.goalState === "paused" ? color.amber : goal.goalState === "completed" ? color.green : goal.goalState === "abandoned" ? color.dim : color.dim;
|
|
9746
10289
|
opts.renderer.write(
|
|
9747
|
-
color.dim("Goal: ") +
|
|
10290
|
+
color.dim("Goal: ") + stateColor(summary) + color.dim(` [${goal.goalState}] (iter ${goal.iterations})`) + "\n"
|
|
9748
10291
|
);
|
|
10292
|
+
if (goal.journal.length > 0) {
|
|
10293
|
+
const lastEntry = goal.journal[goal.journal.length - 1];
|
|
10294
|
+
const statusIcon2 = lastEntry.status === "success" ? "\u2713" : lastEntry.status === "failure" ? "\u2717" : lastEntry.status === "aborted" ? "\u2298" : lastEntry.status === "skipped" ? "\u229D" : "\xB7";
|
|
10295
|
+
opts.renderer.write(
|
|
10296
|
+
color.dim(` Last: ${statusIcon2} ${lastEntry.task} (${lastEntry.status})`) + "\n"
|
|
10297
|
+
);
|
|
10298
|
+
}
|
|
9749
10299
|
if (goal.engineState === "running") {
|
|
9750
10300
|
opts.renderer.write(
|
|
9751
10301
|
color.amber(" \u21BA Eternal engine was running when last session ended.") + "\n"
|
|
@@ -9770,6 +10320,18 @@ async function renderGoalBanner(opts) {
|
|
|
9770
10320
|
color.dim(" Use `/autonomy eternal` to resume.") + "\n"
|
|
9771
10321
|
);
|
|
9772
10322
|
}
|
|
10323
|
+
} else if (goal.goalState === "paused") {
|
|
10324
|
+
opts.renderer.write(
|
|
10325
|
+
color.amber(" \u23F8 Goal is paused. Use `/goal resume` to continue.") + "\n"
|
|
10326
|
+
);
|
|
10327
|
+
} else if (goal.goalState === "completed") {
|
|
10328
|
+
opts.renderer.write(
|
|
10329
|
+
color.green(" \u2713 Goal completed! Use `/goal clear` to set a new goal.") + "\n"
|
|
10330
|
+
);
|
|
10331
|
+
} else if (goal.goalState === "abandoned") {
|
|
10332
|
+
opts.renderer.write(
|
|
10333
|
+
color.dim(" Use `/goal clear` to set a new goal.") + "\n"
|
|
10334
|
+
);
|
|
9773
10335
|
}
|
|
9774
10336
|
opts.renderer.write("\n");
|
|
9775
10337
|
}
|
|
@@ -9868,6 +10430,7 @@ async function execute(deps) {
|
|
|
9868
10430
|
skillLoader
|
|
9869
10431
|
} = deps;
|
|
9870
10432
|
let code = 0;
|
|
10433
|
+
let fleetStatusLine = null;
|
|
9871
10434
|
try {
|
|
9872
10435
|
const visionAdapters = () => createToolVisionAdapters(agent.tools);
|
|
9873
10436
|
const supportsVision = async () => {
|
|
@@ -9887,6 +10450,11 @@ async function execute(deps) {
|
|
|
9887
10450
|
if ((goalFlag || askFlag) && positional.length === 0 && !promptFlag) {
|
|
9888
10451
|
flags.tui = true;
|
|
9889
10452
|
}
|
|
10453
|
+
const enteringTui = !(positional.length > 0 || promptFlag) && !!flags.tui && flags["no-tui"] !== true;
|
|
10454
|
+
if (!enteringTui) {
|
|
10455
|
+
fleetStatusLine = new FleetStatusLine({ events, version: CLI_VERSION });
|
|
10456
|
+
fleetStatusLine.start();
|
|
10457
|
+
}
|
|
9890
10458
|
if (positional.length > 0 || promptFlag) {
|
|
9891
10459
|
const query = positional.join(" ");
|
|
9892
10460
|
const ctrl = new AbortController();
|
|
@@ -10011,6 +10579,7 @@ async function execute(deps) {
|
|
|
10011
10579
|
setStatuslineHiddenItems,
|
|
10012
10580
|
initialGoal: goalFlag,
|
|
10013
10581
|
initialAsk: askFlag,
|
|
10582
|
+
projectRoot,
|
|
10014
10583
|
getSDDContext: () => {
|
|
10015
10584
|
const { getActiveSDDContext: getActiveSDDContext2 } = (init_sdd(), __toCommonJS(sdd_exports));
|
|
10016
10585
|
return getActiveSDDContext2();
|
|
@@ -10097,6 +10666,7 @@ async function execute(deps) {
|
|
|
10097
10666
|
});
|
|
10098
10667
|
}
|
|
10099
10668
|
} finally {
|
|
10669
|
+
fleetStatusLine?.stop();
|
|
10100
10670
|
try {
|
|
10101
10671
|
stats.render(renderer);
|
|
10102
10672
|
} catch (err) {
|
|
@@ -10156,6 +10726,13 @@ var MultiAgentHost = class {
|
|
|
10156
10726
|
getCoordinator() {
|
|
10157
10727
|
return this.director.coordinator;
|
|
10158
10728
|
}
|
|
10729
|
+
/** Public accessor for the Director — used by buildRoutingRunner. */
|
|
10730
|
+
getDirector() {
|
|
10731
|
+
return this.director;
|
|
10732
|
+
}
|
|
10733
|
+
async ensureCoordinator(config) {
|
|
10734
|
+
await this.buildDirector();
|
|
10735
|
+
}
|
|
10159
10736
|
async buildDirector() {
|
|
10160
10737
|
if (this.director) return;
|
|
10161
10738
|
const config = this.deps.configStore.get();
|
|
@@ -10211,22 +10788,41 @@ var MultiAgentHost = class {
|
|
|
10211
10788
|
limit: payload.limit
|
|
10212
10789
|
});
|
|
10213
10790
|
});
|
|
10214
|
-
this.
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10791
|
+
this.director.fleet.filter("budget.extended", (e) => {
|
|
10792
|
+
const payload = e.payload;
|
|
10793
|
+
this.deps.events.emit("subagent.budget_extended", {
|
|
10794
|
+
subagentId: e.subagentId,
|
|
10795
|
+
kind: payload.kind,
|
|
10796
|
+
newLimit: payload.newLimit,
|
|
10797
|
+
totalExtensions: payload.totalExtensions
|
|
10219
10798
|
});
|
|
10220
10799
|
});
|
|
10221
|
-
|
|
10800
|
+
this.getCoordinator().on(
|
|
10801
|
+
"task.assigned",
|
|
10802
|
+
({
|
|
10803
|
+
task,
|
|
10804
|
+
subagentId
|
|
10805
|
+
}) => {
|
|
10806
|
+
this.deps.events.emit("subagent.task_started", {
|
|
10807
|
+
subagentId,
|
|
10808
|
+
taskId: task.id,
|
|
10809
|
+
description: task.description
|
|
10810
|
+
});
|
|
10811
|
+
}
|
|
10812
|
+
);
|
|
10813
|
+
const runner = await this.buildSubagentRunner(config);
|
|
10222
10814
|
this.getCoordinator().setRunner(runner);
|
|
10223
10815
|
}
|
|
10224
10816
|
/**
|
|
10225
|
-
* Build
|
|
10226
|
-
*
|
|
10817
|
+
* Build a per-role subagent factory: given a SubagentConfig, construct a
|
|
10818
|
+
* fresh, isolated Agent with the role's filtered tools and (when the config
|
|
10819
|
+
* carries one) the role's persona as an appended system-prompt block. Public
|
|
10820
|
+
* so the autonomy-parallel engine can reuse the exact same agent-construction
|
|
10821
|
+
* path the director/spawn flow uses — each parallel slot then runs as a real,
|
|
10822
|
+
* specialized, concurrency-safe agent instead of sharing the leader's Context.
|
|
10227
10823
|
*/
|
|
10228
|
-
|
|
10229
|
-
|
|
10824
|
+
makeSubagentFactory(config) {
|
|
10825
|
+
return async (subCfg) => {
|
|
10230
10826
|
const events = new EventBus();
|
|
10231
10827
|
const provider = await this.buildSubagentProvider(config, subCfg.provider);
|
|
10232
10828
|
const baseSystem = await this.deps.systemPromptBuilder.build({
|
|
@@ -10241,6 +10837,9 @@ var MultiAgentHost = class {
|
|
|
10241
10837
|
// meaningless to a single delegated subtask.
|
|
10242
10838
|
subagent: true
|
|
10243
10839
|
});
|
|
10840
|
+
if (subCfg.systemPromptOverride) {
|
|
10841
|
+
baseSystem.push({ type: "text", text: subCfg.systemPromptOverride });
|
|
10842
|
+
}
|
|
10244
10843
|
let subSession;
|
|
10245
10844
|
if (this.sessionFactory) {
|
|
10246
10845
|
const subagentName = subCfg.id ?? subCfg.name ?? `sub_${randomUUID().slice(0, 8)}`;
|
|
@@ -10319,7 +10918,22 @@ var MultiAgentHost = class {
|
|
|
10319
10918
|
};
|
|
10320
10919
|
return { agent, events, dispose };
|
|
10321
10920
|
};
|
|
10322
|
-
|
|
10921
|
+
}
|
|
10922
|
+
/**
|
|
10923
|
+
* Build the per-subagent runner.
|
|
10924
|
+
*
|
|
10925
|
+
* ACP agents (provider: 'acp') get their own runner via
|
|
10926
|
+
* makeACPSubagentRunner — they run external processes and don't go
|
|
10927
|
+
* through the Agent factory. Regular agents use the standard
|
|
10928
|
+
* makeAgentSubagentRunner path.
|
|
10929
|
+
*/
|
|
10930
|
+
async buildSubagentRunner(config) {
|
|
10931
|
+
return buildRoutingRunner(config, this);
|
|
10932
|
+
}
|
|
10933
|
+
async buildACPRunner(subagentId) {
|
|
10934
|
+
const cmd = ACP_AGENT_COMMANDS[subagentId];
|
|
10935
|
+
if (!cmd) throw new Error(`Unknown ACP agent: ${subagentId}`);
|
|
10936
|
+
return makeACPSubagentRunner(cmd);
|
|
10323
10937
|
}
|
|
10324
10938
|
/**
|
|
10325
10939
|
* Build a Provider for a subagent. When `overrideId` is supplied (from
|
|
@@ -10341,6 +10955,24 @@ var MultiAgentHost = class {
|
|
|
10341
10955
|
type: providerId
|
|
10342
10956
|
});
|
|
10343
10957
|
}
|
|
10958
|
+
async spawnACP(subagentId, task, config) {
|
|
10959
|
+
const taskId = randomUUID();
|
|
10960
|
+
await this.ensureCoordinator(config);
|
|
10961
|
+
const coordinator = this.getCoordinator();
|
|
10962
|
+
const acpRunner = await this.buildACPRunner(subagentId);
|
|
10963
|
+
coordinator.setRunner(acpRunner);
|
|
10964
|
+
await coordinator.spawn({
|
|
10965
|
+
id: subagentId,
|
|
10966
|
+
name: subagentId,
|
|
10967
|
+
role: subagentId,
|
|
10968
|
+
provider: "acp"
|
|
10969
|
+
});
|
|
10970
|
+
await coordinator.assign({
|
|
10971
|
+
id: taskId,
|
|
10972
|
+
description: task
|
|
10973
|
+
});
|
|
10974
|
+
return taskId;
|
|
10975
|
+
}
|
|
10344
10976
|
/** Returns a tool slice for the subagent — full set unless restricted. */
|
|
10345
10977
|
filterTools(allow) {
|
|
10346
10978
|
const all = this.deps.toolRegistry.list();
|
|
@@ -10350,7 +10982,7 @@ var MultiAgentHost = class {
|
|
|
10350
10982
|
}
|
|
10351
10983
|
subagentToolRegistry(allow) {
|
|
10352
10984
|
if (!allow || allow.length === 0) return this.deps.toolRegistry;
|
|
10353
|
-
const sub =
|
|
10985
|
+
const sub = new ToolRegistry();
|
|
10354
10986
|
for (const t of this.filterTools(allow)) sub.register(t);
|
|
10355
10987
|
return sub;
|
|
10356
10988
|
}
|
|
@@ -10595,6 +11227,20 @@ var MultiAgentHost = class {
|
|
|
10595
11227
|
}
|
|
10596
11228
|
}
|
|
10597
11229
|
};
|
|
11230
|
+
function buildRoutingRunner(config, host) {
|
|
11231
|
+
const standardRunner = makeAgentSubagentRunner({
|
|
11232
|
+
factory: host.makeSubagentFactory(config),
|
|
11233
|
+
fleetBus: host.getDirector()?.fleet ?? NULL_FLEET_BUS
|
|
11234
|
+
});
|
|
11235
|
+
return async (task, ctx) => {
|
|
11236
|
+
const subCfg = ctx.config;
|
|
11237
|
+
if (subCfg.provider === "acp") {
|
|
11238
|
+
const cacheKey = subCfg.role ?? subCfg.name ?? subCfg.id;
|
|
11239
|
+
return host.buildACPRunner(cacheKey).then((r) => r(task, ctx));
|
|
11240
|
+
}
|
|
11241
|
+
return standardRunner(task, ctx);
|
|
11242
|
+
};
|
|
11243
|
+
}
|
|
10598
11244
|
function makePromptDelegate(reader) {
|
|
10599
11245
|
return async (tool, input, suggestedPattern) => {
|
|
10600
11246
|
process.stdout.write("\x07");
|
|
@@ -12068,7 +12714,11 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
12068
12714
|
projectRoot,
|
|
12069
12715
|
compactor: container.resolve(TOKENS.Compactor),
|
|
12070
12716
|
maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
|
|
12071
|
-
onIteration: broadcastEternalIteration
|
|
12717
|
+
onIteration: broadcastEternalIteration,
|
|
12718
|
+
// Real per-role factory: each dispatched slot runs as a fresh,
|
|
12719
|
+
// isolated agent with the role's filtered tools + persona prompt
|
|
12720
|
+
// (instead of sharing the leader agent's Context).
|
|
12721
|
+
subagentFactory: multiAgentHost.makeSubagentFactory(config)
|
|
12072
12722
|
});
|
|
12073
12723
|
}
|
|
12074
12724
|
void parallelEngine.prime?.();
|
|
@@ -12095,10 +12745,13 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
12095
12745
|
onBeforeExit: async () => {
|
|
12096
12746
|
const { spawn: spawn3 } = await import('child_process');
|
|
12097
12747
|
const cwd2 = projectRoot;
|
|
12098
|
-
const statusResult = await new Promise((resolve4) => {
|
|
12748
|
+
const statusResult = await new Promise((resolve4, reject) => {
|
|
12099
12749
|
const child = spawn3("git", ["status", "--porcelain"], { cwd: cwd2, stdio: ["ignore", "pipe", "pipe"] });
|
|
12100
12750
|
let stdout = "";
|
|
12101
|
-
child.stdout?.on("data", (d) =>
|
|
12751
|
+
child.stdout?.on("data", (d) => {
|
|
12752
|
+
stdout += d;
|
|
12753
|
+
});
|
|
12754
|
+
child.on("error", reject);
|
|
12102
12755
|
child.on("close", (code) => resolve4({ stdout, code: code ?? 0 }));
|
|
12103
12756
|
});
|
|
12104
12757
|
if (statusResult.stdout.trim().length > 0) {
|
|
@@ -12143,7 +12796,11 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
12143
12796
|
provider: context.provider,
|
|
12144
12797
|
model: context.model
|
|
12145
12798
|
});
|
|
12146
|
-
}
|
|
12799
|
+
},
|
|
12800
|
+
onDispatchClassify: makeProviderClassifier(
|
|
12801
|
+
context.provider,
|
|
12802
|
+
context.model
|
|
12803
|
+
)
|
|
12147
12804
|
});
|
|
12148
12805
|
for (const cmd of slashCmds) slashRegistry.register(cmd);
|
|
12149
12806
|
const eternalFlag = typeof flags["eternal"] === "string" ? flags["eternal"].trim() : "";
|
|
@@ -12262,12 +12919,12 @@ if (isMain) {
|
|
|
12262
12919
|
main(process.argv.slice(2)).then(
|
|
12263
12920
|
(c) => {
|
|
12264
12921
|
process.exitCode = c;
|
|
12265
|
-
setTimeout(() => process.exit(c),
|
|
12922
|
+
setTimeout(() => process.exit(c), 500).unref();
|
|
12266
12923
|
},
|
|
12267
12924
|
(err) => {
|
|
12268
12925
|
process.stderr.write((err instanceof Error ? err.stack : String(err)) + "\n");
|
|
12269
12926
|
process.exitCode = 1;
|
|
12270
|
-
setTimeout(() => process.exit(1),
|
|
12927
|
+
setTimeout(() => process.exit(1), 500).unref();
|
|
12271
12928
|
}
|
|
12272
12929
|
);
|
|
12273
12930
|
}
|