@wrongstack/cli 1.0.3 → 1.0.4
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/{acp-JK4ZQO3R.js → acp-AB54FWM2.js} +120 -38
- package/dist/acp-agent-list.d.ts +14 -0
- package/dist/acp-hq-telemetry.d.ts +9 -2
- package/dist/acp-mcp-servers.d.ts +49 -0
- package/dist/acp-server-agent.d.ts +18 -6
- package/dist/{auth-XD3GN5RJ.js → auth-WDMSNZ3S.js} +2 -2
- package/dist/boot/short-circuit-hq.d.ts +2 -0
- package/dist/boot/tool-registry.d.ts +18 -1
- package/dist/{chunk-C2E24IXZ.js → chunk-3I5ZJXM5.js} +147 -241
- package/dist/{chunk-JHPXJRRI.js → chunk-4XCBEUBX.js} +13 -3
- package/dist/{chunk-FPIWTJ2G.js → chunk-6WRKAACA.js} +4 -4
- package/dist/chunk-AUQDGU5D.js +32 -0
- package/dist/{chunk-IUEKNHPT.js → chunk-BIWE22O5.js} +10 -10
- package/dist/{chunk-VOTTENR5.js → chunk-CDY4D5QK.js} +2 -2
- package/dist/{chunk-E6RFXHWD.js → chunk-EK2P53GL.js} +3 -2
- package/dist/chunk-I7V2OYVH.js +242 -0
- package/dist/{chunk-MXEJEYT7.js → chunk-JX6KL7OF.js} +15 -5
- package/dist/{chunk-GMY45LO6.js → chunk-KEPABQYT.js} +101 -2
- package/dist/{chunk-IMWXXF4J.js → chunk-KNMIBKLV.js} +79 -1
- package/dist/chunk-Q7E3BPDU.js +62 -0
- package/dist/{chunk-4NHD5OXJ.js → chunk-U6PGWDMZ.js} +5 -5
- package/dist/{chunk-U6KCWRMC.js → chunk-XJ7K67PP.js} +5 -1
- package/dist/{cli-context-Q6QP534K.js → cli-context-WFC3BNGM.js} +116 -21
- package/dist/{cli-main-JQXCDZEA.js → cli-main-GU3YZLMO.js} +1221 -694
- package/dist/{diag-doctor-7LYUYU35.js → diag-doctor-PCER6KHH.js} +2 -2
- package/dist/{execution-KH7WJNAX.js → execution-RAZDDQFC.js} +107 -15
- package/dist/execution-specialist-trigger.d.ts +28 -0
- package/dist/fleet/acp-live-progress.d.ts +19 -0
- package/dist/fleet/host-acp-runner-cache.d.ts +3 -1
- package/dist/fleet/host-acp.d.ts +14 -2
- package/dist/fleet/host.d.ts +1 -0
- package/dist/{hq-IG3YRGCO.js → hq-5XHEIIH6.js} +127 -8
- package/dist/hq-command-controller.d.ts +31 -2
- package/dist/hq-server/client-address.d.ts +2 -0
- package/dist/hq-server/ip-allowlist.d.ts +16 -0
- package/dist/hq-server/preflight.d.ts +1 -0
- package/dist/hq-server/routes/auth/password-routes.d.ts +1 -1
- package/dist/hq-server/routes.d.ts +5 -4
- package/dist/hq-server/secret-input.d.ts +7 -0
- package/dist/hq-server/types.d.ts +3 -2
- package/dist/hq-server/upgrade-handler.d.ts +2 -0
- package/dist/hq-server/utils.d.ts +6 -0
- package/dist/{hq-server-OILU3NPI.js → hq-server-CNUTBWTK.js} +4 -3
- package/dist/hq-server.d.ts +4 -0
- package/dist/hq-service-L5INQFM4.js +507 -0
- package/dist/hq-service.d.ts +22 -0
- package/dist/index.js +4 -4
- package/dist/ip-allowlist-NHXO6AKJ.js +9 -0
- package/dist/live-settings-input.d.ts +2 -0
- package/dist/{per-subcommand-help-BWBHYVJO.js → per-subcommand-help-DHTYU524.js} +2 -2
- package/dist/{plugins-U7KGQAUG.js → plugins-WJLW6ULV.js} +2 -2
- package/dist/projects-LMNAGGEU.js +36 -0
- package/dist/{proxy-wiring-MGRSKZZB.js → proxy-wiring-K2DORFFL.js} +3 -3
- package/dist/short-circuit-flags-YDMLAQ5R.js +10 -0
- package/dist/slash-commands/acp.d.ts +3 -0
- package/dist/slash-commands/command-context.d.ts +22 -0
- package/dist/slash-commands/ensemble.d.ts +1 -1
- package/dist/slash-commands/profile.d.ts +7 -0
- package/dist/subcommands/handlers/hq.d.ts +1 -0
- package/dist/subcommands/handlers/projects.d.ts +14 -0
- package/dist/{subcommands-CUTGBDKP.js → subcommands-7N4RIK6H.js} +2 -2
- package/dist/{version-help-MYKF724O.js → version-help-WXI42PZ3.js} +2 -2
- package/dist/{webui-server-QWBUT7BO.js → webui-server-OBV5JYJV.js} +9 -3
- package/dist/wiring/cli-slash-commands-setup.d.ts +7 -0
- package/dist/wiring/codebase-embeddings.d.ts +2 -2
- package/dist/wiring/command-host-state.d.ts +7 -0
- package/dist/wiring/pipeline.d.ts +2 -0
- package/dist/wiring/provider-runtime-setup.d.ts +1 -1
- package/dist/wiring/session-command-handlers.d.ts +2 -1
- package/dist/wiring/slash-commands.d.ts +1 -0
- package/package.json +28 -28
- package/dist/chunk-Q5GTM25S.js +0 -17
- package/dist/projects-L6C6XLDY.js +0 -37
- package/dist/short-circuit-flags-ZNZX44MC.js +0 -10
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createHqCommandDispatcher
|
|
3
|
-
} from "./chunk-IMWXXF4J.js";
|
|
4
|
-
import {
|
|
5
|
-
runProjectPicker
|
|
6
|
-
} from "./chunk-WQZW3RDP.js";
|
|
7
|
-
import {
|
|
8
|
-
ensureProjectDataDir,
|
|
9
|
-
findProject,
|
|
10
|
-
generateSlug,
|
|
11
|
-
loadManifest,
|
|
12
|
-
saveManifest
|
|
13
|
-
} from "./chunk-IS2PPBQV.js";
|
|
14
|
-
import {
|
|
15
|
-
getWrongTrace
|
|
16
|
-
} from "./chunk-ERX4BLKP.js";
|
|
17
1
|
import {
|
|
18
2
|
MAX_TUI_THINKING_WORD_LENGTH,
|
|
19
3
|
clearSuggestions,
|
|
@@ -23,6 +7,9 @@ import {
|
|
|
23
7
|
runGit,
|
|
24
8
|
setSuggestions
|
|
25
9
|
} from "./chunk-SXJTVNWF.js";
|
|
10
|
+
import {
|
|
11
|
+
setupPlugins
|
|
12
|
+
} from "./chunk-XJ7K67PP.js";
|
|
26
13
|
import {
|
|
27
14
|
deriveFsAccessPair,
|
|
28
15
|
filterSafeForProject,
|
|
@@ -31,8 +18,37 @@ import {
|
|
|
31
18
|
persistTelegramConfig
|
|
32
19
|
} from "./chunk-JACOMTIH.js";
|
|
33
20
|
import {
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
awaitFirstWrongProxyProbe,
|
|
22
|
+
bootstrapWrongProxy
|
|
23
|
+
} from "./chunk-CDY4D5QK.js";
|
|
24
|
+
import {
|
|
25
|
+
advanceToNextTask,
|
|
26
|
+
findSpec,
|
|
27
|
+
formatElapsed,
|
|
28
|
+
gatherProjectContext,
|
|
29
|
+
getSessionState,
|
|
30
|
+
getTaskProgress,
|
|
31
|
+
matchTaskNode,
|
|
32
|
+
sddState
|
|
33
|
+
} from "./chunk-JHUJUIOH.js";
|
|
34
|
+
import {
|
|
35
|
+
createHqCommandDispatcher,
|
|
36
|
+
createProjectKanbanAssignHandler,
|
|
37
|
+
createProjectKanbanTransitionHandler
|
|
38
|
+
} from "./chunk-KNMIBKLV.js";
|
|
39
|
+
import {
|
|
40
|
+
runProjectPicker
|
|
41
|
+
} from "./chunk-WQZW3RDP.js";
|
|
42
|
+
import {
|
|
43
|
+
ensureProjectDataDir,
|
|
44
|
+
findProject,
|
|
45
|
+
generateSlug,
|
|
46
|
+
loadManifest,
|
|
47
|
+
saveManifest
|
|
48
|
+
} from "./chunk-IS2PPBQV.js";
|
|
49
|
+
import {
|
|
50
|
+
getWrongTrace
|
|
51
|
+
} from "./chunk-ERX4BLKP.js";
|
|
36
52
|
import {
|
|
37
53
|
configureSimpleUiRuntimeContext,
|
|
38
54
|
detectProjectFacts,
|
|
@@ -48,26 +64,12 @@ import {
|
|
|
48
64
|
import {
|
|
49
65
|
renderDeepHelpToString,
|
|
50
66
|
renderFocusedHelpToString
|
|
51
|
-
} from "./chunk-
|
|
52
|
-
import {
|
|
53
|
-
awaitFirstWrongProxyProbe,
|
|
54
|
-
bootstrapWrongProxy
|
|
55
|
-
} from "./chunk-VOTTENR5.js";
|
|
56
|
-
import {
|
|
57
|
-
advanceToNextTask,
|
|
58
|
-
findSpec,
|
|
59
|
-
formatElapsed,
|
|
60
|
-
gatherProjectContext,
|
|
61
|
-
getSessionState,
|
|
62
|
-
getTaskProgress,
|
|
63
|
-
matchTaskNode,
|
|
64
|
-
sddState
|
|
65
|
-
} from "./chunk-JHUJUIOH.js";
|
|
67
|
+
} from "./chunk-JX6KL7OF.js";
|
|
66
68
|
import {
|
|
67
69
|
PLUGIN_AUDIT_ENTRIES,
|
|
68
70
|
runPluginManagementCommand
|
|
69
71
|
} from "./chunk-SIS37L3W.js";
|
|
70
|
-
import "./chunk-
|
|
72
|
+
import "./chunk-4XCBEUBX.js";
|
|
71
73
|
import {
|
|
72
74
|
recordGateDecision
|
|
73
75
|
} from "./chunk-D3K4NYQL.js";
|
|
@@ -80,11 +82,12 @@ import {
|
|
|
80
82
|
import {
|
|
81
83
|
buildProviderForId,
|
|
82
84
|
createGracefulShutdown,
|
|
85
|
+
formatAcpAgentList,
|
|
83
86
|
loadCachedAcpRegistry,
|
|
84
87
|
refreshAcpRegistry,
|
|
85
88
|
resolveProviderCfg,
|
|
86
89
|
setupProvider
|
|
87
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-KEPABQYT.js";
|
|
88
91
|
import {
|
|
89
92
|
startCliHqConnection
|
|
90
93
|
} from "./chunk-TE3Q32JP.js";
|
|
@@ -113,7 +116,7 @@ import {
|
|
|
113
116
|
} from "./chunk-YMXXOOFN.js";
|
|
114
117
|
import {
|
|
115
118
|
parseSpawnFlags
|
|
116
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-EK2P53GL.js";
|
|
117
120
|
import "./chunk-SIGUWXAA.js";
|
|
118
121
|
import "./chunk-DAKVBMD4.js";
|
|
119
122
|
import {
|
|
@@ -1250,7 +1253,7 @@ function createSubagentWrongTraceHookRunner(sessionId, emit) {
|
|
|
1250
1253
|
|
|
1251
1254
|
// src/fleet/host.ts
|
|
1252
1255
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
1253
|
-
import { createProjectAgentRoster } from "@wrongstack/core/agent-catalog";
|
|
1256
|
+
import { createProjectAgentRoster, recordDispatch } from "@wrongstack/core/agent-catalog";
|
|
1254
1257
|
import {
|
|
1255
1258
|
AdaptiveConcurrencyController,
|
|
1256
1259
|
areSubagentsAllowedForSession as areSubagentsAllowedForSession2,
|
|
@@ -1269,27 +1272,352 @@ import {
|
|
|
1269
1272
|
} from "@wrongstack/core/types";
|
|
1270
1273
|
import { wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
1271
1274
|
|
|
1275
|
+
// src/fleet/host-event-bridge.ts
|
|
1276
|
+
import * as path2 from "node:path";
|
|
1277
|
+
var BRIDGE_TEXT_CAP = 360;
|
|
1278
|
+
var BRIDGE_OUTPUT_CAP = 4096;
|
|
1279
|
+
function compactText(value, cap = BRIDGE_TEXT_CAP) {
|
|
1280
|
+
return value.length <= cap ? value : `${value.slice(0, cap - 1)}\u2026`;
|
|
1281
|
+
}
|
|
1282
|
+
function lineCount(value) {
|
|
1283
|
+
return value.length === 0 ? 0 : value.split(/\r?\n/).length;
|
|
1284
|
+
}
|
|
1285
|
+
function patchDelta(value) {
|
|
1286
|
+
let addedLines = 0;
|
|
1287
|
+
let removedLines = 0;
|
|
1288
|
+
const hunkStart = value.indexOf("@@");
|
|
1289
|
+
if (hunkStart === -1) return { addedLines, removedLines };
|
|
1290
|
+
for (const line of value.slice(hunkStart).split(/\r?\n/)) {
|
|
1291
|
+
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
1292
|
+
if (line.startsWith("+")) addedLines += 1;
|
|
1293
|
+
else if (line.startsWith("-")) removedLines += 1;
|
|
1294
|
+
}
|
|
1295
|
+
return { addedLines, removedLines };
|
|
1296
|
+
}
|
|
1297
|
+
function compactBridgeToolInput(input) {
|
|
1298
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
1299
|
+
return typeof input === "string" ? compactText(input) : input;
|
|
1300
|
+
}
|
|
1301
|
+
const source = input;
|
|
1302
|
+
const compact = {};
|
|
1303
|
+
for (const key of [
|
|
1304
|
+
"file_path",
|
|
1305
|
+
"filePath",
|
|
1306
|
+
"path",
|
|
1307
|
+
"filename",
|
|
1308
|
+
"target_file",
|
|
1309
|
+
"targetFile",
|
|
1310
|
+
"line",
|
|
1311
|
+
"start_line",
|
|
1312
|
+
"startLine",
|
|
1313
|
+
"end_line",
|
|
1314
|
+
"endLine",
|
|
1315
|
+
"offset",
|
|
1316
|
+
"limit",
|
|
1317
|
+
"command",
|
|
1318
|
+
"cmd",
|
|
1319
|
+
"url",
|
|
1320
|
+
"href",
|
|
1321
|
+
"uri",
|
|
1322
|
+
"query",
|
|
1323
|
+
"pattern"
|
|
1324
|
+
]) {
|
|
1325
|
+
const value = source[key];
|
|
1326
|
+
if (typeof value === "string") compact[key] = compactText(value);
|
|
1327
|
+
else if (typeof value === "number" || typeof value === "boolean") compact[key] = value;
|
|
1328
|
+
}
|
|
1329
|
+
const content = [source["content"], source["text"], source["data"]].find(
|
|
1330
|
+
(value) => typeof value === "string"
|
|
1331
|
+
);
|
|
1332
|
+
const oldText = [source["old_string"], source["oldString"], source["search"]].find(
|
|
1333
|
+
(value) => typeof value === "string"
|
|
1334
|
+
);
|
|
1335
|
+
const newText = [source["new_string"], source["newString"], source["replacement"]].find(
|
|
1336
|
+
(value) => typeof value === "string"
|
|
1337
|
+
);
|
|
1338
|
+
const patch = [source["patch"], source["diff"]].find(
|
|
1339
|
+
(value) => typeof value === "string"
|
|
1340
|
+
);
|
|
1341
|
+
const bodies = [content, oldText, newText, patch].filter(
|
|
1342
|
+
(value) => value !== void 0
|
|
1343
|
+
);
|
|
1344
|
+
if (bodies.length > 0) {
|
|
1345
|
+
compact["_bodyBytes"] = bodies.reduce(
|
|
1346
|
+
(total, value) => total + Buffer.byteLength(value, "utf8"),
|
|
1347
|
+
0
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
if (content !== void 0) compact["inputLines"] = lineCount(content);
|
|
1351
|
+
if (oldText !== void 0) compact["oldLines"] = lineCount(oldText);
|
|
1352
|
+
if (newText !== void 0) compact["newLines"] = lineCount(newText);
|
|
1353
|
+
if (patch !== void 0) {
|
|
1354
|
+
const delta = patchDelta(patch);
|
|
1355
|
+
compact["addedLines"] = delta.addedLines;
|
|
1356
|
+
compact["removedLines"] = delta.removedLines;
|
|
1357
|
+
}
|
|
1358
|
+
return compact;
|
|
1359
|
+
}
|
|
1360
|
+
function compactBridgeToolOutput(output) {
|
|
1361
|
+
return output === void 0 ? void 0 : compactText(output, BRIDGE_OUTPUT_CAP);
|
|
1362
|
+
}
|
|
1363
|
+
function installSubagentEventBridge(opts) {
|
|
1364
|
+
const {
|
|
1365
|
+
events,
|
|
1366
|
+
hostEvents,
|
|
1367
|
+
hostSessionId,
|
|
1368
|
+
projectRoot,
|
|
1369
|
+
effectiveCfg,
|
|
1370
|
+
subCfg,
|
|
1371
|
+
tokenCounter,
|
|
1372
|
+
subagentProvider,
|
|
1373
|
+
subagentModel
|
|
1374
|
+
} = opts;
|
|
1375
|
+
const subagentId = () => effectiveCfg.id ?? effectiveCfg.name ?? "subagent";
|
|
1376
|
+
const agentName = (name) => name ?? subCfg.name;
|
|
1377
|
+
const offToolStartedBridge = events.on("tool.started", (e) => {
|
|
1378
|
+
hostEvents.emit("subagent.tool_started", {
|
|
1379
|
+
sessionId: hostSessionId,
|
|
1380
|
+
agentSessionId: e.sessionId,
|
|
1381
|
+
subagentId: subagentId(),
|
|
1382
|
+
agentName: agentName(e.agentName),
|
|
1383
|
+
traceId: e.traceId,
|
|
1384
|
+
id: e.id,
|
|
1385
|
+
name: e.name,
|
|
1386
|
+
input: compactBridgeToolInput(e.input)
|
|
1387
|
+
});
|
|
1388
|
+
});
|
|
1389
|
+
const offToolProgressBridge = events.on("tool.progress", (e) => {
|
|
1390
|
+
if (e.event.type !== "file_changed") return;
|
|
1391
|
+
const rawPath = e.event.path ?? (typeof e.event.data?.["path"] === "string" ? e.event.data["path"] : void 0);
|
|
1392
|
+
if (!rawPath) return;
|
|
1393
|
+
const filePath = path2.normalize(
|
|
1394
|
+
path2.isAbsolute(rawPath) ? rawPath : path2.resolve(projectRoot, rawPath)
|
|
1395
|
+
);
|
|
1396
|
+
hostEvents.emit("file.activity", {
|
|
1397
|
+
filePath,
|
|
1398
|
+
operation: e.event.operation ?? "edit",
|
|
1399
|
+
phase: "changed",
|
|
1400
|
+
source: "deterministic",
|
|
1401
|
+
at: Date.now(),
|
|
1402
|
+
sessionId: e.sessionId,
|
|
1403
|
+
traceId: e.traceId,
|
|
1404
|
+
agentId: subagentId(),
|
|
1405
|
+
agentName: agentName(e.agentName),
|
|
1406
|
+
toolUseId: e.id,
|
|
1407
|
+
toolName: e.name,
|
|
1408
|
+
line: e.event.line,
|
|
1409
|
+
endLine: e.event.endLine
|
|
1410
|
+
});
|
|
1411
|
+
});
|
|
1412
|
+
const offToolBridge = events.on("tool.executed", (e) => {
|
|
1413
|
+
hostEvents.emit("subagent.tool_executed", {
|
|
1414
|
+
sessionId: hostSessionId,
|
|
1415
|
+
agentSessionId: e.sessionId,
|
|
1416
|
+
subagentId: subagentId(),
|
|
1417
|
+
agentName: agentName(e.agentName),
|
|
1418
|
+
traceId: e.traceId,
|
|
1419
|
+
id: e.id,
|
|
1420
|
+
name: e.name,
|
|
1421
|
+
durationMs: e.durationMs,
|
|
1422
|
+
ok: e.ok,
|
|
1423
|
+
input: compactBridgeToolInput(e.input),
|
|
1424
|
+
output: compactBridgeToolOutput(e.output),
|
|
1425
|
+
outputBytes: e.outputBytes,
|
|
1426
|
+
outputTokens: e.outputTokens,
|
|
1427
|
+
outputLines: e.outputLines
|
|
1428
|
+
});
|
|
1429
|
+
});
|
|
1430
|
+
const offSummaryBridge = events.on("subagent.iteration_summary", (e) => {
|
|
1431
|
+
hostEvents.emit("subagent.iteration_summary", {
|
|
1432
|
+
...e,
|
|
1433
|
+
sessionId: hostSessionId,
|
|
1434
|
+
subagentId: subagentId()
|
|
1435
|
+
});
|
|
1436
|
+
});
|
|
1437
|
+
const offCtxBridge = events.on("ctx.pct", (e) => {
|
|
1438
|
+
hostEvents.emit("subagent.ctx_pct", {
|
|
1439
|
+
sessionId: hostSessionId,
|
|
1440
|
+
subagentId: subagentId(),
|
|
1441
|
+
load: e.load,
|
|
1442
|
+
tokens: e.tokens,
|
|
1443
|
+
maxContext: e.maxContext
|
|
1444
|
+
});
|
|
1445
|
+
});
|
|
1446
|
+
const offTokenBridge = events.on("token.accounted", (e) => {
|
|
1447
|
+
hostEvents.emit("subagent.token_accounted", {
|
|
1448
|
+
sessionId: hostSessionId,
|
|
1449
|
+
subagentId: subagentId(),
|
|
1450
|
+
agentName: agentName(),
|
|
1451
|
+
provider: e.provider ?? subagentProvider,
|
|
1452
|
+
model: e.model ?? subagentModel,
|
|
1453
|
+
// The subagent counter's own running total, not the leader's.
|
|
1454
|
+
usage: tokenCounter ? tokenCounter.total() : e.usage,
|
|
1455
|
+
deltaUsage: e.deltaUsage,
|
|
1456
|
+
cost: e.cost
|
|
1457
|
+
});
|
|
1458
|
+
});
|
|
1459
|
+
const bridgeAttempt = (outcome, e) => {
|
|
1460
|
+
hostEvents.emit("subagent.provider_attempt", {
|
|
1461
|
+
sessionId: hostSessionId,
|
|
1462
|
+
subagentId: subagentId(),
|
|
1463
|
+
agentName: agentName(),
|
|
1464
|
+
outcome,
|
|
1465
|
+
provider: e.providerId ?? subagentProvider,
|
|
1466
|
+
model: e.model ?? subagentModel,
|
|
1467
|
+
attempt: e.attempt,
|
|
1468
|
+
durationMs: e.durationMs,
|
|
1469
|
+
stopReason: e.stopReason,
|
|
1470
|
+
usage: e.usage,
|
|
1471
|
+
description: e.description,
|
|
1472
|
+
status: e.status,
|
|
1473
|
+
failureKind: e.failureKind,
|
|
1474
|
+
retryable: e.retryable,
|
|
1475
|
+
traceId: e.traceId,
|
|
1476
|
+
logicalRequestId: e.logicalRequestId,
|
|
1477
|
+
promptManifestId: e.promptManifestId,
|
|
1478
|
+
attemptId: e.attemptId
|
|
1479
|
+
});
|
|
1480
|
+
};
|
|
1481
|
+
const offAttemptStarted = events.on(
|
|
1482
|
+
"provider.attempt.started",
|
|
1483
|
+
(e) => bridgeAttempt("started", e)
|
|
1484
|
+
);
|
|
1485
|
+
const offAttemptCompleted = events.on(
|
|
1486
|
+
"provider.attempt.completed",
|
|
1487
|
+
(e) => bridgeAttempt("completed", e)
|
|
1488
|
+
);
|
|
1489
|
+
const offAttemptFailed = events.on("provider.attempt.failed", (e) => bridgeAttempt("failed", e));
|
|
1490
|
+
return () => {
|
|
1491
|
+
offToolStartedBridge();
|
|
1492
|
+
offToolProgressBridge();
|
|
1493
|
+
offToolBridge();
|
|
1494
|
+
offSummaryBridge();
|
|
1495
|
+
offCtxBridge();
|
|
1496
|
+
offTokenBridge();
|
|
1497
|
+
offAttemptStarted();
|
|
1498
|
+
offAttemptCompleted();
|
|
1499
|
+
offAttemptFailed();
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// src/fleet/acp-live-progress.ts
|
|
1504
|
+
function toolName(title, kind) {
|
|
1505
|
+
const t = title?.trim();
|
|
1506
|
+
if (t) return t;
|
|
1507
|
+
return kind && kind.length > 0 ? kind : "tool";
|
|
1508
|
+
}
|
|
1509
|
+
function publishAcpLiveProgress(opts) {
|
|
1510
|
+
const { event, subagentId, agentName, sessionId, taskId, fleet, hostEvents } = opts;
|
|
1511
|
+
const ts = Date.now();
|
|
1512
|
+
switch (event.type) {
|
|
1513
|
+
case "message": {
|
|
1514
|
+
if (!event.text) return;
|
|
1515
|
+
fleet?.emit({
|
|
1516
|
+
subagentId,
|
|
1517
|
+
taskId,
|
|
1518
|
+
ts,
|
|
1519
|
+
type: "provider.text_delta",
|
|
1520
|
+
payload: { text: event.text }
|
|
1521
|
+
});
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
case "thought": {
|
|
1525
|
+
if (!event.text) return;
|
|
1526
|
+
fleet?.emit({
|
|
1527
|
+
subagentId,
|
|
1528
|
+
taskId,
|
|
1529
|
+
ts,
|
|
1530
|
+
type: "provider.thinking_delta",
|
|
1531
|
+
payload: { text: event.text }
|
|
1532
|
+
});
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
case "tool_call": {
|
|
1536
|
+
const name = toolName(event.toolCall.title, event.toolCall.kind);
|
|
1537
|
+
const id = event.toolCall.toolCallId;
|
|
1538
|
+
const input = compactBridgeToolInput(event.toolCall.rawInput);
|
|
1539
|
+
fleet?.emit({
|
|
1540
|
+
subagentId,
|
|
1541
|
+
taskId,
|
|
1542
|
+
ts,
|
|
1543
|
+
type: "tool.started",
|
|
1544
|
+
payload: { id, name, input }
|
|
1545
|
+
});
|
|
1546
|
+
hostEvents?.emit("subagent.tool_started", {
|
|
1547
|
+
sessionId,
|
|
1548
|
+
subagentId,
|
|
1549
|
+
agentName,
|
|
1550
|
+
taskId,
|
|
1551
|
+
id,
|
|
1552
|
+
name,
|
|
1553
|
+
input
|
|
1554
|
+
});
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
case "tool_call_update": {
|
|
1558
|
+
const status = event.toolCall.status;
|
|
1559
|
+
if (status !== "completed" && status !== "failed") return;
|
|
1560
|
+
const name = toolName(event.toolCall.title, event.toolCall.kind);
|
|
1561
|
+
const id = event.toolCall.toolCallId;
|
|
1562
|
+
const ok = status === "completed";
|
|
1563
|
+
const output = event.toolCall.rawOutput !== void 0 ? safeJson(event.toolCall.rawOutput) : void 0;
|
|
1564
|
+
fleet?.emit({
|
|
1565
|
+
subagentId,
|
|
1566
|
+
taskId,
|
|
1567
|
+
ts,
|
|
1568
|
+
type: "tool.executed",
|
|
1569
|
+
payload: { id, name, ok, output, durationMs: 0 }
|
|
1570
|
+
});
|
|
1571
|
+
hostEvents?.emit("subagent.tool_executed", {
|
|
1572
|
+
sessionId,
|
|
1573
|
+
subagentId,
|
|
1574
|
+
agentName,
|
|
1575
|
+
taskId,
|
|
1576
|
+
id,
|
|
1577
|
+
name,
|
|
1578
|
+
ok,
|
|
1579
|
+
durationMs: 0,
|
|
1580
|
+
output
|
|
1581
|
+
});
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
case "diff": {
|
|
1585
|
+
const filePath = event.diff.path;
|
|
1586
|
+
if (!filePath) return;
|
|
1587
|
+
hostEvents?.emit("file.activity", {
|
|
1588
|
+
filePath,
|
|
1589
|
+
operation: event.diff.oldText === null ? "write" : "edit",
|
|
1590
|
+
phase: "changed",
|
|
1591
|
+
source: "deterministic",
|
|
1592
|
+
at: ts,
|
|
1593
|
+
sessionId,
|
|
1594
|
+
agentId: subagentId,
|
|
1595
|
+
agentName
|
|
1596
|
+
});
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
default:
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
function safeJson(value) {
|
|
1604
|
+
try {
|
|
1605
|
+
const text = JSON.stringify(value);
|
|
1606
|
+
return text.length > 4096 ? `${text.slice(0, 4095)}\u2026` : text;
|
|
1607
|
+
} catch {
|
|
1608
|
+
return void 0;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1272
1612
|
// src/fleet/host-acp.ts
|
|
1273
1613
|
import {
|
|
1274
|
-
ACP_AGENT_COMMANDS,
|
|
1275
1614
|
defaultPermissionPolicy,
|
|
1276
|
-
|
|
1277
|
-
|
|
1615
|
+
makeACPSubagentRunner,
|
|
1616
|
+
resolveAcpAgentCommand
|
|
1278
1617
|
} from "@wrongstack/acp";
|
|
1279
1618
|
import { ToolValidationError } from "@wrongstack/core/types";
|
|
1280
|
-
function buildAcpSubagentRunner(subagentId) {
|
|
1281
|
-
|
|
1282
|
-
if (!cmd) {
|
|
1283
|
-
const desc = findAgentDescriptor(subagentId);
|
|
1284
|
-
if (desc) {
|
|
1285
|
-
cmd = {
|
|
1286
|
-
command: desc.acp.command,
|
|
1287
|
-
args: [...desc.acp.args ?? []],
|
|
1288
|
-
role: subagentId,
|
|
1289
|
-
...desc.acp.env ? { env: desc.acp.env } : {}
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1619
|
+
function buildAcpSubagentRunner(subagentId, options) {
|
|
1620
|
+
const cmd = resolveAcpAgentCommand(subagentId, options?.overrides, options?.live);
|
|
1293
1621
|
if (!cmd) {
|
|
1294
1622
|
throw new ToolValidationError({
|
|
1295
1623
|
message: `Unknown ACP agent: ${subagentId}`,
|
|
@@ -1297,11 +1625,15 @@ function buildAcpSubagentRunner(subagentId) {
|
|
|
1297
1625
|
context: { requested: subagentId }
|
|
1298
1626
|
});
|
|
1299
1627
|
}
|
|
1300
|
-
return makeACPSubagentRunner({
|
|
1628
|
+
return makeACPSubagentRunner({
|
|
1629
|
+
...cmd,
|
|
1630
|
+
permissionPolicy: defaultPermissionPolicy,
|
|
1631
|
+
...options?.publishLive ? { publishLive: options.publishLive } : {}
|
|
1632
|
+
});
|
|
1301
1633
|
}
|
|
1302
1634
|
|
|
1303
1635
|
// src/fleet/host-helpers.ts
|
|
1304
|
-
import * as
|
|
1636
|
+
import * as path3 from "node:path";
|
|
1305
1637
|
import {
|
|
1306
1638
|
makeSessionNoteTool,
|
|
1307
1639
|
makeSubagentResultTool
|
|
@@ -1313,8 +1645,8 @@ import {
|
|
|
1313
1645
|
} from "@wrongstack/core/security";
|
|
1314
1646
|
import { makePreferSideConflictResolver } from "@wrongstack/sdd";
|
|
1315
1647
|
function isInsideDirectory(root, candidate) {
|
|
1316
|
-
const relative5 =
|
|
1317
|
-
return relative5 === "" || !relative5.startsWith("..") && !
|
|
1648
|
+
const relative5 = path3.relative(path3.resolve(root), path3.resolve(candidate));
|
|
1649
|
+
return relative5 === "" || !relative5.startsWith("..") && !path3.isAbsolute(relative5);
|
|
1318
1650
|
}
|
|
1319
1651
|
function isAgentAvailable(schedule, at = /* @__PURE__ */ new Date()) {
|
|
1320
1652
|
const parts = new Intl.DateTimeFormat("en-US", {
|
|
@@ -1467,10 +1799,12 @@ function makeFleetWorktreeConflictResolver() {
|
|
|
1467
1799
|
|
|
1468
1800
|
// src/fleet/host-acp-runner-cache.ts
|
|
1469
1801
|
var HostAcpRunnerCache = class {
|
|
1470
|
-
constructor(maxEntries = 20) {
|
|
1802
|
+
constructor(maxEntries = 20, resolveOpts) {
|
|
1471
1803
|
this.maxEntries = maxEntries;
|
|
1804
|
+
this.resolveOpts = resolveOpts;
|
|
1472
1805
|
}
|
|
1473
1806
|
maxEntries;
|
|
1807
|
+
resolveOpts;
|
|
1474
1808
|
runners = /* @__PURE__ */ new Map();
|
|
1475
1809
|
accessOrder = [];
|
|
1476
1810
|
get(subagentId) {
|
|
@@ -1479,7 +1813,7 @@ var HostAcpRunnerCache = class {
|
|
|
1479
1813
|
touchLruKey(this.accessOrder, subagentId);
|
|
1480
1814
|
return cached;
|
|
1481
1815
|
}
|
|
1482
|
-
const runner = buildAcpSubagentRunner(subagentId);
|
|
1816
|
+
const runner = buildAcpSubagentRunner(subagentId, this.resolveOpts?.());
|
|
1483
1817
|
setBoundedLruEntry(this.runners, this.accessOrder, subagentId, runner, this.maxEntries);
|
|
1484
1818
|
return runner;
|
|
1485
1819
|
}
|
|
@@ -1500,7 +1834,7 @@ function normalizeMaxConcurrent(value) {
|
|
|
1500
1834
|
|
|
1501
1835
|
// src/fleet/host-director-builder.ts
|
|
1502
1836
|
import { randomUUID } from "node:crypto";
|
|
1503
|
-
import * as
|
|
1837
|
+
import * as path4 from "node:path";
|
|
1504
1838
|
import {
|
|
1505
1839
|
DEFAULT_MAX_FLEET_SPAWNS,
|
|
1506
1840
|
FleetManager,
|
|
@@ -1532,7 +1866,7 @@ function prepareHostDirectorRuntime(input) {
|
|
|
1532
1866
|
const fleetLifecycle = config.fleet?.lifecycle;
|
|
1533
1867
|
const configuredIdleTimeoutMs = opts.subagentIdleTimeoutMs ?? fleetLifecycle?.idleTimeoutMs;
|
|
1534
1868
|
const subagentIdleTimeoutMs = typeof configuredIdleTimeoutMs === "number" && Number.isFinite(configuredIdleTimeoutMs) && configuredIdleTimeoutMs >= 0 ? configuredIdleTimeoutMs : 3e5;
|
|
1535
|
-
const defaultScratchpad = opts.sharedScratchpadPath || (opts.sessionsRoot && opts.directorRunId ?
|
|
1869
|
+
const defaultScratchpad = opts.sharedScratchpadPath || (opts.sessionsRoot && opts.directorRunId ? path4.join(opts.sessionsRoot, opts.directorRunId, "shared") : void 0);
|
|
1536
1870
|
const worktreePolicy = resolveFleetWorktreePolicy(config);
|
|
1537
1871
|
const worktrees = worktreePolicy.enabled === false || worktreePolicy.mode === "off" ? void 0 : new WorktreeManager({
|
|
1538
1872
|
projectRoot: deps.projectRoot,
|
|
@@ -1912,67 +2246,6 @@ function createHostStatusBroadcaster(input) {
|
|
|
1912
2246
|
});
|
|
1913
2247
|
}
|
|
1914
2248
|
|
|
1915
|
-
// src/fleet/host-explore-companion-registry.ts
|
|
1916
|
-
var DEFAULT_MAX_COMPANIONS = 4;
|
|
1917
|
-
var DEFAULT_IDLE_MS = 15 * 6e4;
|
|
1918
|
-
function createExploreCompanionRegistry(input) {
|
|
1919
|
-
const max = Math.max(1, input.max ?? DEFAULT_MAX_COMPANIONS);
|
|
1920
|
-
const idleMs = input.idleMs ?? DEFAULT_IDLE_MS;
|
|
1921
|
-
const now = input.now ?? Date.now;
|
|
1922
|
-
const live = /* @__PURE__ */ new Map();
|
|
1923
|
-
const stop = (sessionId) => {
|
|
1924
|
-
const entry = live.get(sessionId);
|
|
1925
|
-
if (!entry) return;
|
|
1926
|
-
live.delete(sessionId);
|
|
1927
|
-
try {
|
|
1928
|
-
entry.companion.stop();
|
|
1929
|
-
} catch {
|
|
1930
|
-
}
|
|
1931
|
-
};
|
|
1932
|
-
const sweepIdle = (keep) => {
|
|
1933
|
-
const cutoff = now() - idleMs;
|
|
1934
|
-
for (const [sessionId, entry] of [...live]) {
|
|
1935
|
-
if (sessionId === keep) continue;
|
|
1936
|
-
if (entry.lastActiveAt > cutoff) continue;
|
|
1937
|
-
stop(sessionId);
|
|
1938
|
-
}
|
|
1939
|
-
};
|
|
1940
|
-
const evictOldest = (keep) => {
|
|
1941
|
-
for (const sessionId of live.keys()) {
|
|
1942
|
-
if (sessionId === keep) continue;
|
|
1943
|
-
stop(sessionId);
|
|
1944
|
-
return;
|
|
1945
|
-
}
|
|
1946
|
-
};
|
|
1947
|
-
return {
|
|
1948
|
-
ensure(sessionId) {
|
|
1949
|
-
if (!sessionId) return;
|
|
1950
|
-
const existing = live.get(sessionId);
|
|
1951
|
-
if (existing) {
|
|
1952
|
-
live.delete(sessionId);
|
|
1953
|
-
existing.lastActiveAt = now();
|
|
1954
|
-
live.set(sessionId, existing);
|
|
1955
|
-
return;
|
|
1956
|
-
}
|
|
1957
|
-
sweepIdle(sessionId);
|
|
1958
|
-
while (live.size >= max) {
|
|
1959
|
-
const before = live.size;
|
|
1960
|
-
evictOldest(sessionId);
|
|
1961
|
-
if (live.size === before) return;
|
|
1962
|
-
}
|
|
1963
|
-
const companion = input.create(sessionId);
|
|
1964
|
-
if (!companion) return;
|
|
1965
|
-
live.set(sessionId, { companion, lastActiveAt: now() });
|
|
1966
|
-
},
|
|
1967
|
-
peek: (sessionId) => live.get(sessionId)?.companion,
|
|
1968
|
-
ids: () => [...live.keys()],
|
|
1969
|
-
release: stop,
|
|
1970
|
-
disposeAll() {
|
|
1971
|
-
for (const sessionId of [...live.keys()]) stop(sessionId);
|
|
1972
|
-
}
|
|
1973
|
-
};
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
2249
|
// src/fleet/host-explore-companion.ts
|
|
1977
2250
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1978
2251
|
import {
|
|
@@ -2071,6 +2344,67 @@ function forwardExploreFindings(input) {
|
|
|
2071
2344
|
}
|
|
2072
2345
|
}
|
|
2073
2346
|
|
|
2347
|
+
// src/fleet/host-explore-companion-registry.ts
|
|
2348
|
+
var DEFAULT_MAX_COMPANIONS = 4;
|
|
2349
|
+
var DEFAULT_IDLE_MS = 15 * 6e4;
|
|
2350
|
+
function createExploreCompanionRegistry(input) {
|
|
2351
|
+
const max = Math.max(1, input.max ?? DEFAULT_MAX_COMPANIONS);
|
|
2352
|
+
const idleMs = input.idleMs ?? DEFAULT_IDLE_MS;
|
|
2353
|
+
const now = input.now ?? Date.now;
|
|
2354
|
+
const live = /* @__PURE__ */ new Map();
|
|
2355
|
+
const stop = (sessionId) => {
|
|
2356
|
+
const entry = live.get(sessionId);
|
|
2357
|
+
if (!entry) return;
|
|
2358
|
+
live.delete(sessionId);
|
|
2359
|
+
try {
|
|
2360
|
+
entry.companion.stop();
|
|
2361
|
+
} catch {
|
|
2362
|
+
}
|
|
2363
|
+
};
|
|
2364
|
+
const sweepIdle = (keep) => {
|
|
2365
|
+
const cutoff = now() - idleMs;
|
|
2366
|
+
for (const [sessionId, entry] of [...live]) {
|
|
2367
|
+
if (sessionId === keep) continue;
|
|
2368
|
+
if (entry.lastActiveAt > cutoff) continue;
|
|
2369
|
+
stop(sessionId);
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
const evictOldest = (keep) => {
|
|
2373
|
+
for (const sessionId of live.keys()) {
|
|
2374
|
+
if (sessionId === keep) continue;
|
|
2375
|
+
stop(sessionId);
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
return {
|
|
2380
|
+
ensure(sessionId) {
|
|
2381
|
+
if (!sessionId) return;
|
|
2382
|
+
const existing = live.get(sessionId);
|
|
2383
|
+
if (existing) {
|
|
2384
|
+
live.delete(sessionId);
|
|
2385
|
+
existing.lastActiveAt = now();
|
|
2386
|
+
live.set(sessionId, existing);
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
sweepIdle(sessionId);
|
|
2390
|
+
while (live.size >= max) {
|
|
2391
|
+
const before = live.size;
|
|
2392
|
+
evictOldest(sessionId);
|
|
2393
|
+
if (live.size === before) return;
|
|
2394
|
+
}
|
|
2395
|
+
const companion = input.create(sessionId);
|
|
2396
|
+
if (!companion) return;
|
|
2397
|
+
live.set(sessionId, { companion, lastActiveAt: now() });
|
|
2398
|
+
},
|
|
2399
|
+
peek: (sessionId) => live.get(sessionId)?.companion,
|
|
2400
|
+
ids: () => [...live.keys()],
|
|
2401
|
+
release: stop,
|
|
2402
|
+
disposeAll() {
|
|
2403
|
+
for (const sessionId of [...live.keys()]) stop(sessionId);
|
|
2404
|
+
}
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2074
2408
|
// src/fleet/host-learning-scheduler.ts
|
|
2075
2409
|
import {
|
|
2076
2410
|
LearningOptimizationScheduler,
|
|
@@ -2378,19 +2712,19 @@ function emitHostLifecycleCompleted(events, sessionId, taskId, result) {
|
|
|
2378
2712
|
}
|
|
2379
2713
|
|
|
2380
2714
|
// src/fleet/host-paths.ts
|
|
2381
|
-
import * as
|
|
2715
|
+
import * as path5 from "node:path";
|
|
2382
2716
|
function applyFleetRootDefaults(opts) {
|
|
2383
2717
|
if (opts.fleetRoot && !opts.manifestPath) {
|
|
2384
|
-
opts.manifestPath =
|
|
2718
|
+
opts.manifestPath = path5.join(opts.fleetRoot, "fleet.json");
|
|
2385
2719
|
}
|
|
2386
2720
|
if (opts.fleetRoot && !opts.sharedScratchpadPath) {
|
|
2387
|
-
opts.sharedScratchpadPath =
|
|
2721
|
+
opts.sharedScratchpadPath = path5.join(opts.fleetRoot, "shared");
|
|
2388
2722
|
}
|
|
2389
2723
|
if (opts.fleetRoot && !opts.sessionsRoot) {
|
|
2390
|
-
opts.sessionsRoot =
|
|
2724
|
+
opts.sessionsRoot = path5.join(opts.fleetRoot, "subagents");
|
|
2391
2725
|
}
|
|
2392
2726
|
if (opts.fleetRoot && !opts.stateCheckpointPath) {
|
|
2393
|
-
opts.stateCheckpointPath =
|
|
2727
|
+
opts.stateCheckpointPath = path5.join(opts.fleetRoot, "director-state.json");
|
|
2394
2728
|
}
|
|
2395
2729
|
}
|
|
2396
2730
|
|
|
@@ -3013,234 +3347,6 @@ async function retrieveHostSubagentMemory(deps, getLeaderMode, subCfg, taskConte
|
|
|
3013
3347
|
}
|
|
3014
3348
|
}
|
|
3015
3349
|
|
|
3016
|
-
// src/fleet/host-event-bridge.ts
|
|
3017
|
-
import * as path5 from "node:path";
|
|
3018
|
-
var BRIDGE_TEXT_CAP = 360;
|
|
3019
|
-
var BRIDGE_OUTPUT_CAP = 4096;
|
|
3020
|
-
function compactText(value, cap = BRIDGE_TEXT_CAP) {
|
|
3021
|
-
return value.length <= cap ? value : `${value.slice(0, cap - 1)}\u2026`;
|
|
3022
|
-
}
|
|
3023
|
-
function lineCount(value) {
|
|
3024
|
-
return value.length === 0 ? 0 : value.split(/\r?\n/).length;
|
|
3025
|
-
}
|
|
3026
|
-
function patchDelta(value) {
|
|
3027
|
-
let addedLines = 0;
|
|
3028
|
-
let removedLines = 0;
|
|
3029
|
-
const hunkStart = value.indexOf("@@");
|
|
3030
|
-
if (hunkStart === -1) return { addedLines, removedLines };
|
|
3031
|
-
for (const line of value.slice(hunkStart).split(/\r?\n/)) {
|
|
3032
|
-
if (line.startsWith("+++") || line.startsWith("---")) continue;
|
|
3033
|
-
if (line.startsWith("+")) addedLines += 1;
|
|
3034
|
-
else if (line.startsWith("-")) removedLines += 1;
|
|
3035
|
-
}
|
|
3036
|
-
return { addedLines, removedLines };
|
|
3037
|
-
}
|
|
3038
|
-
function compactBridgeToolInput(input) {
|
|
3039
|
-
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
3040
|
-
return typeof input === "string" ? compactText(input) : input;
|
|
3041
|
-
}
|
|
3042
|
-
const source = input;
|
|
3043
|
-
const compact = {};
|
|
3044
|
-
for (const key of [
|
|
3045
|
-
"file_path",
|
|
3046
|
-
"filePath",
|
|
3047
|
-
"path",
|
|
3048
|
-
"filename",
|
|
3049
|
-
"target_file",
|
|
3050
|
-
"targetFile",
|
|
3051
|
-
"line",
|
|
3052
|
-
"start_line",
|
|
3053
|
-
"startLine",
|
|
3054
|
-
"end_line",
|
|
3055
|
-
"endLine",
|
|
3056
|
-
"offset",
|
|
3057
|
-
"limit",
|
|
3058
|
-
"command",
|
|
3059
|
-
"cmd",
|
|
3060
|
-
"url",
|
|
3061
|
-
"href",
|
|
3062
|
-
"uri",
|
|
3063
|
-
"query",
|
|
3064
|
-
"pattern"
|
|
3065
|
-
]) {
|
|
3066
|
-
const value = source[key];
|
|
3067
|
-
if (typeof value === "string") compact[key] = compactText(value);
|
|
3068
|
-
else if (typeof value === "number" || typeof value === "boolean") compact[key] = value;
|
|
3069
|
-
}
|
|
3070
|
-
const content = [source["content"], source["text"], source["data"]].find(
|
|
3071
|
-
(value) => typeof value === "string"
|
|
3072
|
-
);
|
|
3073
|
-
const oldText = [source["old_string"], source["oldString"], source["search"]].find(
|
|
3074
|
-
(value) => typeof value === "string"
|
|
3075
|
-
);
|
|
3076
|
-
const newText = [source["new_string"], source["newString"], source["replacement"]].find(
|
|
3077
|
-
(value) => typeof value === "string"
|
|
3078
|
-
);
|
|
3079
|
-
const patch = [source["patch"], source["diff"]].find(
|
|
3080
|
-
(value) => typeof value === "string"
|
|
3081
|
-
);
|
|
3082
|
-
const bodies = [content, oldText, newText, patch].filter(
|
|
3083
|
-
(value) => value !== void 0
|
|
3084
|
-
);
|
|
3085
|
-
if (bodies.length > 0) {
|
|
3086
|
-
compact["_bodyBytes"] = bodies.reduce(
|
|
3087
|
-
(total, value) => total + Buffer.byteLength(value, "utf8"),
|
|
3088
|
-
0
|
|
3089
|
-
);
|
|
3090
|
-
}
|
|
3091
|
-
if (content !== void 0) compact["inputLines"] = lineCount(content);
|
|
3092
|
-
if (oldText !== void 0) compact["oldLines"] = lineCount(oldText);
|
|
3093
|
-
if (newText !== void 0) compact["newLines"] = lineCount(newText);
|
|
3094
|
-
if (patch !== void 0) {
|
|
3095
|
-
const delta = patchDelta(patch);
|
|
3096
|
-
compact["addedLines"] = delta.addedLines;
|
|
3097
|
-
compact["removedLines"] = delta.removedLines;
|
|
3098
|
-
}
|
|
3099
|
-
return compact;
|
|
3100
|
-
}
|
|
3101
|
-
function compactBridgeToolOutput(output) {
|
|
3102
|
-
return output === void 0 ? void 0 : compactText(output, BRIDGE_OUTPUT_CAP);
|
|
3103
|
-
}
|
|
3104
|
-
function installSubagentEventBridge(opts) {
|
|
3105
|
-
const {
|
|
3106
|
-
events,
|
|
3107
|
-
hostEvents,
|
|
3108
|
-
hostSessionId,
|
|
3109
|
-
projectRoot,
|
|
3110
|
-
effectiveCfg,
|
|
3111
|
-
subCfg,
|
|
3112
|
-
tokenCounter,
|
|
3113
|
-
subagentProvider,
|
|
3114
|
-
subagentModel
|
|
3115
|
-
} = opts;
|
|
3116
|
-
const subagentId = () => effectiveCfg.id ?? effectiveCfg.name ?? "subagent";
|
|
3117
|
-
const agentName = (name) => name ?? subCfg.name;
|
|
3118
|
-
const offToolStartedBridge = events.on("tool.started", (e) => {
|
|
3119
|
-
hostEvents.emit("subagent.tool_started", {
|
|
3120
|
-
sessionId: hostSessionId,
|
|
3121
|
-
agentSessionId: e.sessionId,
|
|
3122
|
-
subagentId: subagentId(),
|
|
3123
|
-
agentName: agentName(e.agentName),
|
|
3124
|
-
traceId: e.traceId,
|
|
3125
|
-
id: e.id,
|
|
3126
|
-
name: e.name,
|
|
3127
|
-
input: compactBridgeToolInput(e.input)
|
|
3128
|
-
});
|
|
3129
|
-
});
|
|
3130
|
-
const offToolProgressBridge = events.on("tool.progress", (e) => {
|
|
3131
|
-
if (e.event.type !== "file_changed") return;
|
|
3132
|
-
const rawPath = e.event.path ?? (typeof e.event.data?.["path"] === "string" ? e.event.data["path"] : void 0);
|
|
3133
|
-
if (!rawPath) return;
|
|
3134
|
-
const filePath = path5.normalize(
|
|
3135
|
-
path5.isAbsolute(rawPath) ? rawPath : path5.resolve(projectRoot, rawPath)
|
|
3136
|
-
);
|
|
3137
|
-
hostEvents.emit("file.activity", {
|
|
3138
|
-
filePath,
|
|
3139
|
-
operation: e.event.operation ?? "edit",
|
|
3140
|
-
phase: "changed",
|
|
3141
|
-
source: "deterministic",
|
|
3142
|
-
at: Date.now(),
|
|
3143
|
-
sessionId: e.sessionId,
|
|
3144
|
-
traceId: e.traceId,
|
|
3145
|
-
agentId: subagentId(),
|
|
3146
|
-
agentName: agentName(e.agentName),
|
|
3147
|
-
toolUseId: e.id,
|
|
3148
|
-
toolName: e.name,
|
|
3149
|
-
line: e.event.line,
|
|
3150
|
-
endLine: e.event.endLine
|
|
3151
|
-
});
|
|
3152
|
-
});
|
|
3153
|
-
const offToolBridge = events.on("tool.executed", (e) => {
|
|
3154
|
-
hostEvents.emit("subagent.tool_executed", {
|
|
3155
|
-
sessionId: hostSessionId,
|
|
3156
|
-
agentSessionId: e.sessionId,
|
|
3157
|
-
subagentId: subagentId(),
|
|
3158
|
-
agentName: agentName(e.agentName),
|
|
3159
|
-
traceId: e.traceId,
|
|
3160
|
-
id: e.id,
|
|
3161
|
-
name: e.name,
|
|
3162
|
-
durationMs: e.durationMs,
|
|
3163
|
-
ok: e.ok,
|
|
3164
|
-
input: compactBridgeToolInput(e.input),
|
|
3165
|
-
output: compactBridgeToolOutput(e.output),
|
|
3166
|
-
outputBytes: e.outputBytes,
|
|
3167
|
-
outputTokens: e.outputTokens,
|
|
3168
|
-
outputLines: e.outputLines
|
|
3169
|
-
});
|
|
3170
|
-
});
|
|
3171
|
-
const offSummaryBridge = events.on("subagent.iteration_summary", (e) => {
|
|
3172
|
-
hostEvents.emit("subagent.iteration_summary", {
|
|
3173
|
-
...e,
|
|
3174
|
-
sessionId: hostSessionId,
|
|
3175
|
-
subagentId: subagentId()
|
|
3176
|
-
});
|
|
3177
|
-
});
|
|
3178
|
-
const offCtxBridge = events.on("ctx.pct", (e) => {
|
|
3179
|
-
hostEvents.emit("subagent.ctx_pct", {
|
|
3180
|
-
sessionId: hostSessionId,
|
|
3181
|
-
subagentId: subagentId(),
|
|
3182
|
-
load: e.load,
|
|
3183
|
-
tokens: e.tokens,
|
|
3184
|
-
maxContext: e.maxContext
|
|
3185
|
-
});
|
|
3186
|
-
});
|
|
3187
|
-
const offTokenBridge = events.on("token.accounted", (e) => {
|
|
3188
|
-
hostEvents.emit("subagent.token_accounted", {
|
|
3189
|
-
sessionId: hostSessionId,
|
|
3190
|
-
subagentId: subagentId(),
|
|
3191
|
-
agentName: agentName(),
|
|
3192
|
-
provider: e.provider ?? subagentProvider,
|
|
3193
|
-
model: e.model ?? subagentModel,
|
|
3194
|
-
// The subagent counter's own running total, not the leader's.
|
|
3195
|
-
usage: tokenCounter ? tokenCounter.total() : e.usage,
|
|
3196
|
-
deltaUsage: e.deltaUsage,
|
|
3197
|
-
cost: e.cost
|
|
3198
|
-
});
|
|
3199
|
-
});
|
|
3200
|
-
const bridgeAttempt = (outcome, e) => {
|
|
3201
|
-
hostEvents.emit("subagent.provider_attempt", {
|
|
3202
|
-
sessionId: hostSessionId,
|
|
3203
|
-
subagentId: subagentId(),
|
|
3204
|
-
agentName: agentName(),
|
|
3205
|
-
outcome,
|
|
3206
|
-
provider: e.providerId ?? subagentProvider,
|
|
3207
|
-
model: e.model ?? subagentModel,
|
|
3208
|
-
attempt: e.attempt,
|
|
3209
|
-
durationMs: e.durationMs,
|
|
3210
|
-
stopReason: e.stopReason,
|
|
3211
|
-
usage: e.usage,
|
|
3212
|
-
description: e.description,
|
|
3213
|
-
status: e.status,
|
|
3214
|
-
failureKind: e.failureKind,
|
|
3215
|
-
retryable: e.retryable,
|
|
3216
|
-
traceId: e.traceId,
|
|
3217
|
-
logicalRequestId: e.logicalRequestId,
|
|
3218
|
-
promptManifestId: e.promptManifestId,
|
|
3219
|
-
attemptId: e.attemptId
|
|
3220
|
-
});
|
|
3221
|
-
};
|
|
3222
|
-
const offAttemptStarted = events.on(
|
|
3223
|
-
"provider.attempt.started",
|
|
3224
|
-
(e) => bridgeAttempt("started", e)
|
|
3225
|
-
);
|
|
3226
|
-
const offAttemptCompleted = events.on(
|
|
3227
|
-
"provider.attempt.completed",
|
|
3228
|
-
(e) => bridgeAttempt("completed", e)
|
|
3229
|
-
);
|
|
3230
|
-
const offAttemptFailed = events.on("provider.attempt.failed", (e) => bridgeAttempt("failed", e));
|
|
3231
|
-
return () => {
|
|
3232
|
-
offToolStartedBridge();
|
|
3233
|
-
offToolProgressBridge();
|
|
3234
|
-
offToolBridge();
|
|
3235
|
-
offSummaryBridge();
|
|
3236
|
-
offCtxBridge();
|
|
3237
|
-
offTokenBridge();
|
|
3238
|
-
offAttemptStarted();
|
|
3239
|
-
offAttemptCompleted();
|
|
3240
|
-
offAttemptFailed();
|
|
3241
|
-
};
|
|
3242
|
-
}
|
|
3243
|
-
|
|
3244
3350
|
// src/fleet/host-session-writer.ts
|
|
3245
3351
|
import { stampAgentId } from "@wrongstack/core/storage";
|
|
3246
3352
|
function createParentSubagentSessionWriter(parentSession, agentId) {
|
|
@@ -3620,7 +3726,14 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
|
3620
3726
|
context: ctx,
|
|
3621
3727
|
permissionPolicy: new AutoApprovePermissionPolicy(subAllowedCaps),
|
|
3622
3728
|
toolExecutor,
|
|
3623
|
-
loopDetection: config.tools?.loopDetection
|
|
3729
|
+
loopDetection: config.tools?.loopDetection,
|
|
3730
|
+
// `tools.maxIterations` was never read on this host, so an operator who
|
|
3731
|
+
// set a turn budget got it honoured on the CLI and WebUI hosts and
|
|
3732
|
+
// silently ignored here. Only a POSITIVE value is forwarded: the shipped
|
|
3733
|
+
// default is `0` ("no hard limit"), and handing that to the agent loop
|
|
3734
|
+
// would trade a missing setting for a missing safety net. Absent an
|
|
3735
|
+
// explicit budget the Agent keeps its own DEFAULT_MAX_ITERATIONS.
|
|
3736
|
+
...typeof config.tools?.maxIterations === "number" && Number.isFinite(config.tools.maxIterations) && config.tools.maxIterations > 0 ? { maxIterations: config.tools.maxIterations } : {}
|
|
3624
3737
|
});
|
|
3625
3738
|
agent.extensions.register(
|
|
3626
3739
|
createFallbackModelExtension({
|
|
@@ -3856,6 +3969,7 @@ var MultiAgentHost = class {
|
|
|
3856
3969
|
this.deps = deps;
|
|
3857
3970
|
this.opts = opts;
|
|
3858
3971
|
this.roster = createProjectAgentRoster(FLEET_ROSTER2, deps.projectRoot);
|
|
3972
|
+
this.acpRunnerCache = new HostAcpRunnerCache(20, () => this.acpCommandOpts());
|
|
3859
3973
|
this.learningScheduler = new HostLearningScheduler(deps);
|
|
3860
3974
|
this.shadowManager = new HostShadowManager({
|
|
3861
3975
|
deps,
|
|
@@ -3891,7 +4005,7 @@ var MultiAgentHost = class {
|
|
|
3891
4005
|
* creating a new transport process on every ACP task dispatch. Stores the
|
|
3892
4006
|
* pending promise so concurrent calls for the same subagentId share one spawn.
|
|
3893
4007
|
* Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
|
|
3894
|
-
acpRunnerCache
|
|
4008
|
+
acpRunnerCache;
|
|
3895
4009
|
learningRoles = new HostLearningRoleTracker();
|
|
3896
4010
|
learningScheduler;
|
|
3897
4011
|
/** Adaptive concurrency controller — created in buildDirector() when config has
|
|
@@ -4027,6 +4141,10 @@ var MultiAgentHost = class {
|
|
|
4027
4141
|
brain: this.opts.brain,
|
|
4028
4142
|
roster: this.roster,
|
|
4029
4143
|
dispatchClassifier: (task, candidates) => this.learningScheduler.classifyDispatch(task, candidates),
|
|
4144
|
+
// Routing telemetry. Sibling of `dispatchClassifier` on purpose: both are
|
|
4145
|
+
// seams the host fills so core keeps no provider and no filesystem
|
|
4146
|
+
// dependency. See `.wrongstack/agents/dispatch-log.jsonl`.
|
|
4147
|
+
onSpawnRouted: (entry) => recordDispatch(entry, this.deps.projectRoot),
|
|
4030
4148
|
taskResultNotifier: (n) => this.reportTaskResultToLeader(n),
|
|
4031
4149
|
subagentIdleTimeoutMs,
|
|
4032
4150
|
...this.opts.statusTracker ? { statusTracker: this.opts.statusTracker } : {},
|
|
@@ -4159,6 +4277,28 @@ var MultiAgentHost = class {
|
|
|
4159
4277
|
async buildACPRunner(subagentId) {
|
|
4160
4278
|
return this.acpRunnerCache.get(subagentId);
|
|
4161
4279
|
}
|
|
4280
|
+
acpCommandOpts() {
|
|
4281
|
+
let overrides;
|
|
4282
|
+
try {
|
|
4283
|
+
overrides = this.deps.configStore.get()?.acp?.agents;
|
|
4284
|
+
} catch {
|
|
4285
|
+
overrides = void 0;
|
|
4286
|
+
}
|
|
4287
|
+
return {
|
|
4288
|
+
...overrides ? { overrides } : {},
|
|
4289
|
+
publishLive: (ctx, task, event) => {
|
|
4290
|
+
publishAcpLiveProgress({
|
|
4291
|
+
event,
|
|
4292
|
+
subagentId: ctx.subagentId,
|
|
4293
|
+
agentName: ctx.config.name ?? ctx.config.role ?? ctx.subagentId,
|
|
4294
|
+
sessionId: ctx.sessionId,
|
|
4295
|
+
taskId: task.id,
|
|
4296
|
+
fleet: this.getDirector()?.fleet,
|
|
4297
|
+
hostEvents: this.deps.events
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
};
|
|
4301
|
+
}
|
|
4162
4302
|
async spawnACP(subagentId, task, config) {
|
|
4163
4303
|
const taskId = randomUUID5();
|
|
4164
4304
|
await this.ensureCoordinator(config);
|
|
@@ -4253,9 +4393,12 @@ var MultiAgentHost = class {
|
|
|
4253
4393
|
taskId: this.shadowManager.getTaskId(originSessionId) ?? "shadow-active"
|
|
4254
4394
|
};
|
|
4255
4395
|
}
|
|
4396
|
+
const isAcp = opts?.provider === "acp";
|
|
4256
4397
|
const subagentConfig = {
|
|
4257
4398
|
name: opts?.name ?? "adhoc",
|
|
4258
|
-
|
|
4399
|
+
// ACP `--bg` passes the catalog id as `name`. Routing looks up the
|
|
4400
|
+
// runner by role, so that id must be the role — not 'general'.
|
|
4401
|
+
role: isShadowSpawn ? "shadow-agent" : isAcp ? opts?.name ?? "general" : "general",
|
|
4259
4402
|
provider: opts?.provider,
|
|
4260
4403
|
model: opts?.model,
|
|
4261
4404
|
fallbackModels: opts?.fallbackModels,
|
|
@@ -4782,6 +4925,7 @@ function setupBrainAndOrchestration(deps) {
|
|
|
4782
4925
|
})
|
|
4783
4926
|
);
|
|
4784
4927
|
toolRegistry.exposeToProvider("delegate");
|
|
4928
|
+
toolRegistry.exposeToProvider("define_subagent");
|
|
4785
4929
|
toolRegistry.register(
|
|
4786
4930
|
createMcpControlTool({
|
|
4787
4931
|
getConfig: () => configStore.get(),
|
|
@@ -6039,7 +6183,7 @@ async function runCliExecution(params) {
|
|
|
6039
6183
|
governanceHandle,
|
|
6040
6184
|
setConfig
|
|
6041
6185
|
} = params;
|
|
6042
|
-
const { execute } = await import("./execution-
|
|
6186
|
+
const { execute } = await import("./execution-RAZDDQFC.js");
|
|
6043
6187
|
return execute(
|
|
6044
6188
|
toExecuteDeps({
|
|
6045
6189
|
core: {
|
|
@@ -6246,6 +6390,7 @@ async function runCliExecution(params) {
|
|
|
6246
6390
|
import * as path7 from "node:path";
|
|
6247
6391
|
import { TOKENS as TOKENS8 } from "@wrongstack/core/kernel";
|
|
6248
6392
|
import { ToolRegistry as ToolRegistry2 } from "@wrongstack/core/registry";
|
|
6393
|
+
import { resolveTokenSavingTier } from "@wrongstack/core/types";
|
|
6249
6394
|
|
|
6250
6395
|
// src/boot/system-prompt-builder.ts
|
|
6251
6396
|
import { DefaultSystemPromptBuilder } from "@wrongstack/core/agent";
|
|
@@ -7143,7 +7288,7 @@ ${commit.out}`;
|
|
|
7143
7288
|
|
|
7144
7289
|
// src/boot/tool-registry.ts
|
|
7145
7290
|
function registerBuiltinTools(deps) {
|
|
7146
|
-
const tier = normalizeTokenSavingTier4(deps.config.features.tokenSavingMode);
|
|
7291
|
+
const tier = deps.tier ?? normalizeTokenSavingTier4(deps.config.features.tokenSavingMode);
|
|
7147
7292
|
registerCanonicalHostTools({
|
|
7148
7293
|
registry: deps.toolRegistry,
|
|
7149
7294
|
tier,
|
|
@@ -7241,6 +7386,10 @@ async function setupCliPromptAndTools(params) {
|
|
|
7241
7386
|
events,
|
|
7242
7387
|
vectorMemoryStore
|
|
7243
7388
|
} = params;
|
|
7389
|
+
const tier = resolveTokenSavingTier(
|
|
7390
|
+
config.features.tokenSavingMode,
|
|
7391
|
+
modelCapabilitiesRef.current?.maxContextTokens
|
|
7392
|
+
);
|
|
7244
7393
|
bindSystemPromptBuilder({
|
|
7245
7394
|
container,
|
|
7246
7395
|
modeStore,
|
|
@@ -7255,7 +7404,7 @@ async function setupCliPromptAndTools(params) {
|
|
|
7255
7404
|
skillsEnabled: config.features.skills,
|
|
7256
7405
|
skillMode: config.skills?.mode,
|
|
7257
7406
|
skillEagerMaxChars: config.skills?.eagerMaxChars,
|
|
7258
|
-
tokenSavingMode:
|
|
7407
|
+
tokenSavingMode: tier,
|
|
7259
7408
|
systemPromptVariant: config.systemPrompt?.variant,
|
|
7260
7409
|
paths: {
|
|
7261
7410
|
projectGoal: wpaths.projectGoal,
|
|
@@ -7274,6 +7423,7 @@ async function setupCliPromptAndTools(params) {
|
|
|
7274
7423
|
toolRegistry,
|
|
7275
7424
|
compactor: container.resolve(TOKENS8.Compactor),
|
|
7276
7425
|
config,
|
|
7426
|
+
tier,
|
|
7277
7427
|
memoryStore,
|
|
7278
7428
|
vectorMemoryStore,
|
|
7279
7429
|
events,
|
|
@@ -7333,6 +7483,134 @@ function createCommandHostAdapters(input) {
|
|
|
7333
7483
|
};
|
|
7334
7484
|
}
|
|
7335
7485
|
|
|
7486
|
+
// src/wiring/concept-summarizer.ts
|
|
7487
|
+
import { OneShotOrchestrator } from "@wrongstack/core/execution";
|
|
7488
|
+
import { CONCEPT_RELATIONS, MAX_CRUX_LINES } from "@wrongstack/tools";
|
|
7489
|
+
var FILE_MAX_TOKENS = 400;
|
|
7490
|
+
var SUBSYSTEM_MAX_TOKENS = 500;
|
|
7491
|
+
var CALL_TIMEOUT_MS = 45e3;
|
|
7492
|
+
var FILE_SYSTEM_PROMPT = [
|
|
7493
|
+
"You describe one source file for a code search index.",
|
|
7494
|
+
"",
|
|
7495
|
+
'Answer with JSON only: {"summary": string, "cruxStart": number, "cruxEnd": number}.',
|
|
7496
|
+
"",
|
|
7497
|
+
"- `summary`: one or two sentences on what this file is FOR \u2014 the job it does in the",
|
|
7498
|
+
" system, in plain English. Do not list its exports; the index already has those.",
|
|
7499
|
+
" Prefer the domain vocabulary the code itself uses. Never speculate about code you",
|
|
7500
|
+
" were not shown.",
|
|
7501
|
+
`- \`cruxStart\`/\`cruxEnd\`: the 1-based, inclusive line span of the few lines that`,
|
|
7502
|
+
` actually carry the file's meaning \u2014 the core decision, the central type, the loop`,
|
|
7503
|
+
` everything else serves. At most ${MAX_CRUX_LINES} lines. Omit both if nothing stands out.`
|
|
7504
|
+
].join("\n");
|
|
7505
|
+
var SUBSYSTEM_SYSTEM_PROMPT = [
|
|
7506
|
+
"You describe one subsystem of a codebase, given summaries of its most central files.",
|
|
7507
|
+
"",
|
|
7508
|
+
"Answer with JSON only:",
|
|
7509
|
+
'{"summary": string, "relations": [{"to": string, "relation": string}]}',
|
|
7510
|
+
"",
|
|
7511
|
+
"- `summary`: two or three sentences on what this subsystem is responsible for.",
|
|
7512
|
+
`- \`relations\`: how it relates to OTHER named subsystems, using only these verbs:`,
|
|
7513
|
+
` ${CONCEPT_RELATIONS.join(", ")}. Omit the field entirely if you are not confident.`,
|
|
7514
|
+
" Never invent a subsystem name that was not given to you."
|
|
7515
|
+
].join("\n");
|
|
7516
|
+
function parseJsonObject(text) {
|
|
7517
|
+
const trimmed = text.trim();
|
|
7518
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(trimmed);
|
|
7519
|
+
const candidate = fenced?.[1]?.trim() ?? trimmed;
|
|
7520
|
+
const start = candidate.indexOf("{");
|
|
7521
|
+
const end = candidate.lastIndexOf("}");
|
|
7522
|
+
if (start === -1 || end <= start) return null;
|
|
7523
|
+
try {
|
|
7524
|
+
const parsed = JSON.parse(candidate.slice(start, end + 1));
|
|
7525
|
+
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
7526
|
+
} catch {
|
|
7527
|
+
return null;
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
function asString(value) {
|
|
7531
|
+
return typeof value === "string" ? value.trim() : "";
|
|
7532
|
+
}
|
|
7533
|
+
function asLine(value) {
|
|
7534
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
7535
|
+
}
|
|
7536
|
+
function numberLines(source) {
|
|
7537
|
+
return source.split("\n").map((line, index) => `${index + 1} ${line}`).join("\n");
|
|
7538
|
+
}
|
|
7539
|
+
function createConceptSummarizer(deps) {
|
|
7540
|
+
const orchestrator = new OneShotOrchestrator({
|
|
7541
|
+
buildProvider: deps.buildProvider,
|
|
7542
|
+
getConfig: deps.getConfig,
|
|
7543
|
+
fallbackProfileManager: deps.fallbackProfileManager,
|
|
7544
|
+
...deps.statusTracker ? { statusTracker: deps.statusTracker } : {},
|
|
7545
|
+
...deps.wrapProviderCall ? { wrapProviderCall: deps.wrapProviderCall } : {}
|
|
7546
|
+
});
|
|
7547
|
+
const model = deps.model ?? deps.getConfig().indexing?.concepts?.model;
|
|
7548
|
+
return {
|
|
7549
|
+
async describeFile(input) {
|
|
7550
|
+
const userPrompt = [
|
|
7551
|
+
`File: ${input.file}`,
|
|
7552
|
+
input.declarations.length > 0 ? `Indexed declarations: ${input.declarations.map((d) => `${d.name} (${d.kind}, L${d.line})`).join(", ")}` : "",
|
|
7553
|
+
input.staleSummary !== void 0 ? `A previous description of an older version of this file \u2014 verify it against the source rather than trusting it: ${input.staleSummary}` : "",
|
|
7554
|
+
input.truncated ? "(source truncated)" : "",
|
|
7555
|
+
"",
|
|
7556
|
+
numberLines(input.source)
|
|
7557
|
+
].filter(Boolean).join("\n");
|
|
7558
|
+
const result = await orchestrator.call({
|
|
7559
|
+
system: FILE_SYSTEM_PROMPT,
|
|
7560
|
+
userPrompt,
|
|
7561
|
+
responseFormat: { type: "json_object" },
|
|
7562
|
+
maxTokens: FILE_MAX_TOKENS,
|
|
7563
|
+
timeoutMs: CALL_TIMEOUT_MS,
|
|
7564
|
+
...model ? { model } : { role: "summarizer" },
|
|
7565
|
+
...input.signal ? { signal: input.signal } : {}
|
|
7566
|
+
});
|
|
7567
|
+
if (result.error || !result.text) return null;
|
|
7568
|
+
const parsed = parseJsonObject(result.text);
|
|
7569
|
+
if (parsed === null) return null;
|
|
7570
|
+
const summary = asString(parsed["summary"]);
|
|
7571
|
+
if (summary === "") return null;
|
|
7572
|
+
return {
|
|
7573
|
+
summary,
|
|
7574
|
+
cruxStart: asLine(parsed["cruxStart"]),
|
|
7575
|
+
cruxEnd: asLine(parsed["cruxEnd"]),
|
|
7576
|
+
model: result.model
|
|
7577
|
+
};
|
|
7578
|
+
},
|
|
7579
|
+
async describeSubsystem(input) {
|
|
7580
|
+
if (input.files.length === 0) return null;
|
|
7581
|
+
const userPrompt = [
|
|
7582
|
+
`Subsystem: ${input.name}`,
|
|
7583
|
+
"",
|
|
7584
|
+
"Its most central files, with what each is for:",
|
|
7585
|
+
...input.files.map((f) => `- ${f.file}: ${f.summary}`)
|
|
7586
|
+
].join("\n");
|
|
7587
|
+
const result = await orchestrator.call({
|
|
7588
|
+
system: SUBSYSTEM_SYSTEM_PROMPT,
|
|
7589
|
+
userPrompt,
|
|
7590
|
+
responseFormat: { type: "json_object" },
|
|
7591
|
+
maxTokens: SUBSYSTEM_MAX_TOKENS,
|
|
7592
|
+
timeoutMs: CALL_TIMEOUT_MS,
|
|
7593
|
+
...model ? { model } : { role: "summarizer" },
|
|
7594
|
+
...input.signal ? { signal: input.signal } : {}
|
|
7595
|
+
});
|
|
7596
|
+
if (result.error || !result.text) return null;
|
|
7597
|
+
const parsed = parseJsonObject(result.text);
|
|
7598
|
+
if (parsed === null) return null;
|
|
7599
|
+
const summary = asString(parsed["summary"]);
|
|
7600
|
+
if (summary === "") return null;
|
|
7601
|
+
const rawRelations = parsed["relations"];
|
|
7602
|
+
const relations = Array.isArray(rawRelations) ? rawRelations.flatMap((entry) => {
|
|
7603
|
+
if (entry === null || typeof entry !== "object") return [];
|
|
7604
|
+
const record = entry;
|
|
7605
|
+
const to = asString(record["to"]);
|
|
7606
|
+
const relation = asString(record["relation"]);
|
|
7607
|
+
return to !== "" && relation !== "" ? [{ to, relation }] : [];
|
|
7608
|
+
}) : [];
|
|
7609
|
+
return { summary, relations, model: result.model };
|
|
7610
|
+
}
|
|
7611
|
+
};
|
|
7612
|
+
}
|
|
7613
|
+
|
|
7336
7614
|
// src/wiring/eternal-command-handlers.ts
|
|
7337
7615
|
import { EternalAutonomyEngine as EternalAutonomyEngine2, ParallelEternalEngine } from "@wrongstack/core/execution";
|
|
7338
7616
|
function createEternalCommandHandlers(input) {
|
|
@@ -8382,6 +8660,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
8382
8660
|
toolCount: server.toolCount
|
|
8383
8661
|
})),
|
|
8384
8662
|
onYolo: input.onYolo,
|
|
8663
|
+
onYoloConfirm: input.onYoloConfirm,
|
|
8385
8664
|
onNextPredict: (enabled) => {
|
|
8386
8665
|
if (enabled !== void 0) {
|
|
8387
8666
|
input.setNextPredict(enabled);
|
|
@@ -8499,134 +8778,6 @@ function formatDiagnostics(input) {
|
|
|
8499
8778
|
].filter((line) => line !== null).join("\n");
|
|
8500
8779
|
}
|
|
8501
8780
|
|
|
8502
|
-
// src/wiring/concept-summarizer.ts
|
|
8503
|
-
import { OneShotOrchestrator } from "@wrongstack/core/execution";
|
|
8504
|
-
import { CONCEPT_RELATIONS, MAX_CRUX_LINES } from "@wrongstack/tools";
|
|
8505
|
-
var FILE_MAX_TOKENS = 400;
|
|
8506
|
-
var SUBSYSTEM_MAX_TOKENS = 500;
|
|
8507
|
-
var CALL_TIMEOUT_MS = 45e3;
|
|
8508
|
-
var FILE_SYSTEM_PROMPT = [
|
|
8509
|
-
"You describe one source file for a code search index.",
|
|
8510
|
-
"",
|
|
8511
|
-
'Answer with JSON only: {"summary": string, "cruxStart": number, "cruxEnd": number}.',
|
|
8512
|
-
"",
|
|
8513
|
-
"- `summary`: one or two sentences on what this file is FOR \u2014 the job it does in the",
|
|
8514
|
-
" system, in plain English. Do not list its exports; the index already has those.",
|
|
8515
|
-
" Prefer the domain vocabulary the code itself uses. Never speculate about code you",
|
|
8516
|
-
" were not shown.",
|
|
8517
|
-
`- \`cruxStart\`/\`cruxEnd\`: the 1-based, inclusive line span of the few lines that`,
|
|
8518
|
-
` actually carry the file's meaning \u2014 the core decision, the central type, the loop`,
|
|
8519
|
-
` everything else serves. At most ${MAX_CRUX_LINES} lines. Omit both if nothing stands out.`
|
|
8520
|
-
].join("\n");
|
|
8521
|
-
var SUBSYSTEM_SYSTEM_PROMPT = [
|
|
8522
|
-
"You describe one subsystem of a codebase, given summaries of its most central files.",
|
|
8523
|
-
"",
|
|
8524
|
-
"Answer with JSON only:",
|
|
8525
|
-
'{"summary": string, "relations": [{"to": string, "relation": string}]}',
|
|
8526
|
-
"",
|
|
8527
|
-
"- `summary`: two or three sentences on what this subsystem is responsible for.",
|
|
8528
|
-
`- \`relations\`: how it relates to OTHER named subsystems, using only these verbs:`,
|
|
8529
|
-
` ${CONCEPT_RELATIONS.join(", ")}. Omit the field entirely if you are not confident.`,
|
|
8530
|
-
" Never invent a subsystem name that was not given to you."
|
|
8531
|
-
].join("\n");
|
|
8532
|
-
function parseJsonObject(text) {
|
|
8533
|
-
const trimmed = text.trim();
|
|
8534
|
-
const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(trimmed);
|
|
8535
|
-
const candidate = fenced?.[1]?.trim() ?? trimmed;
|
|
8536
|
-
const start = candidate.indexOf("{");
|
|
8537
|
-
const end = candidate.lastIndexOf("}");
|
|
8538
|
-
if (start === -1 || end <= start) return null;
|
|
8539
|
-
try {
|
|
8540
|
-
const parsed = JSON.parse(candidate.slice(start, end + 1));
|
|
8541
|
-
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
8542
|
-
} catch {
|
|
8543
|
-
return null;
|
|
8544
|
-
}
|
|
8545
|
-
}
|
|
8546
|
-
function asString(value) {
|
|
8547
|
-
return typeof value === "string" ? value.trim() : "";
|
|
8548
|
-
}
|
|
8549
|
-
function asLine(value) {
|
|
8550
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
8551
|
-
}
|
|
8552
|
-
function numberLines(source) {
|
|
8553
|
-
return source.split("\n").map((line, index) => `${index + 1} ${line}`).join("\n");
|
|
8554
|
-
}
|
|
8555
|
-
function createConceptSummarizer(deps) {
|
|
8556
|
-
const orchestrator = new OneShotOrchestrator({
|
|
8557
|
-
buildProvider: deps.buildProvider,
|
|
8558
|
-
getConfig: deps.getConfig,
|
|
8559
|
-
fallbackProfileManager: deps.fallbackProfileManager,
|
|
8560
|
-
...deps.statusTracker ? { statusTracker: deps.statusTracker } : {},
|
|
8561
|
-
...deps.wrapProviderCall ? { wrapProviderCall: deps.wrapProviderCall } : {}
|
|
8562
|
-
});
|
|
8563
|
-
const model = deps.model ?? deps.getConfig().indexing?.concepts?.model;
|
|
8564
|
-
return {
|
|
8565
|
-
async describeFile(input) {
|
|
8566
|
-
const userPrompt = [
|
|
8567
|
-
`File: ${input.file}`,
|
|
8568
|
-
input.declarations.length > 0 ? `Indexed declarations: ${input.declarations.map((d) => `${d.name} (${d.kind}, L${d.line})`).join(", ")}` : "",
|
|
8569
|
-
input.staleSummary !== void 0 ? `A previous description of an older version of this file \u2014 verify it against the source rather than trusting it: ${input.staleSummary}` : "",
|
|
8570
|
-
input.truncated ? "(source truncated)" : "",
|
|
8571
|
-
"",
|
|
8572
|
-
numberLines(input.source)
|
|
8573
|
-
].filter(Boolean).join("\n");
|
|
8574
|
-
const result = await orchestrator.call({
|
|
8575
|
-
system: FILE_SYSTEM_PROMPT,
|
|
8576
|
-
userPrompt,
|
|
8577
|
-
responseFormat: { type: "json_object" },
|
|
8578
|
-
maxTokens: FILE_MAX_TOKENS,
|
|
8579
|
-
timeoutMs: CALL_TIMEOUT_MS,
|
|
8580
|
-
...model ? { model } : { role: "summarizer" },
|
|
8581
|
-
...input.signal ? { signal: input.signal } : {}
|
|
8582
|
-
});
|
|
8583
|
-
if (result.error || !result.text) return null;
|
|
8584
|
-
const parsed = parseJsonObject(result.text);
|
|
8585
|
-
if (parsed === null) return null;
|
|
8586
|
-
const summary = asString(parsed["summary"]);
|
|
8587
|
-
if (summary === "") return null;
|
|
8588
|
-
return {
|
|
8589
|
-
summary,
|
|
8590
|
-
cruxStart: asLine(parsed["cruxStart"]),
|
|
8591
|
-
cruxEnd: asLine(parsed["cruxEnd"]),
|
|
8592
|
-
model: result.model
|
|
8593
|
-
};
|
|
8594
|
-
},
|
|
8595
|
-
async describeSubsystem(input) {
|
|
8596
|
-
if (input.files.length === 0) return null;
|
|
8597
|
-
const userPrompt = [
|
|
8598
|
-
`Subsystem: ${input.name}`,
|
|
8599
|
-
"",
|
|
8600
|
-
"Its most central files, with what each is for:",
|
|
8601
|
-
...input.files.map((f) => `- ${f.file}: ${f.summary}`)
|
|
8602
|
-
].join("\n");
|
|
8603
|
-
const result = await orchestrator.call({
|
|
8604
|
-
system: SUBSYSTEM_SYSTEM_PROMPT,
|
|
8605
|
-
userPrompt,
|
|
8606
|
-
responseFormat: { type: "json_object" },
|
|
8607
|
-
maxTokens: SUBSYSTEM_MAX_TOKENS,
|
|
8608
|
-
timeoutMs: CALL_TIMEOUT_MS,
|
|
8609
|
-
...model ? { model } : { role: "summarizer" },
|
|
8610
|
-
...input.signal ? { signal: input.signal } : {}
|
|
8611
|
-
});
|
|
8612
|
-
if (result.error || !result.text) return null;
|
|
8613
|
-
const parsed = parseJsonObject(result.text);
|
|
8614
|
-
if (parsed === null) return null;
|
|
8615
|
-
const summary = asString(parsed["summary"]);
|
|
8616
|
-
if (summary === "") return null;
|
|
8617
|
-
const rawRelations = parsed["relations"];
|
|
8618
|
-
const relations = Array.isArray(rawRelations) ? rawRelations.flatMap((entry) => {
|
|
8619
|
-
if (entry === null || typeof entry !== "object") return [];
|
|
8620
|
-
const record = entry;
|
|
8621
|
-
const to = asString(record["to"]);
|
|
8622
|
-
const relation = asString(record["relation"]);
|
|
8623
|
-
return to !== "" && relation !== "" ? [{ to, relation }] : [];
|
|
8624
|
-
}) : [];
|
|
8625
|
-
return { summary, relations, model: result.model };
|
|
8626
|
-
}
|
|
8627
|
-
};
|
|
8628
|
-
}
|
|
8629
|
-
|
|
8630
8781
|
// src/services/statusline-config.ts
|
|
8631
8782
|
import * as fs3 from "node:fs/promises";
|
|
8632
8783
|
import * as os from "node:os";
|
|
@@ -8867,7 +9018,7 @@ import {
|
|
|
8867
9018
|
probeAcpAgents,
|
|
8868
9019
|
renderAcpBenchText,
|
|
8869
9020
|
renderEnsembleText,
|
|
8870
|
-
resolveAcpAgentCommand,
|
|
9021
|
+
resolveAcpAgentCommand as resolveAcpAgentCommand2,
|
|
8871
9022
|
runAcpBench,
|
|
8872
9023
|
runEnsemble,
|
|
8873
9024
|
runOneAcpTask
|
|
@@ -8878,7 +9029,7 @@ function buildAcpCommand(opts) {
|
|
|
8878
9029
|
name: "acp",
|
|
8879
9030
|
category: "Agent",
|
|
8880
9031
|
description: "Discover and assign tasks to installed ACP coding agents (claude-code, codex-cli, gemini-cli, \u2026).",
|
|
8881
|
-
argsHint: "[list | probe | <agent-id> [--bg] <task> | parallel <csv> <task>]",
|
|
9032
|
+
argsHint: "[list | probe | <agent-id> [--bg] <task> | parallel <csv> [--bg] <task>]",
|
|
8882
9033
|
help: [
|
|
8883
9034
|
"Use the ACP-supporting CLIs already installed on this machine as",
|
|
8884
9035
|
"subagents \u2014 they run with their own login, so no API key is spent here.",
|
|
@@ -8888,6 +9039,7 @@ function buildAcpCommand(opts) {
|
|
|
8888
9039
|
" /acp <agent-id> <task> Run a task on ONE agent, inline + streamed",
|
|
8889
9040
|
" /acp <agent-id> --bg <task> Run it as a background fleet subagent (/agents)",
|
|
8890
9041
|
" /acp parallel <csv> <task> Fan one task out to several agents at once",
|
|
9042
|
+
" /acp parallel <csv> --bg <task> Same, as background fleet subagents",
|
|
8891
9043
|
" /acp probe [csv] Handshake-test agents \u2014 shows what truly works",
|
|
8892
9044
|
" /acp bench [csv] [--fs] End-to-end verify each agent + graded report",
|
|
8893
9045
|
" /acp sync Pull the official agentclientprotocol/registry",
|
|
@@ -8899,6 +9051,7 @@ function buildAcpCommand(opts) {
|
|
|
8899
9051
|
' /acp gemini-cli "explain src/agent.ts"',
|
|
8900
9052
|
' /acp claude-code --bg "refactor auth/session.ts and run the tests"',
|
|
8901
9053
|
' /acp parallel claude-code,gemini-cli,codex-cli "review this diff"',
|
|
9054
|
+
' /acp parallel claude-code,gemini-cli --bg "review this diff"',
|
|
8902
9055
|
" /acp probe",
|
|
8903
9056
|
"",
|
|
8904
9057
|
"If an agent is detected but `probe` shows it failing, its catalog entry",
|
|
@@ -8918,7 +9071,7 @@ function buildAcpCommand(opts) {
|
|
|
8918
9071
|
const rest = spaceIdx === -1 ? "" : trimmed.slice(spaceIdx + 1).trim();
|
|
8919
9072
|
if (head === "probe") return probeAgents(rest, overrides, live, opts);
|
|
8920
9073
|
if (head === "bench") return benchAgents(rest, overrides, live, opts);
|
|
8921
|
-
if (head === "parallel") return parallelAgents(rest, overrides, live);
|
|
9074
|
+
if (head === "parallel") return parallelAgents(rest, overrides, live, opts);
|
|
8922
9075
|
return runSingle(head, rest, overrides, live, opts);
|
|
8923
9076
|
}
|
|
8924
9077
|
};
|
|
@@ -8950,42 +9103,7 @@ The bundled offline catalog is still available via \`/acp\`.`
|
|
|
8950
9103
|
}
|
|
8951
9104
|
async function listAgents(live) {
|
|
8952
9105
|
const detected = await new EnsembleRegistry().list();
|
|
8953
|
-
|
|
8954
|
-
const missing = detected.filter((a) => !a.installed);
|
|
8955
|
-
const lines = ["Detected ACP agents (local installs):", ""];
|
|
8956
|
-
for (const a of installed) {
|
|
8957
|
-
const ver = a.version ? ` (${a.version.split("\n")[0]})` : "";
|
|
8958
|
-
lines.push(` \u2713 ${a.id.padEnd(16)} ${a.displayName}${ver}`);
|
|
8959
|
-
}
|
|
8960
|
-
for (const a of missing) {
|
|
8961
|
-
lines.push(` \u2717 ${a.id.padEnd(16)} ${a.displayName} (${a.reason ?? "not installed"})`);
|
|
8962
|
-
}
|
|
8963
|
-
lines.push("");
|
|
8964
|
-
lines.push(`${installed.length} of ${detected.length} bundled agents installed locally.`);
|
|
8965
|
-
if (live && live.agents.length > 0) {
|
|
8966
|
-
const localIds = new Set(detected.map((a) => a.id));
|
|
8967
|
-
const aliases = {
|
|
8968
|
-
"claude-acp": "claude-code",
|
|
8969
|
-
gemini: "gemini-cli",
|
|
8970
|
-
"codex-acp": "codex-cli",
|
|
8971
|
-
"github-copilot-cli": "copilot"
|
|
8972
|
-
};
|
|
8973
|
-
const extra = live.agents.filter(
|
|
8974
|
-
(a) => !localIds.has(a.id) && !localIds.has(aliases[a.id] ?? "")
|
|
8975
|
-
);
|
|
8976
|
-
lines.push("");
|
|
8977
|
-
lines.push(
|
|
8978
|
-
`Synced registry: ${live.agents.length} agents available (use \`/acp <id> <task>\`).`
|
|
8979
|
-
);
|
|
8980
|
-
if (extra.length > 0) {
|
|
8981
|
-
lines.push(` more ids: ${extra.map((a) => a.id).join(", ")}`);
|
|
8982
|
-
}
|
|
8983
|
-
} else {
|
|
8984
|
-
lines.push("");
|
|
8985
|
-
lines.push("Run `/acp sync` to pull the full official registry (37+ agents).");
|
|
8986
|
-
}
|
|
8987
|
-
lines.push("Run `/acp <agent-id> <task>` to assign a task, or `/acp probe` to test handshakes.");
|
|
8988
|
-
return { message: lines.join("\n") };
|
|
9106
|
+
return { message: formatAcpAgentList({ live, detected }) };
|
|
8989
9107
|
}
|
|
8990
9108
|
async function probeAgents(csv, overrides, live, opts) {
|
|
8991
9109
|
let ids;
|
|
@@ -9007,7 +9125,7 @@ async function probeAgents(csv, overrides, live, opts) {
|
|
|
9007
9125
|
const liveById = live?.byId;
|
|
9008
9126
|
const results = await probeAcpAgents({
|
|
9009
9127
|
agentIds: ids,
|
|
9010
|
-
resolveCmd: (id) =>
|
|
9128
|
+
resolveCmd: (id) => resolveAcpAgentCommand2(id, overrides, liveById),
|
|
9011
9129
|
projectRoot: opts.projectRoot,
|
|
9012
9130
|
onProgress: (id, r) => opts.renderer.writeInfo(` ${r.ok ? "\u2713" : "\u2717"} ${id} (${r.ms}ms)
|
|
9013
9131
|
`)
|
|
@@ -9053,7 +9171,7 @@ async function benchAgents(rest, overrides, live, opts) {
|
|
|
9053
9171
|
const liveById = live?.byId;
|
|
9054
9172
|
const result = await runAcpBench({
|
|
9055
9173
|
agentIds: ids,
|
|
9056
|
-
resolveCmd: (id) =>
|
|
9174
|
+
resolveCmd: (id) => resolveAcpAgentCommand2(id, overrides, liveById),
|
|
9057
9175
|
projectRoot: opts.projectRoot,
|
|
9058
9176
|
checkFs,
|
|
9059
9177
|
onProgress: (agentId, phase, r) => {
|
|
@@ -9065,22 +9183,62 @@ async function benchAgents(rest, overrides, live, opts) {
|
|
|
9065
9183
|
});
|
|
9066
9184
|
return { message: renderAcpBenchText(result) };
|
|
9067
9185
|
}
|
|
9068
|
-
async function parallelAgents(rest, overrides, live) {
|
|
9069
|
-
const
|
|
9186
|
+
async function parallelAgents(rest, overrides, live, opts) {
|
|
9187
|
+
const tokens = rest.split(/\s+/).filter(Boolean);
|
|
9188
|
+
const bg = tokens.includes("--bg");
|
|
9189
|
+
const withoutFlag = tokens.filter((t) => t !== "--bg").join(" ");
|
|
9190
|
+
const spaceIdx = withoutFlag.search(/\s/);
|
|
9070
9191
|
if (spaceIdx === -1) {
|
|
9071
9192
|
return {
|
|
9072
|
-
message: 'Usage: /acp parallel <agent-ids-csv> <task>\nExample: /acp parallel claude-code,gemini-cli "review this diff"'
|
|
9193
|
+
message: 'Usage: /acp parallel <agent-ids-csv> [--bg] <task>\nExample: /acp parallel claude-code,gemini-cli --bg "review this diff"'
|
|
9073
9194
|
};
|
|
9074
9195
|
}
|
|
9075
|
-
const
|
|
9076
|
-
const
|
|
9077
|
-
|
|
9196
|
+
const idsCsv = withoutFlag.slice(0, spaceIdx).trim();
|
|
9197
|
+
const agentIds = dedup(
|
|
9198
|
+
idsCsv.split(",").map((s) => s.trim()).filter(Boolean)
|
|
9199
|
+
);
|
|
9200
|
+
const task = stripQuotes(withoutFlag.slice(spaceIdx + 1).trim());
|
|
9201
|
+
if (agentIds.length === 0 || !task) {
|
|
9202
|
+
return { message: "Task description is required." };
|
|
9203
|
+
}
|
|
9078
9204
|
const liveById = live?.byId;
|
|
9205
|
+
if (bg) {
|
|
9206
|
+
if (!opts.onSpawn) {
|
|
9207
|
+
return {
|
|
9208
|
+
message: "Background mode needs the fleet (director). Director Mode is always active."
|
|
9209
|
+
};
|
|
9210
|
+
}
|
|
9211
|
+
const dispatched = [];
|
|
9212
|
+
const skipped = [];
|
|
9213
|
+
for (const id of agentIds) {
|
|
9214
|
+
if (!resolveAcpAgentCommand2(id, overrides, liveById)) {
|
|
9215
|
+
skipped.push({ id, reason: "unknown agent" });
|
|
9216
|
+
continue;
|
|
9217
|
+
}
|
|
9218
|
+
try {
|
|
9219
|
+
const summary = await opts.onSpawn(task, { provider: "acp", name: id });
|
|
9220
|
+
dispatched.push(` \u2022 ${id}: ${summary}`);
|
|
9221
|
+
} catch (err) {
|
|
9222
|
+
skipped.push({ id, reason: toErrorMessage4(err) });
|
|
9223
|
+
}
|
|
9224
|
+
}
|
|
9225
|
+
const lines = [
|
|
9226
|
+
dispatched.length === 0 ? "No ACP agents dispatched." : `Dispatched ${dispatched.length} ACP agent${dispatched.length === 1 ? "" : "s"} as background subagents.`,
|
|
9227
|
+
...dispatched
|
|
9228
|
+
];
|
|
9229
|
+
if (skipped.length > 0) {
|
|
9230
|
+
lines.push(
|
|
9231
|
+
`Skipped ${skipped.length}: ${skipped.map((s) => `${s.id} (${s.reason})`).join(", ")}`
|
|
9232
|
+
);
|
|
9233
|
+
}
|
|
9234
|
+
lines.push("Live progress is visible in /agents, the TUI fleet panel and the WebUI Agents panel.");
|
|
9235
|
+
return { message: lines.join("\n") };
|
|
9236
|
+
}
|
|
9079
9237
|
try {
|
|
9080
9238
|
const result = await runEnsemble({
|
|
9081
|
-
agentIds,
|
|
9239
|
+
agentIds: idsCsv,
|
|
9082
9240
|
task,
|
|
9083
|
-
resolveCmd: (id) =>
|
|
9241
|
+
resolveCmd: (id) => resolveAcpAgentCommand2(id, overrides, liveById)
|
|
9084
9242
|
});
|
|
9085
9243
|
return { message: renderEnsembleText(result) };
|
|
9086
9244
|
} catch (err) {
|
|
@@ -9099,7 +9257,7 @@ async function runSingle(agentId, rest, overrides, live, opts) {
|
|
|
9099
9257
|
Task description is required.`
|
|
9100
9258
|
};
|
|
9101
9259
|
}
|
|
9102
|
-
const cmd =
|
|
9260
|
+
const cmd = resolveAcpAgentCommand2(agentId, overrides, live?.byId);
|
|
9103
9261
|
if (!cmd) {
|
|
9104
9262
|
return {
|
|
9105
9263
|
message: `Unknown ACP agent: ${agentId}
|
|
@@ -9289,6 +9447,16 @@ ${lines.join("\n")}`;
|
|
|
9289
9447
|
lines.push(
|
|
9290
9448
|
` directives: ${stats.entryCount} buffered \xB7 ${hitRate} \xB7 ${stats.deadEntryCount} never used`
|
|
9291
9449
|
);
|
|
9450
|
+
if (stats.directiveLift === null) {
|
|
9451
|
+
lines.push(
|
|
9452
|
+
` measured lift: none yet ${color10.dim("(needs a directive with trials both with and without it)")}`
|
|
9453
|
+
);
|
|
9454
|
+
} else {
|
|
9455
|
+
const points = (stats.directiveLift * 100).toFixed(1);
|
|
9456
|
+
lines.push(
|
|
9457
|
+
` measured lift: ${stats.directiveLift >= 0 ? "+" : ""}${points} pts over baseline, across ${stats.measuredEntryCount} directive(s)`
|
|
9458
|
+
);
|
|
9459
|
+
}
|
|
9292
9460
|
}
|
|
9293
9461
|
if (cfg) lines.push(` config: ${JSON.stringify(cfg)}`);
|
|
9294
9462
|
if (kn)
|
|
@@ -13328,7 +13496,7 @@ function buildDoctorCommand(opts) {
|
|
|
13328
13496
|
].join("\n");
|
|
13329
13497
|
async function loadPluginSchemas() {
|
|
13330
13498
|
try {
|
|
13331
|
-
const { BUILTIN_PLUGIN_FACTORIES } = await import("./plugins-
|
|
13499
|
+
const { BUILTIN_PLUGIN_FACTORIES } = await import("./plugins-WJLW6ULV.js");
|
|
13332
13500
|
const settled = await Promise.allSettled(BUILTIN_PLUGIN_FACTORIES.map((f) => f()));
|
|
13333
13501
|
const loaded = [];
|
|
13334
13502
|
for (const s of settled) {
|
|
@@ -13803,9 +13971,13 @@ function buildEnhanceCommand(opts) {
|
|
|
13803
13971
|
}
|
|
13804
13972
|
|
|
13805
13973
|
// src/slash-commands/ensemble.ts
|
|
13806
|
-
import {
|
|
13974
|
+
import {
|
|
13975
|
+
renderEnsembleText as renderEnsembleText2,
|
|
13976
|
+
resolveAcpAgentCommand as resolveAcpAgentCommand3,
|
|
13977
|
+
runEnsemble as runEnsemble2
|
|
13978
|
+
} from "@wrongstack/acp";
|
|
13807
13979
|
import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
|
|
13808
|
-
function buildEnsembleCommand(
|
|
13980
|
+
function buildEnsembleCommand(opts) {
|
|
13809
13981
|
return {
|
|
13810
13982
|
name: "ensemble",
|
|
13811
13983
|
category: "Agent",
|
|
@@ -13830,6 +14002,11 @@ function buildEnsembleCommand(_opts) {
|
|
|
13830
14002
|
" --timeout <sec> Per-agent hard timeout (default 300s).",
|
|
13831
14003
|
" --concurrency <n> Max agents running at once (default 4).",
|
|
13832
14004
|
"",
|
|
14005
|
+
"This command is blocking: you wait for every agent, then get a text dump.",
|
|
14006
|
+
"To watch Claude Code / Gemini / Kimi live in the TUI fleet panel or WebUI",
|
|
14007
|
+
"Agents panel while they run in the background, use:",
|
|
14008
|
+
' /acp parallel claude-code,gemini-cli,kimi --bg "the task"',
|
|
14009
|
+
"",
|
|
13833
14010
|
"Use /acp list (or wstack acp list) to see which agents are detected."
|
|
13834
14011
|
].join("\n"),
|
|
13835
14012
|
async run(args) {
|
|
@@ -13862,9 +14039,12 @@ function buildEnsembleCommand(_opts) {
|
|
|
13862
14039
|
return { message: "Task description is required." };
|
|
13863
14040
|
}
|
|
13864
14041
|
try {
|
|
14042
|
+
const overrides = readEnsembleOverrides(opts);
|
|
14043
|
+
const live = opts.paths ? await loadCachedAcpRegistry(opts.paths) : null;
|
|
13865
14044
|
const result = await runEnsemble2({
|
|
13866
14045
|
agentIds,
|
|
13867
14046
|
task,
|
|
14047
|
+
resolveCmd: (id) => resolveAcpAgentCommand3(id, overrides, live?.byId),
|
|
13868
14048
|
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
13869
14049
|
...maxConcurrency !== void 0 ? { maxConcurrency } : {}
|
|
13870
14050
|
});
|
|
@@ -13875,6 +14055,13 @@ function buildEnsembleCommand(_opts) {
|
|
|
13875
14055
|
}
|
|
13876
14056
|
};
|
|
13877
14057
|
}
|
|
14058
|
+
function readEnsembleOverrides(opts) {
|
|
14059
|
+
try {
|
|
14060
|
+
return opts.configStore?.get()?.acp?.agents;
|
|
14061
|
+
} catch {
|
|
14062
|
+
return void 0;
|
|
14063
|
+
}
|
|
14064
|
+
}
|
|
13878
14065
|
function stripSurroundingQuotes(s) {
|
|
13879
14066
|
if (s.length < 2) return s;
|
|
13880
14067
|
const first = s[0];
|
|
@@ -15494,7 +15681,7 @@ When the error confidence is low (< 0.85) or the problem spans multiple files,
|
|
|
15494
15681
|
}
|
|
15495
15682
|
|
|
15496
15683
|
// src/slash-commands/fleet.ts
|
|
15497
|
-
import { AGENTS_BY_PHASE as AGENTS_BY_PHASE2 } from "@wrongstack/core/agent-catalog";
|
|
15684
|
+
import { AGENTS_BY_PHASE as AGENTS_BY_PHASE2, summarizeDispatchLog } from "@wrongstack/core/agent-catalog";
|
|
15498
15685
|
import { dispatchAgent as dispatchAgent3 } from "@wrongstack/core/coordination";
|
|
15499
15686
|
import { color as color29, toErrorMessage as toErrorMessage12 } from "@wrongstack/core/utils";
|
|
15500
15687
|
var PHASE_ORDER = [
|
|
@@ -15519,6 +15706,7 @@ function buildFleetCommand(opts) {
|
|
|
15519
15706
|
" /fleet status Same as /fleet (verbose status)",
|
|
15520
15707
|
" /fleet list List the agent roster grouped by phase",
|
|
15521
15708
|
" /fleet dispatch <task> Route a task to the best agent and spawn it",
|
|
15709
|
+
" /fleet routing How past spawns chose their role (roster coverage)",
|
|
15522
15710
|
" /fleet spawn <role> [count] Spawn N subagents of a role (default 1)",
|
|
15523
15711
|
" /fleet terminate <subagentId> Stop a specific subagent by id",
|
|
15524
15712
|
" /fleet kill Stop all running subagents",
|
|
@@ -15574,6 +15762,10 @@ function buildFleetCommand(opts) {
|
|
|
15574
15762
|
case "dispatch":
|
|
15575
15763
|
case "route":
|
|
15576
15764
|
return await handleDispatch(opts, subargs);
|
|
15765
|
+
// ── /fleet routing ───────────────────────────────────────────────────
|
|
15766
|
+
case "routing":
|
|
15767
|
+
case "coverage":
|
|
15768
|
+
return handleRouting(opts, subargs);
|
|
15577
15769
|
// ── /fleet concurrency [n] ───────────────────────────────────────────
|
|
15578
15770
|
case "concurrency":
|
|
15579
15771
|
case "slots":
|
|
@@ -15589,6 +15781,7 @@ function buildFleetCommand(opts) {
|
|
|
15589
15781
|
"status",
|
|
15590
15782
|
"list",
|
|
15591
15783
|
"dispatch",
|
|
15784
|
+
"routing",
|
|
15592
15785
|
"usage",
|
|
15593
15786
|
"spawn",
|
|
15594
15787
|
"terminate",
|
|
@@ -15826,6 +16019,48 @@ function handleList() {
|
|
|
15826
16019
|
}
|
|
15827
16020
|
return { message: lines.join("\n") };
|
|
15828
16021
|
}
|
|
16022
|
+
function handleRouting(opts, subargs) {
|
|
16023
|
+
const summary = summarizeDispatchLog(opts.projectRoot);
|
|
16024
|
+
if (summary.entries === 0) {
|
|
16025
|
+
const msg = "No spawn routing recorded yet. Every `spawn_subagent` appends one line to .wrongstack/agents/dispatch-log.jsonl; the log starts empty on a fresh checkout.";
|
|
16026
|
+
opts.renderer.writeInfo?.(msg);
|
|
16027
|
+
return { message: msg };
|
|
16028
|
+
}
|
|
16029
|
+
const { bySource, byMethod, rosterMisses } = summary.totals;
|
|
16030
|
+
const window = summary.since && summary.until ? `${summary.since.slice(0, 10)} \u2192 ${summary.until.slice(0, 10)}` : "unknown window";
|
|
16031
|
+
const lines = [
|
|
16032
|
+
`${color29.bold("Spawn routing")} ${color29.dim(`(${summary.entries} spawns, ${window})`)}`,
|
|
16033
|
+
"",
|
|
16034
|
+
` ${color29.cyan("addressed by")} description ${bySource.description} \xB7 explicit role ${bySource["explicit-role"]} \xB7 name only ${bySource["name-only"]}`,
|
|
16035
|
+
` ${color29.cyan("decided by ")} heuristic ${byMethod.heuristic} \xB7 classifier ${byMethod.llm} \xB7 ${color29.bold(`fallback ${byMethod.fallback}`)}`,
|
|
16036
|
+
` ${color29.cyan("roster miss ")} ${rosterMisses} ${color29.dim("(dispatched to a catalog role with no roster entry)")}`,
|
|
16037
|
+
""
|
|
16038
|
+
];
|
|
16039
|
+
const showAll = subargs.includes("--all");
|
|
16040
|
+
const spawned = summary.roles.filter((row) => row.spawns > 0);
|
|
16041
|
+
const shown = showAll ? spawned : spawned.slice(0, 15);
|
|
16042
|
+
lines.push(color29.cyan(" Roles spawned"));
|
|
16043
|
+
for (const row of shown) {
|
|
16044
|
+
const confidence = row.avgConfidence === null ? " \u2014" : row.avgConfidence.toFixed(2);
|
|
16045
|
+
lines.push(
|
|
16046
|
+
` ${color29.bold(row.role.padEnd(20))} ${String(row.spawns).padStart(5)} ${color29.dim(`conf ${confidence} last ${row.lastAt?.slice(0, 10) ?? "\u2014"}`)}`
|
|
16047
|
+
);
|
|
16048
|
+
}
|
|
16049
|
+
if (!showAll && spawned.length > shown.length) {
|
|
16050
|
+
lines.push(color29.dim(` \u2026and ${spawned.length - shown.length} more (/fleet routing --all)`));
|
|
16051
|
+
}
|
|
16052
|
+
const nearMisses = summary.roles.filter((row) => row.spawns === 0 && row.runnerUp > 0).slice(0, 15);
|
|
16053
|
+
if (nearMisses.length > 0) {
|
|
16054
|
+
lines.push("");
|
|
16055
|
+
lines.push(color29.cyan(" Considered but never chosen"));
|
|
16056
|
+
for (const row of nearMisses) {
|
|
16057
|
+
lines.push(
|
|
16058
|
+
` ${color29.bold(row.role.padEnd(20))} ${String(row.runnerUp).padStart(5)} ${color29.dim("runner-up")}`
|
|
16059
|
+
);
|
|
16060
|
+
}
|
|
16061
|
+
}
|
|
16062
|
+
return { message: lines.join("\n") };
|
|
16063
|
+
}
|
|
15829
16064
|
async function handleDispatch(opts, subargs) {
|
|
15830
16065
|
const skipConfirm = subargs.includes("--yes") || subargs.includes("-y");
|
|
15831
16066
|
const task = subargs.filter((t) => t !== "--yes" && t !== "-y").join(" ").trim();
|
|
@@ -22614,6 +22849,7 @@ function buildProfileCommand(opts) {
|
|
|
22614
22849
|
const wpaths = opts.paths;
|
|
22615
22850
|
const configStore = opts.configStore;
|
|
22616
22851
|
const renderer = opts.renderer;
|
|
22852
|
+
const syncConfig = opts.onActiveProfileChange;
|
|
22617
22853
|
return {
|
|
22618
22854
|
name: "profile",
|
|
22619
22855
|
category: "Config",
|
|
@@ -22626,8 +22862,10 @@ function buildProfileCommand(opts) {
|
|
|
22626
22862
|
"",
|
|
22627
22863
|
"Profiles store all your settings (provider, model, fallbacks, features, etc.)",
|
|
22628
22864
|
"in ~/.wrongstack/profiles/<name>/config.json.",
|
|
22629
|
-
"A successful /profile switch
|
|
22630
|
-
"
|
|
22865
|
+
"A successful /profile switch rebinds the live config watchers to the newly",
|
|
22866
|
+
"selected profile, so provider credentials and routing apply immediately.",
|
|
22867
|
+
"Profile-owned state resolved at boot (memory, skills, prompts, statusline)",
|
|
22868
|
+
"still needs a restart to follow the new profile."
|
|
22631
22869
|
].join("\n"),
|
|
22632
22870
|
async run(args) {
|
|
22633
22871
|
if (!wpaths || !configStore) {
|
|
@@ -22644,7 +22882,7 @@ function buildProfileCommand(opts) {
|
|
|
22644
22882
|
return listProfiles(wpaths, activeProfile, renderer);
|
|
22645
22883
|
}
|
|
22646
22884
|
if (sub === "switch") {
|
|
22647
|
-
return switchProfile(wpaths, rest, renderer);
|
|
22885
|
+
return switchProfile(wpaths, rest, renderer, configStore, syncConfig);
|
|
22648
22886
|
}
|
|
22649
22887
|
if (sub === "copy") {
|
|
22650
22888
|
return copyProfile(wpaths, rest, activeProfile, renderer);
|
|
@@ -22688,7 +22926,7 @@ async function listProfiles(wpaths, activeProfile, renderer) {
|
|
|
22688
22926
|
renderer.write(msg);
|
|
22689
22927
|
return { message: msg };
|
|
22690
22928
|
}
|
|
22691
|
-
async function switchProfile(wpaths, nameArg, renderer) {
|
|
22929
|
+
async function switchProfile(wpaths, nameArg, renderer, configStore, syncConfig) {
|
|
22692
22930
|
const safe = sanitizeProfileName(nameArg);
|
|
22693
22931
|
if (!safe) {
|
|
22694
22932
|
const msg2 = formatNameError(nameArg);
|
|
@@ -22716,10 +22954,57 @@ async function switchProfile(wpaths, nameArg, renderer) {
|
|
|
22716
22954
|
renderer.writeWarning(msg2);
|
|
22717
22955
|
return { message: msg2 };
|
|
22718
22956
|
}
|
|
22957
|
+
const WATCHER_OWNED = [
|
|
22958
|
+
"providers",
|
|
22959
|
+
"apiKey",
|
|
22960
|
+
"baseUrl",
|
|
22961
|
+
"fallbackModels",
|
|
22962
|
+
"fallbackBridge",
|
|
22963
|
+
"fallbackProfiles",
|
|
22964
|
+
"fallbackProfile",
|
|
22965
|
+
"favoriteModels",
|
|
22966
|
+
"favoriteModelsOnly",
|
|
22967
|
+
"modelAvailabilitySchedule",
|
|
22968
|
+
"modelMatrix",
|
|
22969
|
+
"fallbackAuto",
|
|
22970
|
+
"fallbackStickiness",
|
|
22971
|
+
"fallbackMaxLastResortCandidates",
|
|
22972
|
+
"uiLocale"
|
|
22973
|
+
];
|
|
22974
|
+
const KEEP = ["version", "activeProfile", ...WATCHER_OWNED];
|
|
22975
|
+
const incoming = { ...profileData };
|
|
22976
|
+
delete incoming["version"];
|
|
22977
|
+
const patch = { ...incoming, activeProfile: safe };
|
|
22978
|
+
const previous = configStore.get();
|
|
22979
|
+
const previousProfileRaw = await readJsonObjectFile2(
|
|
22980
|
+
wpaths.profileConfig(previous.activeProfile ?? "default")
|
|
22981
|
+
);
|
|
22982
|
+
for (const key of Object.keys(previousProfileRaw)) {
|
|
22983
|
+
if (KEEP.includes(key) || key in patch) continue;
|
|
22984
|
+
patch[key] = null;
|
|
22985
|
+
}
|
|
22986
|
+
let next;
|
|
22987
|
+
try {
|
|
22988
|
+
next = configStore.update(patch);
|
|
22989
|
+
} catch (err) {
|
|
22990
|
+
return finishSwitchFailure(renderer, safe, err);
|
|
22991
|
+
}
|
|
22992
|
+
try {
|
|
22993
|
+
syncConfig?.(next);
|
|
22994
|
+
} catch (err) {
|
|
22995
|
+
return finishSwitchFailure(renderer, safe, err);
|
|
22996
|
+
}
|
|
22719
22997
|
const msg = `${color45.green("\u2713")} Switched to profile "${color45.bold(safe)}"
|
|
22720
|
-
${color45.dim("
|
|
22998
|
+
${color45.dim("Provider credentials and routing reloaded; boot-resolved profile state (memory, skills, prompts) applies on next start.")}`;
|
|
22721
22999
|
renderer.write(msg);
|
|
22722
|
-
return { message: msg
|
|
23000
|
+
return { message: msg };
|
|
23001
|
+
}
|
|
23002
|
+
function finishSwitchFailure(renderer, name, err) {
|
|
23003
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
23004
|
+
const msg = `${color45.red("\u26A0")} Profile "${color45.bold(name)}" is selected on disk, but it could not be applied to this session: ${detail}
|
|
23005
|
+
${color45.dim("Restart WrongStack to finish the switch.")}`;
|
|
23006
|
+
renderer.writeWarning(msg);
|
|
23007
|
+
return { message: msg };
|
|
22723
23008
|
}
|
|
22724
23009
|
async function copyProfile(wpaths, nameArg, activeProfile, renderer) {
|
|
22725
23010
|
const safe = sanitizeProfileName(nameArg);
|
|
@@ -22978,16 +23263,49 @@ function buildProviderStatusCommand(tracker) {
|
|
|
22978
23263
|
|
|
22979
23264
|
// src/slash-commands/prune.ts
|
|
22980
23265
|
import { color as color47 } from "@wrongstack/core/utils";
|
|
23266
|
+
function parseMaxAgeDays(parts) {
|
|
23267
|
+
const numPart = parts.find((p) => /^\d+$/.test(p));
|
|
23268
|
+
if (!numPart) return 30;
|
|
23269
|
+
return Math.max(1, Math.min(365, Number.parseInt(numPart, 10)));
|
|
23270
|
+
}
|
|
22981
23271
|
function buildPruneCommand(opts) {
|
|
22982
23272
|
return {
|
|
22983
23273
|
name: "prune",
|
|
22984
23274
|
category: "Session",
|
|
22985
23275
|
description: "Delete old sessions. /prune (default 30d). To compress instead of delete, use /sessions archive.",
|
|
22986
|
-
help: "Usage:\n /prune Delete sessions older than 30 days.\n /prune 14 Delete sessions older than 14 days.\n /prune --dry-run Show what would be deleted without deleting.\n /prune --rebuild-index Rebuild the session index from disk.",
|
|
23276
|
+
help: "Usage:\n /prune Delete sessions older than 30 days.\n /prune 14 Delete sessions older than 14 days.\n /prune --dry-run Show what would be deleted without deleting.\n /prune --rebuild-index Rebuild the session index from disk.\n /prune --checkpoints Reclaim workspace checkpoints no session references.",
|
|
22987
23277
|
async run(args) {
|
|
22988
23278
|
const parts = args.split(/\s+/).filter(Boolean);
|
|
22989
23279
|
const rebuildIndex = parts.includes("--rebuild-index") || parts.includes("--rebuild");
|
|
22990
23280
|
const dryRun = parts.includes("--dry-run");
|
|
23281
|
+
const checkpoints = parts.includes("--checkpoints");
|
|
23282
|
+
if (checkpoints) {
|
|
23283
|
+
const store = opts.sessionStore;
|
|
23284
|
+
if (!store?.collectCheckpointGarbage) {
|
|
23285
|
+
return { message: color47.yellow("Session store does not support checkpoint GC.") };
|
|
23286
|
+
}
|
|
23287
|
+
const ageDays = parseMaxAgeDays(parts);
|
|
23288
|
+
const gc = await store.collectCheckpointGarbage(ageDays);
|
|
23289
|
+
if (gc.manifestsDeleted === 0 && gc.objectsDeleted === 0) {
|
|
23290
|
+
return {
|
|
23291
|
+
message: color47.dim(
|
|
23292
|
+
`No unreferenced checkpoints older than ${ageDays} day${ageDays === 1 ? "" : "s"} (${gc.manifestsScanned} manifest${gc.manifestsScanned === 1 ? "" : "s"}, ${gc.objectsScanned} object${gc.objectsScanned === 1 ? "" : "s"} checked).`
|
|
23293
|
+
)
|
|
23294
|
+
};
|
|
23295
|
+
}
|
|
23296
|
+
const mib = (gc.bytesReclaimed / (1024 * 1024)).toFixed(1);
|
|
23297
|
+
const lines = [
|
|
23298
|
+
`Reclaimed ${color47.green(`${mib} MiB`)} from the checkpoint store: ${color47.cyan(String(gc.manifestsDeleted))}/${gc.manifestsScanned} manifests and ${color47.cyan(String(gc.objectsDeleted))}/${gc.objectsScanned} objects.`
|
|
23299
|
+
];
|
|
23300
|
+
if (gc.errors.length > 0) {
|
|
23301
|
+
lines.push(
|
|
23302
|
+
color47.dim(
|
|
23303
|
+
`${gc.errors.length} item${gc.errors.length === 1 ? "" : "s"} were kept because they could not be read.`
|
|
23304
|
+
)
|
|
23305
|
+
);
|
|
23306
|
+
}
|
|
23307
|
+
return { message: lines.join("\n") };
|
|
23308
|
+
}
|
|
22991
23309
|
if (rebuildIndex) {
|
|
22992
23310
|
if (!opts.sessionStore?.rebuildIndex) {
|
|
22993
23311
|
return {
|
|
@@ -22999,11 +23317,7 @@ function buildPruneCommand(opts) {
|
|
|
22999
23317
|
message: count === 0 ? color47.dim("No sessions found to index.") : `Session index rebuilt: ${color47.green(String(count))} session${count === 1 ? "" : "s"} indexed.`
|
|
23000
23318
|
};
|
|
23001
23319
|
}
|
|
23002
|
-
|
|
23003
|
-
const numPart = parts.find((p) => /^\d+$/.test(p));
|
|
23004
|
-
if (numPart) {
|
|
23005
|
-
maxAgeDays = Math.max(1, Math.min(365, Number.parseInt(numPart, 10)));
|
|
23006
|
-
}
|
|
23320
|
+
const maxAgeDays = parseMaxAgeDays(parts);
|
|
23007
23321
|
if (dryRun) {
|
|
23008
23322
|
if (!opts.sessionStore) {
|
|
23009
23323
|
return { message: color47.yellow("No session store configured.") };
|
|
@@ -28218,10 +28532,10 @@ async function executeSettingsSubcommand(sub, rest, opts) {
|
|
|
28218
28532
|
}
|
|
28219
28533
|
if (sub === "token-saving") {
|
|
28220
28534
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
28221
|
-
const tiers = ["off", "minimal", "light", "medium", "aggressive"];
|
|
28535
|
+
const tiers = ["auto", "off", "minimal", "light", "medium", "aggressive"];
|
|
28222
28536
|
if (!tiers.includes(raw)) {
|
|
28223
28537
|
return {
|
|
28224
|
-
message: `${color56.amber("Usage:")} /settings token-saving off|minimal|light|medium|aggressive`
|
|
28538
|
+
message: `${color56.amber("Usage:")} /settings token-saving auto|off|minimal|light|medium|aggressive`
|
|
28225
28539
|
};
|
|
28226
28540
|
}
|
|
28227
28541
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -28230,7 +28544,8 @@ async function executeSettingsSubcommand(sub, rest, opts) {
|
|
|
28230
28544
|
cfg.features = feat;
|
|
28231
28545
|
});
|
|
28232
28546
|
return {
|
|
28233
|
-
message: `${color56.green("\u2713")} token-saving \u2192 ${color56.cyan(raw)} ${color56.dim("token-saving mode")}
|
|
28547
|
+
message: `${color56.green("\u2713")} token-saving \u2192 ${color56.cyan(raw)} ${color56.dim("token-saving mode")}
|
|
28548
|
+
` + color56.dim(" Applies to the next session - the tool registry is tiered at startup.")
|
|
28234
28549
|
};
|
|
28235
28550
|
}
|
|
28236
28551
|
if (sub === "max-concurrent") {
|
|
@@ -28663,7 +28978,7 @@ var SETTINGS_HELP = [
|
|
|
28663
28978
|
" /settings context-mode balanced|frugal|deep Context window policy",
|
|
28664
28979
|
" /settings context-strategy hybrid|intelligent|selective Compactor strategy",
|
|
28665
28980
|
" /settings context-auto-compact on|off Auto-compact context when thresholds crossed",
|
|
28666
|
-
" /settings token-saving off|minimal|light|medium|aggressive Token-saving
|
|
28981
|
+
" /settings token-saving auto|off|minimal|light|medium|aggressive Token-saving tier (next session)",
|
|
28667
28982
|
" /settings nextsteps-tool on|off Give the leader a `nextsteps` tool alongside the <nextsteps> block (next session)",
|
|
28668
28983
|
" /settings autothin on|off|status Stats-driven tool auto-thinning (off by default)",
|
|
28669
28984
|
" /settings autothin-idle <days> Tools idle for N days are candidates (default 30)",
|
|
@@ -30884,6 +31199,11 @@ function formatResultRenderMode(mode) {
|
|
|
30884
31199
|
const raw = `result:${mode}`;
|
|
30885
31200
|
return mode === "simple" ? color65.amber(raw) : color65.cyan(raw);
|
|
30886
31201
|
}
|
|
31202
|
+
function toolStatus(reg, name) {
|
|
31203
|
+
if (reg.isDisabled(name)) return color65.red("disabled");
|
|
31204
|
+
if (reg.isExposedToProvider?.(name) === false) return color65.cyan("lazy");
|
|
31205
|
+
return color65.green("direct");
|
|
31206
|
+
}
|
|
30887
31207
|
function buildToolCommand(opts) {
|
|
30888
31208
|
const help = [
|
|
30889
31209
|
"Usage:",
|
|
@@ -31010,6 +31330,20 @@ function buildToolCommand(opts) {
|
|
|
31010
31330
|
""
|
|
31011
31331
|
);
|
|
31012
31332
|
}
|
|
31333
|
+
const lazy = opts.toolRegistry.listWithOwner().filter(({ tool }) => opts.toolRegistry.isExposedToProvider?.(tool.name) === false).map(({ tool }) => tool.name);
|
|
31334
|
+
if (lazy.length > 0) {
|
|
31335
|
+
lines.push(
|
|
31336
|
+
`${color65.bold("Held back by the token-saving tier")} ${color65.dim(`(${lazy.length})`)}`,
|
|
31337
|
+
"",
|
|
31338
|
+
` ${color65.cyan("lazy")}: ${lazy.map((n) => color65.dim(n)).join(", ")}`,
|
|
31339
|
+
"",
|
|
31340
|
+
color65.dim(
|
|
31341
|
+
" Still callable via tool_search/tool_use; their schemas are not sent each turn."
|
|
31342
|
+
),
|
|
31343
|
+
color65.dim(" Change the tier with /settings token-saving <tier>."),
|
|
31344
|
+
""
|
|
31345
|
+
);
|
|
31346
|
+
}
|
|
31013
31347
|
lines.push(
|
|
31014
31348
|
color65.dim(
|
|
31015
31349
|
" /tool <name> desc simple \xB7 /tool <name> result simple \xB7 /tool list \xB7 /tool disable|enable <name>"
|
|
@@ -31023,7 +31357,7 @@ function buildToolCommand(opts) {
|
|
|
31023
31357
|
const descMode = getToolDescriptionMode(opts.toolRegistry, tool.name);
|
|
31024
31358
|
const resultMode = getToolResultRenderMode(opts.toolRegistry, tool.name);
|
|
31025
31359
|
const owner = opts.toolRegistry.ownerOf(tool.name) ?? "core";
|
|
31026
|
-
const status = opts.toolRegistry
|
|
31360
|
+
const status = toolStatus(opts.toolRegistry, tool.name);
|
|
31027
31361
|
return ` ${fit(tool.name, 28)} ${color65.dim(fit(`[${owner}]`, 28))} ${fit(status, 10)} ${fit(formatDescriptionMode(descMode), 14)} ` + formatResultRenderMode(resultMode);
|
|
31028
31362
|
});
|
|
31029
31363
|
return [
|
|
@@ -31493,6 +31827,11 @@ function formatDescriptionMode2(mode) {
|
|
|
31493
31827
|
const raw = `desc:${mode}`;
|
|
31494
31828
|
return mode === "simple" ? color66.amber(raw) : color66.dim(raw);
|
|
31495
31829
|
}
|
|
31830
|
+
function toolStatus2(reg, name) {
|
|
31831
|
+
if (reg.isDisabled(name)) return color66.red("disabled");
|
|
31832
|
+
if (reg.isExposedToProvider?.(name) === false) return color66.cyan("lazy");
|
|
31833
|
+
return color66.green("direct");
|
|
31834
|
+
}
|
|
31496
31835
|
function buildToolsCommand(opts) {
|
|
31497
31836
|
return {
|
|
31498
31837
|
name: "tools",
|
|
@@ -31520,13 +31859,30 @@ function buildToolsCommand(opts) {
|
|
|
31520
31859
|
const lines = all.map(({ tool, owner }) => {
|
|
31521
31860
|
const mode = getToolDescriptionMode2(reg, tool.name);
|
|
31522
31861
|
const rw = tool.mutating ? color66.yellow(fit2("mut", 4)) : color66.cyan(fit2("ro", 4));
|
|
31523
|
-
const status = reg
|
|
31862
|
+
const status = toolStatus2(reg, tool.name);
|
|
31524
31863
|
return ` ${fit2(tool.name, 28)} ${color66.dim(fit2(`[${owner}]`, 28))} ${rw} ${color66.dim(fit2(tool.permission, 8))} ${fit2(status, 10)} ` + formatDescriptionMode2(mode);
|
|
31525
31864
|
});
|
|
31526
|
-
const
|
|
31527
|
-
|
|
31865
|
+
const lazy = allTools.filter(
|
|
31866
|
+
({ tool }) => !reg.isDisabled(tool.name) && reg.isExposedToProvider?.(tool.name) === false
|
|
31867
|
+
);
|
|
31868
|
+
const notes = [];
|
|
31869
|
+
if (lazy.length > 0) {
|
|
31870
|
+
notes.push(
|
|
31871
|
+
`${lazy.length} tool(s) held back from the provider by the token-saving tier - still callable via tool_search/tool_use. Change with /settings token-saving <tier>.`
|
|
31872
|
+
);
|
|
31873
|
+
}
|
|
31874
|
+
if (disabled.length > 0) {
|
|
31875
|
+
notes.push(
|
|
31876
|
+
`${disabled.length} tool(s) disabled. Use /tool enable <name> or /tool enable-all to restore.`
|
|
31877
|
+
);
|
|
31878
|
+
}
|
|
31879
|
+
const extra = notes.length > 0 ? `
|
|
31880
|
+
${color66.dim(notes.join("\n"))}` : "";
|
|
31528
31881
|
const filterNote = filter ? color66.dim(` matching "${filter}" (${all.length} of ${allTools.length})`) : "";
|
|
31529
|
-
const
|
|
31882
|
+
const directCount = allTools.filter(
|
|
31883
|
+
({ tool }) => !reg.isDisabled(tool.name) && reg.isExposedToProvider?.(tool.name) !== false
|
|
31884
|
+
).length;
|
|
31885
|
+
const msg = `${color66.bold("Tools")}${filterNote} (${all.length} shown, ${directCount} direct, ${lazy.length} lazy, ${disabled.length} disabled) ${color66.dim("description detail via /tool <name> simple|extend")}:
|
|
31530
31886
|
${header}
|
|
31531
31887
|
${lines.join("\n")}${extra}
|
|
31532
31888
|
`;
|
|
@@ -32506,7 +32862,72 @@ function buildWorktreeCommand(opts) {
|
|
|
32506
32862
|
}
|
|
32507
32863
|
|
|
32508
32864
|
// src/slash-commands/yolo.ts
|
|
32865
|
+
import {
|
|
32866
|
+
ALL_DESTRUCTIVE_KINDS,
|
|
32867
|
+
isDestructiveKind,
|
|
32868
|
+
LOCKED_DESTRUCTIVE_KINDS
|
|
32869
|
+
} from "@wrongstack/core/security";
|
|
32509
32870
|
import { color as color69 } from "@wrongstack/core/utils";
|
|
32871
|
+
var KIND_BLURBS = {
|
|
32872
|
+
"disk-wipe": "mkfs, dd to a raw device, format C:, fork bomb",
|
|
32873
|
+
"system-halt": "shutdown, reboot",
|
|
32874
|
+
"delete-outside": "recursive force-delete that leaves the project, or hits a system/home root",
|
|
32875
|
+
"git-history": "reset --hard, clean -f, push --force, filter-branch",
|
|
32876
|
+
publish: "npm publish, docker push, kubectl delete namespace",
|
|
32877
|
+
"download-and-run": "curl | sh, and inline payloads that fetch then execute",
|
|
32878
|
+
"bulk-delete": "find -exec rm, inline rmSync across many paths",
|
|
32879
|
+
"agent-state": "writes to WrongStack's own config.json / trust.json / auth.json",
|
|
32880
|
+
"credential-bind": "attaching a well-known API key to a chosen provider endpoint"
|
|
32881
|
+
};
|
|
32882
|
+
function renderConfirmList(opts, map) {
|
|
32883
|
+
const lines = ["Kinds that still prompt while YOLO is on:", ""];
|
|
32884
|
+
for (const kind of ALL_DESTRUCTIVE_KINDS) {
|
|
32885
|
+
const locked = LOCKED_DESTRUCTIVE_KINDS.has(kind);
|
|
32886
|
+
const state = map[kind] ? locked ? color69.dim("ALWAYS ASKS") : color69.yellow("ASKS") : color69.green("runs");
|
|
32887
|
+
lines.push(` ${kind.padEnd(17)} ${state.padEnd(20)} ${color69.dim(KIND_BLURBS[kind])}`);
|
|
32888
|
+
}
|
|
32889
|
+
lines.push("", color69.dim(" /yolo confirm <kind> off lets YOLO run that kind unattended"));
|
|
32890
|
+
lines.push(
|
|
32891
|
+
color69.dim(" ALWAYS ASKS cannot be turned off \u2014 those writes can disable approval itself")
|
|
32892
|
+
);
|
|
32893
|
+
const msg = lines.join("\n");
|
|
32894
|
+
opts.renderer.write(msg);
|
|
32895
|
+
return { message: msg };
|
|
32896
|
+
}
|
|
32897
|
+
function runConfirm(opts, rest) {
|
|
32898
|
+
if (!opts.onYoloConfirm) {
|
|
32899
|
+
const msg2 = "Per-kind YOLO confirmation is not available in this session.";
|
|
32900
|
+
opts.renderer.writeWarning(msg2);
|
|
32901
|
+
return { message: msg2 };
|
|
32902
|
+
}
|
|
32903
|
+
if (!rest) return renderConfirmList(opts, opts.onYoloConfirm());
|
|
32904
|
+
const [rawKind, rawState] = rest.split(/\s+/);
|
|
32905
|
+
if (!isDestructiveKind(rawKind)) {
|
|
32906
|
+
const msg2 = `Unknown kind: ${rawKind}. Known kinds: ${ALL_DESTRUCTIVE_KINDS.join(", ")}.`;
|
|
32907
|
+
opts.renderer.writeWarning(msg2);
|
|
32908
|
+
return { message: msg2 };
|
|
32909
|
+
}
|
|
32910
|
+
if (rawState === void 0) {
|
|
32911
|
+
const msg2 = `Usage: /yolo confirm ${rawKind} on|off`;
|
|
32912
|
+
opts.renderer.writeWarning(msg2);
|
|
32913
|
+
return { message: msg2 };
|
|
32914
|
+
}
|
|
32915
|
+
const confirm = rawState === "on" || rawState === "true" || rawState === "1";
|
|
32916
|
+
if (!confirm && !["off", "false", "0"].includes(rawState)) {
|
|
32917
|
+
const msg2 = `Unknown state: ${rawState}. Use on or off.`;
|
|
32918
|
+
opts.renderer.writeWarning(msg2);
|
|
32919
|
+
return { message: msg2 };
|
|
32920
|
+
}
|
|
32921
|
+
const after = opts.onYoloConfirm({ kind: rawKind, confirm });
|
|
32922
|
+
if (!confirm && after[rawKind]) {
|
|
32923
|
+
const msg2 = `${color69.amber(rawKind)} always asks \u2014 ${KIND_BLURBS[rawKind]} can switch the approval system itself off.`;
|
|
32924
|
+
opts.renderer.writeWarning(msg2);
|
|
32925
|
+
return { message: msg2 };
|
|
32926
|
+
}
|
|
32927
|
+
const msg = confirm ? `${color69.yellow(rawKind)} will keep asking under YOLO.` : `${color69.green(rawKind)} will now run unattended under YOLO.`;
|
|
32928
|
+
opts.renderer.write(msg);
|
|
32929
|
+
return { message: msg };
|
|
32930
|
+
}
|
|
32510
32931
|
function buildYoloCommand(opts) {
|
|
32511
32932
|
return {
|
|
32512
32933
|
name: "yolo",
|
|
@@ -32517,9 +32938,14 @@ function buildYoloCommand(opts) {
|
|
|
32517
32938
|
" /yolo Show current YOLO status",
|
|
32518
32939
|
" /yolo on Enable YOLO mode (auto-approve tool calls)",
|
|
32519
32940
|
" /yolo off Disable YOLO mode (restore permission prompts)",
|
|
32520
|
-
" /yolo
|
|
32941
|
+
" /yolo confirm List which kinds of damage still prompt under YOLO",
|
|
32942
|
+
" /yolo confirm <kind> on Keep prompting for that kind",
|
|
32943
|
+
" /yolo confirm <kind> off Let YOLO run that kind unattended",
|
|
32521
32944
|
"",
|
|
32522
|
-
"YOLO
|
|
32945
|
+
"YOLO auto-approves tool calls unless an explicit deny rule blocks them, or",
|
|
32946
|
+
"the call would do damage of a kind still listed under `/yolo confirm`.",
|
|
32947
|
+
`Kinds: ${ALL_DESTRUCTIVE_KINDS.join(", ")}.`,
|
|
32948
|
+
`Always gated (they can switch approval itself off): ${[...LOCKED_DESTRUCTIVE_KINDS].join(", ")}.`
|
|
32523
32949
|
].join("\n"),
|
|
32524
32950
|
async run(args) {
|
|
32525
32951
|
const arg = args.trim().toLowerCase();
|
|
@@ -32542,18 +32968,10 @@ function buildYoloCommand(opts) {
|
|
|
32542
32968
|
newState = false;
|
|
32543
32969
|
} else if (arg === "toggle") {
|
|
32544
32970
|
newState = !opts.onYolo();
|
|
32545
|
-
} else if (arg === "
|
|
32546
|
-
|
|
32547
|
-
if (!currentMode) {
|
|
32548
|
-
const msg3 = `${color69.amber("YOLO is OFF.")} Destructive-gate flags are deprecated; prompts are active because YOLO is off.`;
|
|
32549
|
-
opts.renderer.writeWarning(msg3);
|
|
32550
|
-
return { message: msg3 };
|
|
32551
|
-
}
|
|
32552
|
-
const msg2 = `${color69.amber("Destructive gate:")} ${color69.dim("deprecated \u2014 YOLO auto-approves all non-denied tool calls.")}`;
|
|
32553
|
-
opts.renderer.writeWarning(msg2);
|
|
32554
|
-
return { message: msg2 };
|
|
32971
|
+
} else if (arg === "confirm" || arg.startsWith("confirm ")) {
|
|
32972
|
+
return runConfirm(opts, arg.slice("confirm".length).trim());
|
|
32555
32973
|
} else {
|
|
32556
|
-
const msg2 = `Unknown argument: ${arg}. Use /yolo on, /yolo off, /yolo toggle, or /yolo
|
|
32974
|
+
const msg2 = `Unknown argument: ${arg}. Use /yolo on, /yolo off, /yolo toggle, or /yolo confirm.`;
|
|
32557
32975
|
opts.renderer.writeWarning(msg2);
|
|
32558
32976
|
return { message: msg2 };
|
|
32559
32977
|
}
|
|
@@ -32742,6 +33160,7 @@ function setupCliSlashCommands(params) {
|
|
|
32742
33160
|
eventWiring,
|
|
32743
33161
|
mcpRegistry,
|
|
32744
33162
|
setYoloMode,
|
|
33163
|
+
setYoloConfirm,
|
|
32745
33164
|
getNextPredict,
|
|
32746
33165
|
setNextPredict,
|
|
32747
33166
|
getCurrentSuggestions,
|
|
@@ -32818,6 +33237,7 @@ function setupCliSlashCommands(params) {
|
|
|
32818
33237
|
...agentMonitor ? { agentMonitor } : {},
|
|
32819
33238
|
onPanelOpen,
|
|
32820
33239
|
configStore,
|
|
33240
|
+
onActiveProfileChange: params.onActiveProfileChange,
|
|
32821
33241
|
reader,
|
|
32822
33242
|
readSecret: secretInputController.readSecret,
|
|
32823
33243
|
readText: secretInputController.readText,
|
|
@@ -32849,6 +33269,7 @@ function setupCliSlashCommands(params) {
|
|
|
32849
33269
|
setEventMaxContext: eventWiring.setEffectiveMaxContext,
|
|
32850
33270
|
mcpRegistry,
|
|
32851
33271
|
onYolo: setYoloMode,
|
|
33272
|
+
onYoloConfirm: setYoloConfirm,
|
|
32852
33273
|
getNextPredict,
|
|
32853
33274
|
setNextPredict,
|
|
32854
33275
|
getCurrentSuggestions,
|
|
@@ -32921,8 +33342,14 @@ function setupCliSlashCommands(params) {
|
|
|
32921
33342
|
import {
|
|
32922
33343
|
areSubagentsAllowedForSession as areSubagentsAllowedForSession4,
|
|
32923
33344
|
FLEET_ROSTER as FLEET_ROSTER5,
|
|
32924
|
-
mailboxSessionTag as mailboxSessionTag6
|
|
33345
|
+
mailboxSessionTag as mailboxSessionTag6,
|
|
33346
|
+
makeKanbanQueueTool
|
|
32925
33347
|
} from "@wrongstack/core/coordination";
|
|
33348
|
+
import {
|
|
33349
|
+
ALL_DESTRUCTIVE_KINDS as ALL_DESTRUCTIVE_KINDS2,
|
|
33350
|
+
normalizeYoloConfirmKinds,
|
|
33351
|
+
resolveYoloConfirmKinds
|
|
33352
|
+
} from "@wrongstack/core/security";
|
|
32926
33353
|
import {
|
|
32927
33354
|
AgentError as AgentError3,
|
|
32928
33355
|
resolveFleetChatVerbosity as resolveFleetChatVerbosity2
|
|
@@ -33002,6 +33429,44 @@ async function setupCommandHostState(input) {
|
|
|
33002
33429
|
input.hqCommandController.killFleet = () => killFleet2(input.getDirector());
|
|
33003
33430
|
input.hqCommandController.terminateAgent = (subagentId) => terminateAgent(input.getDirector(), subagentId);
|
|
33004
33431
|
input.hqCommandController.spawnAgent = (role, task, maxIterations) => spawnAgent2(input.getDirector(), liveSessionId(), role, task, maxIterations);
|
|
33432
|
+
input.hqCommandController.kanbanDispatch = async (request) => {
|
|
33433
|
+
const director = input.getDirector();
|
|
33434
|
+
const sessionId = request.sessionId ?? liveSessionId();
|
|
33435
|
+
if (director === null) {
|
|
33436
|
+
throw new AgentError3({
|
|
33437
|
+
message: "No Director is active for Kanban dispatch.",
|
|
33438
|
+
code: "AGENT_RUN_FAILED",
|
|
33439
|
+
context: { phase: "hq-kanban-dispatch", taskId: request.taskId }
|
|
33440
|
+
});
|
|
33441
|
+
}
|
|
33442
|
+
if (!areSubagentsAllowedForSession4(sessionId)) {
|
|
33443
|
+
throw new Error("Subagents are disabled for this session.");
|
|
33444
|
+
}
|
|
33445
|
+
const result = await makeKanbanQueueTool(director).execute(
|
|
33446
|
+
{
|
|
33447
|
+
action: "dispatch_ready",
|
|
33448
|
+
boardId: request.boardId,
|
|
33449
|
+
taskId: request.taskId,
|
|
33450
|
+
maxTasks: 1,
|
|
33451
|
+
awaitCompletion: false
|
|
33452
|
+
},
|
|
33453
|
+
{
|
|
33454
|
+
projectRoot: input.projectRoot,
|
|
33455
|
+
eventSessionId: () => sessionId
|
|
33456
|
+
},
|
|
33457
|
+
{ signal: new AbortController().signal }
|
|
33458
|
+
);
|
|
33459
|
+
if (result.ok !== true || result.count !== 1) {
|
|
33460
|
+
throw new AgentError3({
|
|
33461
|
+
message: result.errors?.[0]?.error ?? result.message ?? "Kanban task could not be dispatched.",
|
|
33462
|
+
code: "AGENT_RUN_FAILED",
|
|
33463
|
+
context: { phase: "hq-kanban-dispatch", taskId: request.taskId }
|
|
33464
|
+
});
|
|
33465
|
+
}
|
|
33466
|
+
const subagentId = result.dispatched?.[0]?.subagentId;
|
|
33467
|
+
const summary = subagentId ? `task ${request.taskId} dispatched to ${subagentId}` : `task ${request.taskId} dispatched`;
|
|
33468
|
+
return `${summary} \xB7 ${request.comment}`;
|
|
33469
|
+
};
|
|
33005
33470
|
const enhanceController = createEnhanceController(input.getConfig());
|
|
33006
33471
|
const statuslineConfigDeps = createStatuslineConfigDeps();
|
|
33007
33472
|
const hidden = await loadStatuslineHiddenItems();
|
|
@@ -33054,6 +33519,31 @@ async function setupCommandHostState(input) {
|
|
|
33054
33519
|
}
|
|
33055
33520
|
return input.permissionPolicy.getYolo?.() ?? input.getConfig().yolo ?? false;
|
|
33056
33521
|
};
|
|
33522
|
+
const setYoloConfirm = (update) => {
|
|
33523
|
+
if (update) {
|
|
33524
|
+
const current = resolveYoloConfirmKinds(input.getConfig().autonomy?.yoloConfirm);
|
|
33525
|
+
const next = new Set(current);
|
|
33526
|
+
if (update.confirm) next.add(update.kind);
|
|
33527
|
+
else next.delete(update.kind);
|
|
33528
|
+
const normalized = normalizeYoloConfirmKinds(next);
|
|
33529
|
+
input.permissionPolicy.setYoloConfirmKinds?.(normalized);
|
|
33530
|
+
const config = input.getConfig();
|
|
33531
|
+
input.setConfig(
|
|
33532
|
+
patchConfig(config, {
|
|
33533
|
+
autonomy: {
|
|
33534
|
+
...config.autonomy,
|
|
33535
|
+
yoloConfirm: Object.fromEntries(
|
|
33536
|
+
ALL_DESTRUCTIVE_KINDS2.map((kind) => [kind, normalized.has(kind)])
|
|
33537
|
+
)
|
|
33538
|
+
}
|
|
33539
|
+
})
|
|
33540
|
+
);
|
|
33541
|
+
}
|
|
33542
|
+
const effective = input.permissionPolicy.getYoloConfirmKinds?.() ?? resolveYoloConfirmKinds(input.getConfig().autonomy?.yoloConfirm);
|
|
33543
|
+
return Object.fromEntries(
|
|
33544
|
+
ALL_DESTRUCTIVE_KINDS2.map((kind) => [kind, effective.has(kind)])
|
|
33545
|
+
);
|
|
33546
|
+
};
|
|
33057
33547
|
const secretInputController = {
|
|
33058
33548
|
readSecret: (prompt) => input.reader.readSecret(prompt),
|
|
33059
33549
|
readText: (prompt) => input.reader.readLine(prompt)
|
|
@@ -33088,6 +33578,7 @@ async function setupCommandHostState(input) {
|
|
|
33088
33578
|
goalHost,
|
|
33089
33579
|
coordinatorController,
|
|
33090
33580
|
setYoloMode,
|
|
33581
|
+
setYoloConfirm,
|
|
33091
33582
|
secretInputController,
|
|
33092
33583
|
sddRunRegistry
|
|
33093
33584
|
};
|
|
@@ -33283,9 +33774,12 @@ async function ensureDirectorAndAnnounce(args) {
|
|
|
33283
33774
|
const director = await multiAgentHost.ensureDirector();
|
|
33284
33775
|
if (director) {
|
|
33285
33776
|
if (priorFleetState) director.setCheckpointState(priorFleetState);
|
|
33777
|
+
const directorToolNames = [];
|
|
33286
33778
|
for (const tool of director.tools(FLEET_ROSTER6)) {
|
|
33287
33779
|
toolRegistry.register(tool);
|
|
33780
|
+
directorToolNames.push(tool.name);
|
|
33288
33781
|
}
|
|
33782
|
+
toolRegistry.exposeToProvider(directorToolNames);
|
|
33289
33783
|
const browserSurface = flags.webui === true || flags.simpleui === true;
|
|
33290
33784
|
if (browserSurface) {
|
|
33291
33785
|
renderer.writeInfo(
|
|
@@ -33644,7 +34138,9 @@ function setupHqTelemetry(deps) {
|
|
|
33644
34138
|
// writer ref, because an in-process resume / session.new swaps
|
|
33645
34139
|
// `ctx.session` for a new writer object and this captured one goes stale.
|
|
33646
34140
|
sessionId: () => session.id,
|
|
33647
|
-
allowRunCommand: () => flags["hq-allow-exec"] === true
|
|
34141
|
+
allowRunCommand: () => flags["hq-allow-exec"] === true,
|
|
34142
|
+
kanbanTransition: createProjectKanbanTransitionHandler(projectRoot),
|
|
34143
|
+
kanbanAssign: createProjectKanbanAssignHandler(projectRoot)
|
|
33648
34144
|
};
|
|
33649
34145
|
const hqOnCommand = createHqCommandDispatcher(hqCommandController);
|
|
33650
34146
|
const hqConnection = startCliHqConnection({
|
|
@@ -33658,7 +34154,8 @@ function setupHqTelemetry(deps) {
|
|
|
33658
34154
|
"mailbox.summary",
|
|
33659
34155
|
"fleet.summary",
|
|
33660
34156
|
"session.summary",
|
|
33661
|
-
"control.receive"
|
|
34157
|
+
"control.receive",
|
|
34158
|
+
"kanban.dispatch"
|
|
33662
34159
|
],
|
|
33663
34160
|
onConnect: (publisher) => {
|
|
33664
34161
|
hqPublisherRef.current = publisher;
|
|
@@ -33807,9 +34304,9 @@ import { NotifierImpl } from "@wrongstack/core/notifications";
|
|
|
33807
34304
|
import {
|
|
33808
34305
|
SlashCommandRegistry
|
|
33809
34306
|
} from "@wrongstack/core/registry";
|
|
33810
|
-
import { normalizeTokenSavingTier as normalizeTokenSavingTier5 } from "@wrongstack/core/types";
|
|
33811
34307
|
import {
|
|
33812
34308
|
CONTEXT_WINDOW_MODE_PINNED_META_KEY as CONTEXT_WINDOW_MODE_PINNED_META_KEY2,
|
|
34309
|
+
normalizeTokenSavingTier as normalizeTokenSavingTier5,
|
|
33813
34310
|
resolveContextWindowPolicy as resolveContextWindowPolicy3
|
|
33814
34311
|
} from "@wrongstack/core/types";
|
|
33815
34312
|
import {
|
|
@@ -34418,12 +34915,129 @@ function createAgent(params) {
|
|
|
34418
34915
|
executionStrategy: params.config.tools.defaultExecutionStrategy,
|
|
34419
34916
|
perIterationOutputCapBytes: params.config.tools.perIterationOutputCapBytes,
|
|
34420
34917
|
loopDetection: params.config.tools.loopDetection,
|
|
34918
|
+
// Previously unwired: the Agent fell back to `autoExtendLimit: false`, so
|
|
34919
|
+
// `tools.autoExtendLimit` did nothing on the CLI/TUI host while the WebUI
|
|
34920
|
+
// host honoured it — the same config produced two different budget
|
|
34921
|
+
// behaviours depending on the surface.
|
|
34922
|
+
...params.config.tools.autoExtendLimit !== void 0 ? { autoExtendLimit: params.config.tools.autoExtendLimit } : {},
|
|
34923
|
+
...params.config.tools.maxAutoExtensions !== void 0 ? { maxAutoExtensions: params.config.tools.maxAutoExtensions } : {},
|
|
34421
34924
|
confirmAwaiter: params.confirmAwaiter,
|
|
34422
34925
|
toolExecutor,
|
|
34423
34926
|
tracer: params.tracer
|
|
34424
34927
|
});
|
|
34425
34928
|
}
|
|
34426
34929
|
|
|
34930
|
+
// src/wiring/prompt-journal-recorder.ts
|
|
34931
|
+
import {
|
|
34932
|
+
PROMPT_JOURNAL_RAW_MARKER,
|
|
34933
|
+
recordPromptJournalEntry
|
|
34934
|
+
} from "@wrongstack/core/prompts";
|
|
34935
|
+
function systemPromptText(blocks) {
|
|
34936
|
+
if (!blocks || blocks.length === 0) return "";
|
|
34937
|
+
return blocks.map((block) => block.type === "text" ? block.text : "").filter((text) => text.trim().length > 0).join("\n").trim();
|
|
34938
|
+
}
|
|
34939
|
+
function createPromptJournalRecorder() {
|
|
34940
|
+
const systemPromptRecorded = /* @__PURE__ */ new Set();
|
|
34941
|
+
return {
|
|
34942
|
+
name: "PromptJournalRecorder",
|
|
34943
|
+
async handler(payload, next) {
|
|
34944
|
+
const ctx = payload.ctx;
|
|
34945
|
+
const meta = ctx.meta;
|
|
34946
|
+
const raw = typeof meta?.[PROMPT_JOURNAL_RAW_MARKER] === "string" ? meta[PROMPT_JOURNAL_RAW_MARKER] : void 0;
|
|
34947
|
+
if (meta) delete meta[PROMPT_JOURNAL_RAW_MARKER];
|
|
34948
|
+
const projectRoot = ctx.projectRoot;
|
|
34949
|
+
const text = payload.text.trim();
|
|
34950
|
+
if (projectRoot && text) {
|
|
34951
|
+
const sessionId = ctx.session?.id ?? ctx.activeRunSessionId ?? "general";
|
|
34952
|
+
const common = {
|
|
34953
|
+
projectRoot,
|
|
34954
|
+
sessionId,
|
|
34955
|
+
model: ctx.model,
|
|
34956
|
+
provider: ctx.provider?.id,
|
|
34957
|
+
activeTools: ctx.tools?.map((tool) => tool.name) ?? []
|
|
34958
|
+
};
|
|
34959
|
+
if (raw !== void 0 && raw !== text) {
|
|
34960
|
+
void recordPromptJournalEntry({
|
|
34961
|
+
...common,
|
|
34962
|
+
category: "refined_user",
|
|
34963
|
+
content: text,
|
|
34964
|
+
rawContent: raw
|
|
34965
|
+
}).catch(() => {
|
|
34966
|
+
});
|
|
34967
|
+
} else {
|
|
34968
|
+
void recordPromptJournalEntry({
|
|
34969
|
+
...common,
|
|
34970
|
+
category: "raw_user",
|
|
34971
|
+
content: text
|
|
34972
|
+
}).catch(() => {
|
|
34973
|
+
});
|
|
34974
|
+
}
|
|
34975
|
+
if (!systemPromptRecorded.has(sessionId)) {
|
|
34976
|
+
systemPromptRecorded.add(sessionId);
|
|
34977
|
+
const systemText = systemPromptText(ctx.systemPrompt);
|
|
34978
|
+
if (systemText) {
|
|
34979
|
+
void recordPromptJournalEntry({
|
|
34980
|
+
...common,
|
|
34981
|
+
category: "system_prompt",
|
|
34982
|
+
content: systemText
|
|
34983
|
+
}).catch(() => {
|
|
34984
|
+
});
|
|
34985
|
+
}
|
|
34986
|
+
}
|
|
34987
|
+
}
|
|
34988
|
+
return next(payload);
|
|
34989
|
+
}
|
|
34990
|
+
};
|
|
34991
|
+
}
|
|
34992
|
+
var TOOL_CATEGORY = {
|
|
34993
|
+
clarify: "clarify_interaction",
|
|
34994
|
+
delegate: "subagent_delegation",
|
|
34995
|
+
continue_to_next_iteration: "autonomous_next_step"
|
|
34996
|
+
};
|
|
34997
|
+
function createPromptJournalToolCallRecorder() {
|
|
34998
|
+
return {
|
|
34999
|
+
name: "PromptJournalToolCallRecorder",
|
|
35000
|
+
async handler(payload, next) {
|
|
35001
|
+
const ctx = payload.ctx;
|
|
35002
|
+
const projectRoot = ctx.projectRoot;
|
|
35003
|
+
const toolName2 = payload.toolUse?.name;
|
|
35004
|
+
const category = toolName2 ? TOOL_CATEGORY[toolName2] : void 0;
|
|
35005
|
+
if (projectRoot && category) {
|
|
35006
|
+
const input = payload.toolUse.input;
|
|
35007
|
+
const sessionId = ctx.session?.id ?? ctx.activeRunSessionId ?? "general";
|
|
35008
|
+
const common = {
|
|
35009
|
+
projectRoot,
|
|
35010
|
+
sessionId,
|
|
35011
|
+
model: ctx.model,
|
|
35012
|
+
provider: ctx.provider?.id,
|
|
35013
|
+
activeTools: ctx.tools?.map((tool) => tool.name) ?? []
|
|
35014
|
+
};
|
|
35015
|
+
let content = "";
|
|
35016
|
+
let decisionReason;
|
|
35017
|
+
if (toolName2 === "clarify") {
|
|
35018
|
+
content = typeof input?.question === "string" ? input.question : "";
|
|
35019
|
+
decisionReason = typeof input?.context === "string" ? input.context : void 0;
|
|
35020
|
+
} else if (toolName2 === "delegate") {
|
|
35021
|
+
content = typeof input?.task === "string" ? input.task : "";
|
|
35022
|
+
decisionReason = typeof input?.role === "string" ? `delegated to ${input.role}` : typeof input?.name === "string" ? `delegated to ${input.name}` : void 0;
|
|
35023
|
+
} else {
|
|
35024
|
+
content = "Autonomous continue to the next iteration";
|
|
35025
|
+
}
|
|
35026
|
+
if (content.trim()) {
|
|
35027
|
+
void recordPromptJournalEntry({
|
|
35028
|
+
...common,
|
|
35029
|
+
category,
|
|
35030
|
+
content,
|
|
35031
|
+
decisionReason
|
|
35032
|
+
}).catch(() => {
|
|
35033
|
+
});
|
|
35034
|
+
}
|
|
35035
|
+
}
|
|
35036
|
+
return next(payload);
|
|
35037
|
+
}
|
|
35038
|
+
};
|
|
35039
|
+
}
|
|
35040
|
+
|
|
34427
35041
|
// src/wiring/provider-utility-tools.ts
|
|
34428
35042
|
import {
|
|
34429
35043
|
createCouncilPersonaRegistry,
|
|
@@ -34557,117 +35171,6 @@ function registerOrOverride(registry, name, tool) {
|
|
|
34557
35171
|
}
|
|
34558
35172
|
}
|
|
34559
35173
|
|
|
34560
|
-
// src/wiring/prompt-journal-recorder.ts
|
|
34561
|
-
import {
|
|
34562
|
-
PROMPT_JOURNAL_RAW_MARKER,
|
|
34563
|
-
recordPromptJournalEntry
|
|
34564
|
-
} from "@wrongstack/core/prompts";
|
|
34565
|
-
function systemPromptText(blocks) {
|
|
34566
|
-
if (!blocks || blocks.length === 0) return "";
|
|
34567
|
-
return blocks.map((block) => block.type === "text" ? block.text : "").filter((text) => text.trim().length > 0).join("\n").trim();
|
|
34568
|
-
}
|
|
34569
|
-
function createPromptJournalRecorder() {
|
|
34570
|
-
const systemPromptRecorded = /* @__PURE__ */ new Set();
|
|
34571
|
-
return {
|
|
34572
|
-
name: "PromptJournalRecorder",
|
|
34573
|
-
async handler(payload, next) {
|
|
34574
|
-
const ctx = payload.ctx;
|
|
34575
|
-
const meta = ctx.meta;
|
|
34576
|
-
const raw = typeof meta?.[PROMPT_JOURNAL_RAW_MARKER] === "string" ? meta[PROMPT_JOURNAL_RAW_MARKER] : void 0;
|
|
34577
|
-
if (meta) delete meta[PROMPT_JOURNAL_RAW_MARKER];
|
|
34578
|
-
const projectRoot = ctx.projectRoot;
|
|
34579
|
-
const text = payload.text.trim();
|
|
34580
|
-
if (projectRoot && text) {
|
|
34581
|
-
const sessionId = ctx.session?.id ?? ctx.activeRunSessionId ?? "general";
|
|
34582
|
-
const common = {
|
|
34583
|
-
projectRoot,
|
|
34584
|
-
sessionId,
|
|
34585
|
-
model: ctx.model,
|
|
34586
|
-
provider: ctx.provider?.id,
|
|
34587
|
-
activeTools: ctx.tools?.map((tool) => tool.name) ?? []
|
|
34588
|
-
};
|
|
34589
|
-
if (raw !== void 0 && raw !== text) {
|
|
34590
|
-
void recordPromptJournalEntry({
|
|
34591
|
-
...common,
|
|
34592
|
-
category: "refined_user",
|
|
34593
|
-
content: text,
|
|
34594
|
-
rawContent: raw
|
|
34595
|
-
}).catch(() => {
|
|
34596
|
-
});
|
|
34597
|
-
} else {
|
|
34598
|
-
void recordPromptJournalEntry({
|
|
34599
|
-
...common,
|
|
34600
|
-
category: "raw_user",
|
|
34601
|
-
content: text
|
|
34602
|
-
}).catch(() => {
|
|
34603
|
-
});
|
|
34604
|
-
}
|
|
34605
|
-
if (!systemPromptRecorded.has(sessionId)) {
|
|
34606
|
-
systemPromptRecorded.add(sessionId);
|
|
34607
|
-
const systemText = systemPromptText(ctx.systemPrompt);
|
|
34608
|
-
if (systemText) {
|
|
34609
|
-
void recordPromptJournalEntry({
|
|
34610
|
-
...common,
|
|
34611
|
-
category: "system_prompt",
|
|
34612
|
-
content: systemText
|
|
34613
|
-
}).catch(() => {
|
|
34614
|
-
});
|
|
34615
|
-
}
|
|
34616
|
-
}
|
|
34617
|
-
}
|
|
34618
|
-
return next(payload);
|
|
34619
|
-
}
|
|
34620
|
-
};
|
|
34621
|
-
}
|
|
34622
|
-
var TOOL_CATEGORY = {
|
|
34623
|
-
clarify: "clarify_interaction",
|
|
34624
|
-
delegate: "subagent_delegation",
|
|
34625
|
-
continue_to_next_iteration: "autonomous_next_step"
|
|
34626
|
-
};
|
|
34627
|
-
function createPromptJournalToolCallRecorder() {
|
|
34628
|
-
return {
|
|
34629
|
-
name: "PromptJournalToolCallRecorder",
|
|
34630
|
-
async handler(payload, next) {
|
|
34631
|
-
const ctx = payload.ctx;
|
|
34632
|
-
const projectRoot = ctx.projectRoot;
|
|
34633
|
-
const toolName = payload.toolUse?.name;
|
|
34634
|
-
const category = toolName ? TOOL_CATEGORY[toolName] : void 0;
|
|
34635
|
-
if (projectRoot && category) {
|
|
34636
|
-
const input = payload.toolUse.input;
|
|
34637
|
-
const sessionId = ctx.session?.id ?? ctx.activeRunSessionId ?? "general";
|
|
34638
|
-
const common = {
|
|
34639
|
-
projectRoot,
|
|
34640
|
-
sessionId,
|
|
34641
|
-
model: ctx.model,
|
|
34642
|
-
provider: ctx.provider?.id,
|
|
34643
|
-
activeTools: ctx.tools?.map((tool) => tool.name) ?? []
|
|
34644
|
-
};
|
|
34645
|
-
let content = "";
|
|
34646
|
-
let decisionReason;
|
|
34647
|
-
if (toolName === "clarify") {
|
|
34648
|
-
content = typeof input?.question === "string" ? input.question : "";
|
|
34649
|
-
decisionReason = typeof input?.context === "string" ? input.context : void 0;
|
|
34650
|
-
} else if (toolName === "delegate") {
|
|
34651
|
-
content = typeof input?.task === "string" ? input.task : "";
|
|
34652
|
-
decisionReason = typeof input?.role === "string" ? `delegated to ${input.role}` : typeof input?.name === "string" ? `delegated to ${input.name}` : void 0;
|
|
34653
|
-
} else {
|
|
34654
|
-
content = "Autonomous continue to the next iteration";
|
|
34655
|
-
}
|
|
34656
|
-
if (content.trim()) {
|
|
34657
|
-
void recordPromptJournalEntry({
|
|
34658
|
-
...common,
|
|
34659
|
-
category,
|
|
34660
|
-
content,
|
|
34661
|
-
decisionReason
|
|
34662
|
-
}).catch(() => {
|
|
34663
|
-
});
|
|
34664
|
-
}
|
|
34665
|
-
}
|
|
34666
|
-
return next(payload);
|
|
34667
|
-
}
|
|
34668
|
-
};
|
|
34669
|
-
}
|
|
34670
|
-
|
|
34671
35174
|
// src/wiring/lifecycle-plugins.ts
|
|
34672
35175
|
async function setupLifecycleAndPlugins(deps) {
|
|
34673
35176
|
const {
|
|
@@ -34877,7 +35380,8 @@ async function setupLifecycleAndPlugins(deps) {
|
|
|
34877
35380
|
}
|
|
34878
35381
|
}
|
|
34879
35382
|
registerMcpObservability(healthRegistry, metricsSink, mcpRegistry);
|
|
34880
|
-
const
|
|
35383
|
+
const onSlashNotice = (message) => logger.warn(message);
|
|
35384
|
+
const slashRegistry = new SlashCommandRegistry({ onNotice: onSlashNotice });
|
|
34881
35385
|
const hqPublisherRef = { current: void 0 };
|
|
34882
35386
|
const brainMailbox = getSharedProjectMailbox10(
|
|
34883
35387
|
wpaths.projectDir,
|
|
@@ -35119,6 +35623,7 @@ import {
|
|
|
35119
35623
|
SessionMemoryCurator,
|
|
35120
35624
|
watchProviderConfig
|
|
35121
35625
|
} from "@wrongstack/core/storage";
|
|
35626
|
+
import { readJsonObjectFile as readJsonObjectFile3 } from "@wrongstack/core/utils";
|
|
35122
35627
|
import { createProxyInstantApply } from "@wrongstack/core/wiring/proxy-rewrite";
|
|
35123
35628
|
import { withCatalogCapabilities as withCatalogCapabilities2 } from "@wrongstack/providers";
|
|
35124
35629
|
import { getSageService as getSageService2 } from "@wrongstack/sage";
|
|
@@ -35452,6 +35957,20 @@ function setupProviderRuntime(deps) {
|
|
|
35452
35957
|
{ warn: (msg) => logger.warn(`Config watcher (${layer.path}): ${msg}`) }
|
|
35453
35958
|
)
|
|
35454
35959
|
);
|
|
35960
|
+
watchers.push(
|
|
35961
|
+
watchProviderConfig(
|
|
35962
|
+
wpaths.globalConfig,
|
|
35963
|
+
vault,
|
|
35964
|
+
async () => {
|
|
35965
|
+
const bootstrap = await readJsonObjectFile3(wpaths.globalConfig);
|
|
35966
|
+
const selected = bootstrap["activeProfile"];
|
|
35967
|
+
const current = configStore.get().activeProfile ?? "default";
|
|
35968
|
+
if (typeof selected !== "string" || !selected || selected === current) return;
|
|
35969
|
+
configStore.update({ activeProfile: selected });
|
|
35970
|
+
},
|
|
35971
|
+
{ warn: (msg) => logger.warn(`Config watcher (bootstrap): ${msg}`) }
|
|
35972
|
+
)
|
|
35973
|
+
);
|
|
35455
35974
|
};
|
|
35456
35975
|
const closeWatchers = () => {
|
|
35457
35976
|
for (const w of watchers) w.close();
|
|
@@ -37698,6 +38217,7 @@ async function runInteractive(cliCtx) {
|
|
|
37698
38217
|
goalHost,
|
|
37699
38218
|
coordinatorController,
|
|
37700
38219
|
setYoloMode,
|
|
38220
|
+
setYoloConfirm,
|
|
37701
38221
|
secretInputController,
|
|
37702
38222
|
sddRunRegistry
|
|
37703
38223
|
} = await setupCommandHostState({
|
|
@@ -37755,6 +38275,12 @@ async function runInteractive(cliCtx) {
|
|
|
37755
38275
|
agentMonitor,
|
|
37756
38276
|
onPanelOpen,
|
|
37757
38277
|
configStore,
|
|
38278
|
+
// `/profile switch` mutates the store; adopt the resulting config into the
|
|
38279
|
+
// host's own `config` closure exactly the way setupProviderRuntime's
|
|
38280
|
+
// onConfigUpdate does, so the two copies cannot drift apart.
|
|
38281
|
+
onActiveProfileChange: (nextConfig) => {
|
|
38282
|
+
config = nextConfig;
|
|
38283
|
+
},
|
|
37758
38284
|
secretInputController,
|
|
37759
38285
|
vault,
|
|
37760
38286
|
brain,
|
|
@@ -37776,6 +38302,7 @@ async function runInteractive(cliCtx) {
|
|
|
37776
38302
|
eventWiring,
|
|
37777
38303
|
mcpRegistry,
|
|
37778
38304
|
setYoloMode,
|
|
38305
|
+
setYoloConfirm,
|
|
37779
38306
|
getNextPredict: () => nextPredictEnabled,
|
|
37780
38307
|
setNextPredict: (enabled) => {
|
|
37781
38308
|
nextPredictEnabled = enabled;
|
|
@@ -37979,4 +38506,4 @@ export {
|
|
|
37979
38506
|
CLI_VERSION,
|
|
37980
38507
|
runInteractive
|
|
37981
38508
|
};
|
|
37982
|
-
//# sourceMappingURL=cli-main-
|
|
38509
|
+
//# sourceMappingURL=cli-main-GU3YZLMO.js.map
|