@bike4mind/cli 0.2.25-feat-cli-multi-agentic-workflow.18573 → 0.2.25-fix-anthropic-overloaded-fallback.18535
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/{chunk-TVW4ZESU.js → chunk-EIDW3VBS.js} +4 -51
- package/dist/index.js +194 -911
- package/dist/store-JNTO6SRG.js +7 -0
- package/package.json +6 -6
- package/dist/agents/defaults/test.md +0 -56
- package/dist/store-FU6NDC2W.js +0 -11
package/dist/index.js
CHANGED
|
@@ -10,9 +10,8 @@ import {
|
|
|
10
10
|
ConfigStore
|
|
11
11
|
} from "./chunk-23T2XGSZ.js";
|
|
12
12
|
import {
|
|
13
|
-
selectActiveBackgroundAgents,
|
|
14
13
|
useCliStore
|
|
15
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EIDW3VBS.js";
|
|
16
15
|
import "./chunk-RAI6IM62.js";
|
|
17
16
|
import "./chunk-NJW6ZADK.js";
|
|
18
17
|
import {
|
|
@@ -88,15 +87,14 @@ import {
|
|
|
88
87
|
} from "./chunk-OCYRD7D6.js";
|
|
89
88
|
|
|
90
89
|
// src/index.tsx
|
|
91
|
-
import
|
|
92
|
-
import { render, Box as
|
|
90
|
+
import React19, { useState as useState8, useEffect as useEffect4, useCallback, useRef as useRef3 } from "react";
|
|
91
|
+
import { render, Box as Box18, Text as Text18, useApp, useInput as useInput8 } from "ink";
|
|
93
92
|
import { execSync } from "child_process";
|
|
94
|
-
import { randomBytes as randomBytes5 } from "crypto";
|
|
95
93
|
import { v4 as uuidv411 } from "uuid";
|
|
96
94
|
|
|
97
95
|
// src/components/App.tsx
|
|
98
|
-
import
|
|
99
|
-
import { Box as
|
|
96
|
+
import React13, { useState as useState4 } from "react";
|
|
97
|
+
import { Box as Box12, Text as Text12, Static, useInput as useInput5 } from "ink";
|
|
100
98
|
|
|
101
99
|
// src/components/StatusBar.tsx
|
|
102
100
|
import React from "react";
|
|
@@ -1151,101 +1149,23 @@ var AgentThinking = React7.memo(function AgentThinking2() {
|
|
|
1151
1149
|
return /* @__PURE__ */ React7.createElement(Box6, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React7.createElement(ThoughtStream, { isThinking }));
|
|
1152
1150
|
});
|
|
1153
1151
|
|
|
1154
|
-
// src/components/BackgroundAgentStatus.tsx
|
|
1155
|
-
import React8, { useMemo as useMemo2 } from "react";
|
|
1156
|
-
import { Box as Box7, Text as Text7 } from "ink";
|
|
1157
|
-
import Spinner2 from "ink-spinner";
|
|
1158
|
-
var JobItem = React8.memo(function JobItem2({
|
|
1159
|
-
job,
|
|
1160
|
-
indented = false
|
|
1161
|
-
}) {
|
|
1162
|
-
const elapsed = Math.round((Date.now() - job.startTime) / 1e3);
|
|
1163
|
-
const maxTaskLength = indented ? 50 : 60;
|
|
1164
|
-
const taskPreview = job.task.length > maxTaskLength ? job.task.slice(0, maxTaskLength - 3) + "..." : job.task;
|
|
1165
|
-
const isQueued = job.status === "queued";
|
|
1166
|
-
return /* @__PURE__ */ React8.createElement(Box7, null, indented && /* @__PURE__ */ React8.createElement(Text7, null, " "), isQueued ? /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "\u23F3") : /* @__PURE__ */ React8.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner2, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text7, { color: isQueued ? "yellow" : "blue" }, " ", job.agentName), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " ", "[", job.id, "] ", taskPreview, " ", isQueued ? "(queued)" : `(${elapsed}s)`));
|
|
1167
|
-
});
|
|
1168
|
-
function formatStatusCounts(jobs) {
|
|
1169
|
-
let running = 0;
|
|
1170
|
-
let queued = 0;
|
|
1171
|
-
for (const job of jobs) {
|
|
1172
|
-
if (job.status === "running") running++;
|
|
1173
|
-
else if (job.status === "queued") queued++;
|
|
1174
|
-
}
|
|
1175
|
-
const parts = [];
|
|
1176
|
-
if (running > 0) parts.push(`${running} running`);
|
|
1177
|
-
if (queued > 0) parts.push(`${queued} queued`);
|
|
1178
|
-
return parts.join(", ");
|
|
1179
|
-
}
|
|
1180
|
-
function groupJobsByTurn(jobs) {
|
|
1181
|
-
const groups = /* @__PURE__ */ new Map();
|
|
1182
|
-
const ungrouped = [];
|
|
1183
|
-
for (const job of jobs) {
|
|
1184
|
-
if (job.turnId) {
|
|
1185
|
-
const existing = groups.get(job.turnId);
|
|
1186
|
-
if (existing) {
|
|
1187
|
-
existing.jobs.push(job);
|
|
1188
|
-
if (!existing.description && job.groupDescription) {
|
|
1189
|
-
existing.description = job.groupDescription;
|
|
1190
|
-
}
|
|
1191
|
-
} else {
|
|
1192
|
-
groups.set(job.turnId, {
|
|
1193
|
-
description: job.groupDescription,
|
|
1194
|
-
jobs: [job]
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
} else {
|
|
1198
|
-
ungrouped.push(job);
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
return { groups, ungrouped };
|
|
1202
|
-
}
|
|
1203
|
-
function BackgroundAgentStatus() {
|
|
1204
|
-
const activeJobs = useCliStore(selectActiveBackgroundAgents);
|
|
1205
|
-
const permissionPrompt = useCliStore((state) => state.permissionPrompt);
|
|
1206
|
-
const { groups, ungrouped } = useMemo2(() => groupJobsByTurn(activeJobs), [activeJobs]);
|
|
1207
|
-
if (activeJobs.length === 0) return null;
|
|
1208
|
-
if (permissionPrompt) {
|
|
1209
|
-
return /* @__PURE__ */ React8.createElement(Box7, { paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Background agents: ", formatStatusCounts(activeJobs)));
|
|
1210
|
-
}
|
|
1211
|
-
return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", paddingX: 1, marginBottom: 0 }, Array.from(groups.entries()).map(([turnId, group]) => /* @__PURE__ */ React8.createElement(Box7, { key: turnId, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { color: "magenta" }, "\u25B8 "), /* @__PURE__ */ React8.createElement(Text7, { color: "magenta", bold: true }, group.description || "Background Tasks"), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " (", formatStatusCounts(group.jobs), ")")), group.jobs.map((job) => /* @__PURE__ */ React8.createElement(JobItem, { key: job.id, job, indented: true })))), ungrouped.map((job) => /* @__PURE__ */ React8.createElement(JobItem, { key: job.id, job })));
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
// src/components/CompletedGroupNotification.tsx
|
|
1215
|
-
import React9, { useEffect as useEffect3 } from "react";
|
|
1216
|
-
import { Box as Box8, Text as Text8 } from "ink";
|
|
1217
|
-
var NOTIFICATION_DISPLAY_DURATION_MS = 3e3;
|
|
1218
|
-
function CompletedGroupNotification() {
|
|
1219
|
-
const notifications = useCliStore((state) => state.completedGroupNotifications);
|
|
1220
|
-
const clearNotifications = useCliStore((state) => state.clearCompletedGroupNotifications);
|
|
1221
|
-
useEffect3(() => {
|
|
1222
|
-
if (notifications.length > 0) {
|
|
1223
|
-
const timer = setTimeout(() => {
|
|
1224
|
-
clearNotifications();
|
|
1225
|
-
}, NOTIFICATION_DISPLAY_DURATION_MS);
|
|
1226
|
-
return () => clearTimeout(timer);
|
|
1227
|
-
}
|
|
1228
|
-
}, [notifications.length, clearNotifications]);
|
|
1229
|
-
if (notifications.length === 0) return null;
|
|
1230
|
-
return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", paddingX: 1, marginBottom: 1 }, notifications.map((item, index) => /* @__PURE__ */ React9.createElement(Box8, { key: `${item.timestamp}-${index}`, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, "\u2714", " "), /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, item.groupDescription ? `Background tasks completed: "${item.groupDescription}"` : "Background tasks completed")), /* @__PURE__ */ React9.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, italic: true }, "Results will be incorporated in the next response.")))));
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
1152
|
// src/components/PermissionPrompt.tsx
|
|
1234
|
-
import
|
|
1235
|
-
import { Box as
|
|
1153
|
+
import React8 from "react";
|
|
1154
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
1155
|
+
import SelectInput from "ink-select-input";
|
|
1236
1156
|
function renderDiffPreview(preview) {
|
|
1237
1157
|
const lines = preview.split("\n");
|
|
1238
1158
|
return lines.map((line, index) => {
|
|
1239
1159
|
if (line.startsWith("+")) {
|
|
1240
|
-
return /* @__PURE__ */
|
|
1160
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "green" }, line);
|
|
1241
1161
|
}
|
|
1242
1162
|
if (line.startsWith("-")) {
|
|
1243
|
-
return /* @__PURE__ */
|
|
1163
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "red" }, line);
|
|
1244
1164
|
}
|
|
1245
1165
|
if (line.startsWith("@@")) {
|
|
1246
|
-
return /* @__PURE__ */
|
|
1166
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, color: "cyan" }, line);
|
|
1247
1167
|
}
|
|
1248
|
-
return /* @__PURE__ */
|
|
1168
|
+
return /* @__PURE__ */ React8.createElement(Text7, { key: index, dimColor: true }, line);
|
|
1249
1169
|
});
|
|
1250
1170
|
}
|
|
1251
1171
|
function PermissionPrompt({
|
|
@@ -1264,36 +1184,16 @@ function PermissionPrompt({
|
|
|
1264
1184
|
{ label: "\u2713 Allow once", value: "allow-once" },
|
|
1265
1185
|
{ label: "\u2717 Deny", value: "deny" }
|
|
1266
1186
|
];
|
|
1267
|
-
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1268
|
-
const [responded, setResponded] = useState3(false);
|
|
1269
|
-
const handleSelect = useCallback(() => {
|
|
1270
|
-
if (responded) return;
|
|
1271
|
-
setResponded(true);
|
|
1272
|
-
onResponse(items[selectedIndex].value);
|
|
1273
|
-
}, [responded, onResponse, items, selectedIndex]);
|
|
1274
|
-
useInput3(
|
|
1275
|
-
(_input, key) => {
|
|
1276
|
-
if (responded) return;
|
|
1277
|
-
if (key.upArrow) {
|
|
1278
|
-
setSelectedIndex((i) => i > 0 ? i - 1 : items.length - 1);
|
|
1279
|
-
} else if (key.downArrow) {
|
|
1280
|
-
setSelectedIndex((i) => i < items.length - 1 ? i + 1 : 0);
|
|
1281
|
-
} else if (key.return) {
|
|
1282
|
-
handleSelect();
|
|
1283
|
-
}
|
|
1284
|
-
},
|
|
1285
|
-
{ isActive: !responded }
|
|
1286
|
-
);
|
|
1287
1187
|
const MAX_ARGS_LENGTH = 500;
|
|
1288
1188
|
const rawArgsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
1289
1189
|
const argsString = rawArgsString.length > MAX_ARGS_LENGTH ? rawArgsString.slice(0, MAX_ARGS_LENGTH) + `
|
|
1290
1190
|
... (${rawArgsString.length - MAX_ARGS_LENGTH} more chars)` : rawArgsString;
|
|
1291
|
-
return /* @__PURE__ */
|
|
1191
|
+
return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Tool: "), /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "Action: "), /* @__PURE__ */ React8.createElement(Text7, null, toolDescription)), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Arguments:"), /* @__PURE__ */ React8.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text7, { bold: true }, "Preview:"), /* @__PURE__ */ React8.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
|
|
1292
1192
|
}
|
|
1293
1193
|
|
|
1294
1194
|
// src/components/ConfigEditor.tsx
|
|
1295
|
-
import
|
|
1296
|
-
import { Box as
|
|
1195
|
+
import React9, { useState as useState3, useMemo as useMemo2 } from "react";
|
|
1196
|
+
import { Box as Box8, Text as Text8, useInput as useInput3 } from "ink";
|
|
1297
1197
|
var MAX_ITERATIONS_OPTIONS = [
|
|
1298
1198
|
{ label: "10", value: 10 },
|
|
1299
1199
|
{ label: "20", value: 20 },
|
|
@@ -1437,12 +1337,12 @@ function buildConfigItems(availableModels) {
|
|
|
1437
1337
|
return items;
|
|
1438
1338
|
}
|
|
1439
1339
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
1440
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1441
|
-
const [editedConfig, setEditedConfig] =
|
|
1442
|
-
const [saveError, setSaveError] =
|
|
1443
|
-
const [isSaving, setIsSaving] =
|
|
1444
|
-
const configItems =
|
|
1445
|
-
const hasChanges =
|
|
1340
|
+
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
1341
|
+
const [editedConfig, setEditedConfig] = useState3(config);
|
|
1342
|
+
const [saveError, setSaveError] = useState3(null);
|
|
1343
|
+
const [isSaving, setIsSaving] = useState3(false);
|
|
1344
|
+
const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
|
|
1345
|
+
const hasChanges = useMemo2(() => {
|
|
1446
1346
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
1447
1347
|
}, [config.preferences, editedConfig.preferences, config.defaultModel, editedConfig.defaultModel]);
|
|
1448
1348
|
const handleSaveAndClose = async () => {
|
|
@@ -1460,7 +1360,7 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1460
1360
|
}
|
|
1461
1361
|
onClose();
|
|
1462
1362
|
};
|
|
1463
|
-
|
|
1363
|
+
useInput3((input, key) => {
|
|
1464
1364
|
const currentItem = configItems[selectedIndex];
|
|
1465
1365
|
if (key.upArrow) {
|
|
1466
1366
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
|
|
@@ -1525,33 +1425,33 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1525
1425
|
}
|
|
1526
1426
|
return String(value);
|
|
1527
1427
|
};
|
|
1528
|
-
return /* @__PURE__ */
|
|
1428
|
+
return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React9.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
|
|
1529
1429
|
const isSelected = index === selectedIndex;
|
|
1530
1430
|
const value = item.getValue(editedConfig);
|
|
1531
1431
|
const displayValue = formatValue(item, value);
|
|
1532
1432
|
const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
|
|
1533
|
-
return /* @__PURE__ */
|
|
1534
|
-
})), /* @__PURE__ */
|
|
1433
|
+
return /* @__PURE__ */ React9.createElement(Box8, { key: item.key }, /* @__PURE__ */ React9.createElement(Text8, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React9.createElement(Box8, { width: 18 }, /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React9.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "< "), /* @__PURE__ */ React9.createElement(Text8, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " >")));
|
|
1434
|
+
})), /* @__PURE__ */ React9.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
|
|
1535
1435
|
}
|
|
1536
1436
|
|
|
1537
1437
|
// src/components/McpViewer.tsx
|
|
1538
|
-
import
|
|
1539
|
-
import { Box as
|
|
1438
|
+
import React10 from "react";
|
|
1439
|
+
import { Box as Box9, Text as Text9, useInput as useInput4 } from "ink";
|
|
1540
1440
|
function McpViewer({ config, mcpManager, onClose }) {
|
|
1541
|
-
|
|
1441
|
+
useInput4((input, key) => {
|
|
1542
1442
|
if (key.escape || input === "q") {
|
|
1543
1443
|
onClose();
|
|
1544
1444
|
}
|
|
1545
1445
|
});
|
|
1546
1446
|
if (config.mcpServers.length === 0) {
|
|
1547
|
-
return /* @__PURE__ */
|
|
1447
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React10.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, "\u{1F4E1} MCP Server Status")), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No MCP servers configured."), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "To add MCP servers, edit your config file:")), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " Global: ~/.bike4mind/config.json"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " Project: .bike4mind/config.json"), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, italic: true }, "Press Esc or q to close")));
|
|
1548
1448
|
}
|
|
1549
1449
|
const enabledServers = config.mcpServers.filter((s) => s.enabled);
|
|
1550
|
-
return /* @__PURE__ */
|
|
1450
|
+
return /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", paddingX: 2, paddingY: 1 }, /* @__PURE__ */ React10.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "cyan" }, "\u{1F4E1} MCP Server Status")), /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 2 }, /* @__PURE__ */ React10.createElement(Text9, { bold: true, dimColor: true }, "Configured Servers:"), config.mcpServers.map((server) => {
|
|
1551
1451
|
const status = server.enabled ? "\u2705 Enabled" : "\u23F8\uFE0F Disabled";
|
|
1552
1452
|
const commandInfo = server.command ? `${server.command} ${(server.args || []).join(" ")}` : "(internal)";
|
|
1553
|
-
return /* @__PURE__ */
|
|
1554
|
-
})), enabledServers.length > 0 && mcpManager && /* @__PURE__ */
|
|
1453
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: server.name, flexDirection: "column", marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, null, "\u2022 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, server.name), " - ", /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, status)), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " Command: ", commandInfo), Object.keys(server.env).length > 0 && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " Env vars: ", Object.keys(server.env).join(", ")));
|
|
1454
|
+
})), enabledServers.length > 0 && mcpManager && /* @__PURE__ */ React10.createElement(Box9, { flexDirection: "column", marginBottom: 2 }, /* @__PURE__ */ React10.createElement(Text9, { bold: true, dimColor: true }, "Connection Status:"), enabledServers.map((server) => {
|
|
1555
1455
|
const state = mcpManager.getConnectionState(server.name);
|
|
1556
1456
|
let icon;
|
|
1557
1457
|
let statusText;
|
|
@@ -1581,30 +1481,30 @@ function McpViewer({ config, mcpManager, onClose }) {
|
|
|
1581
1481
|
const serverTools = toolCounts.find((t) => t.serverName === server.name);
|
|
1582
1482
|
const toolCount = serverTools?.count || 0;
|
|
1583
1483
|
const toolInfo = state === "connected" && toolCount > 0 ? ` (${toolCount} tool${toolCount === 1 ? "" : "s"})` : "";
|
|
1584
|
-
return /* @__PURE__ */
|
|
1585
|
-
}), /* @__PURE__ */
|
|
1484
|
+
return /* @__PURE__ */ React10.createElement(Box9, { key: server.name, marginTop: 1, marginLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, null, "\u2022 ", /* @__PURE__ */ React10.createElement(Text9, { bold: true }, server.name), " \xB7 ", /* @__PURE__ */ React10.createElement(Text9, { color }, icon), " ", /* @__PURE__ */ React10.createElement(Text9, { color }, statusText), toolInfo && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, toolInfo)));
|
|
1485
|
+
}), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1, marginLeft: 2 }, (() => {
|
|
1586
1486
|
const toolCounts = mcpManager.getToolCount();
|
|
1587
1487
|
const totalTools = toolCounts.reduce((sum2, s) => sum2 + s.count, 0);
|
|
1588
1488
|
if (totalTools > 0) {
|
|
1589
|
-
return /* @__PURE__ */
|
|
1489
|
+
return /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: "green" }, "Total: ", totalTools, " MCP tool", totalTools === 1 ? "" : "s", " available");
|
|
1590
1490
|
} else {
|
|
1591
|
-
return /* @__PURE__ */
|
|
1491
|
+
return /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No MCP tools available yet (servers still connecting)");
|
|
1592
1492
|
}
|
|
1593
|
-
})())), enabledServers.length === 0 && /* @__PURE__ */
|
|
1493
|
+
})())), enabledServers.length === 0 && /* @__PURE__ */ React10.createElement(Box9, { marginBottom: 2 }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F No MCP servers enabled"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " Use `b4m mcp enable ", "<name>", "` to enable a server")), /* @__PURE__ */ React10.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, italic: true }, "Press Esc or q to close")));
|
|
1594
1494
|
}
|
|
1595
1495
|
|
|
1596
1496
|
// src/components/MessageItem.tsx
|
|
1597
|
-
import
|
|
1598
|
-
import { Box as
|
|
1497
|
+
import React12 from "react";
|
|
1498
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1599
1499
|
|
|
1600
1500
|
// src/components/MarkdownRenderer.tsx
|
|
1601
|
-
import
|
|
1602
|
-
import { Box as
|
|
1501
|
+
import React11 from "react";
|
|
1502
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1603
1503
|
import { marked } from "marked";
|
|
1604
1504
|
import { highlight } from "cli-highlight";
|
|
1605
1505
|
function MarkdownRenderer({ content }) {
|
|
1606
1506
|
const tokens = marked.lexer(content);
|
|
1607
|
-
return /* @__PURE__ */
|
|
1507
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, tokens.map((token, idx) => renderToken(token, idx)));
|
|
1608
1508
|
}
|
|
1609
1509
|
function renderToken(token, idx) {
|
|
1610
1510
|
switch (token.type) {
|
|
@@ -1619,12 +1519,12 @@ function renderToken(token, idx) {
|
|
|
1619
1519
|
case "blockquote":
|
|
1620
1520
|
return renderBlockquote(token, idx);
|
|
1621
1521
|
case "hr":
|
|
1622
|
-
return /* @__PURE__ */
|
|
1522
|
+
return /* @__PURE__ */ React11.createElement(Text10, { key: idx, dimColor: true }, "\u2500".repeat(50));
|
|
1623
1523
|
case "space":
|
|
1624
1524
|
return null;
|
|
1625
1525
|
default:
|
|
1626
1526
|
if ("text" in token) {
|
|
1627
|
-
return /* @__PURE__ */
|
|
1527
|
+
return /* @__PURE__ */ React11.createElement(Text10, { key: idx }, token.text);
|
|
1628
1528
|
}
|
|
1629
1529
|
return null;
|
|
1630
1530
|
}
|
|
@@ -1639,7 +1539,7 @@ function renderHeading(token, idx) {
|
|
|
1639
1539
|
6: "white"
|
|
1640
1540
|
};
|
|
1641
1541
|
const color = colors[token.depth] || "white";
|
|
1642
|
-
return /* @__PURE__ */
|
|
1542
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, marginTop: idx > 0 ? 1 : 0 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color }, parseInlineText(token.text)));
|
|
1643
1543
|
}
|
|
1644
1544
|
function renderCodeBlock(token, idx) {
|
|
1645
1545
|
let highlightedCode;
|
|
@@ -1651,20 +1551,20 @@ function renderCodeBlock(token, idx) {
|
|
|
1651
1551
|
} catch (error) {
|
|
1652
1552
|
highlightedCode = token.text;
|
|
1653
1553
|
}
|
|
1654
|
-
return /* @__PURE__ */
|
|
1554
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, flexDirection: "column", paddingLeft: 2 }, token.lang && /* @__PURE__ */ React11.createElement(Text10, { dimColor: true, color: "gray" }, token.lang), /* @__PURE__ */ React11.createElement(Text10, null, highlightedCode));
|
|
1655
1555
|
}
|
|
1656
1556
|
function renderParagraph(token, idx) {
|
|
1657
|
-
return /* @__PURE__ */
|
|
1557
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx }, /* @__PURE__ */ React11.createElement(Text10, null, parseInlineText(token.text)));
|
|
1658
1558
|
}
|
|
1659
1559
|
function renderList(token, idx) {
|
|
1660
|
-
return /* @__PURE__ */
|
|
1560
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, flexDirection: "column" }, token.items.map((item, itemIdx) => renderListItem(item, itemIdx, token.ordered, itemIdx + 1)));
|
|
1661
1561
|
}
|
|
1662
1562
|
function renderListItem(item, idx, ordered, number) {
|
|
1663
1563
|
const bullet = ordered ? `${number}.` : "\u2022";
|
|
1664
|
-
return /* @__PURE__ */
|
|
1564
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text10, null, bullet, " ", parseInlineText(item.text)));
|
|
1665
1565
|
}
|
|
1666
1566
|
function renderBlockquote(token, idx) {
|
|
1667
|
-
return /* @__PURE__ */
|
|
1567
|
+
return /* @__PURE__ */ React11.createElement(Box10, { key: idx, paddingLeft: 2, borderStyle: "single", borderLeft: true, borderColor: "gray" }, token.tokens.map((t, i) => renderToken(t, i)));
|
|
1668
1568
|
}
|
|
1669
1569
|
function parseInlineText(text) {
|
|
1670
1570
|
const parts = [];
|
|
@@ -1677,15 +1577,15 @@ function parseInlineText(text) {
|
|
|
1677
1577
|
}
|
|
1678
1578
|
if (match[1]) {
|
|
1679
1579
|
parts.push(
|
|
1680
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ React11.createElement(Text10, { key: match.index, bold: true }, match[2])
|
|
1681
1581
|
);
|
|
1682
1582
|
} else if (match[3]) {
|
|
1683
1583
|
parts.push(
|
|
1684
|
-
/* @__PURE__ */
|
|
1584
|
+
/* @__PURE__ */ React11.createElement(Text10, { key: match.index, italic: true }, match[4])
|
|
1685
1585
|
);
|
|
1686
1586
|
} else if (match[5]) {
|
|
1687
1587
|
parts.push(
|
|
1688
|
-
/* @__PURE__ */
|
|
1588
|
+
/* @__PURE__ */ React11.createElement(Text10, { key: match.index, color: "cyan" }, match[6])
|
|
1689
1589
|
);
|
|
1690
1590
|
}
|
|
1691
1591
|
lastIndex = regex.lastIndex;
|
|
@@ -1696,45 +1596,27 @@ function parseInlineText(text) {
|
|
|
1696
1596
|
if (parts.length === 0) {
|
|
1697
1597
|
return text;
|
|
1698
1598
|
}
|
|
1699
|
-
return /* @__PURE__ */
|
|
1599
|
+
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, parts);
|
|
1700
1600
|
}
|
|
1701
1601
|
|
|
1702
1602
|
// src/components/MessageItem.tsx
|
|
1703
|
-
function
|
|
1704
|
-
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
1705
|
-
if (str.length <= maxLength) {
|
|
1706
|
-
return str;
|
|
1707
|
-
}
|
|
1708
|
-
return str.slice(0, maxLength) + "...";
|
|
1709
|
-
}
|
|
1710
|
-
function getRoleDisplay(role) {
|
|
1711
|
-
switch (role) {
|
|
1712
|
-
case "user":
|
|
1713
|
-
return { color: "cyan", label: "\u{1F464} You" };
|
|
1714
|
-
case "system":
|
|
1715
|
-
return { color: "gray", label: "\u2139\uFE0F System" };
|
|
1716
|
-
default:
|
|
1717
|
-
return { color: "green", label: "\u{1F916} Assistant" };
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
var MessageItem = React14.memo(function MessageItem2({ message }) {
|
|
1603
|
+
function MessageItem({ message }) {
|
|
1721
1604
|
const isUser = message.role === "user";
|
|
1722
|
-
const
|
|
1723
|
-
|
|
1724
|
-
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", marginBottom: 1 }, !isContinuation && /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { bold: true, color: roleDisplay.color }, roleDisplay.label), isUser && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1605
|
+
const isSystem = message.role === "system";
|
|
1606
|
+
return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: isUser ? "cyan" : isSystem ? "gray" : "green" }, isUser ? "\u{1F464} You" : isSystem ? "\u2139\uFE0F System" : "\u{1F916} Assistant"), isUser && /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), isUser && message.content && /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { backgroundColor: "whiteBright", color: "black" }, " ", message.content, " ")), !isUser && message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1725
1607
|
if (step.type === "thought") {
|
|
1726
|
-
return /* @__PURE__ */
|
|
1608
|
+
return /* @__PURE__ */ React12.createElement(Box11, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, ` ${step.content}`));
|
|
1727
1609
|
}
|
|
1728
1610
|
if (step.type === "action") {
|
|
1729
1611
|
const toolName = step.metadata?.toolName || "unknown";
|
|
1730
1612
|
const toolInput = step.metadata?.toolInput;
|
|
1731
|
-
const observationStep = message.metadata
|
|
1613
|
+
const observationStep = message.metadata.steps[idx + 1];
|
|
1732
1614
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1733
|
-
return /* @__PURE__ */
|
|
1615
|
+
return /* @__PURE__ */ React12.createElement(Box11, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1734
1616
|
}
|
|
1735
1617
|
return null;
|
|
1736
|
-
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */
|
|
1737
|
-
}
|
|
1618
|
+
}).filter(Boolean)), !isUser && message.content !== "..." && /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2, marginTop: message.metadata?.steps?.length ? 1 : 0 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React12.createElement(MarkdownRenderer, { content: message.content })), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React12.createElement(Box11, { paddingLeft: 2 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1619
|
+
}
|
|
1738
1620
|
|
|
1739
1621
|
// src/utils/processFileReferences.ts
|
|
1740
1622
|
import * as fs2 from "fs";
|
|
@@ -1853,7 +1735,6 @@ function hasFileReferences(message) {
|
|
|
1853
1735
|
// src/components/App.tsx
|
|
1854
1736
|
function App({
|
|
1855
1737
|
onMessage,
|
|
1856
|
-
onBackgroundCompletion,
|
|
1857
1738
|
onCommand,
|
|
1858
1739
|
onBashCommand,
|
|
1859
1740
|
onPermissionResponse,
|
|
@@ -1880,22 +1761,14 @@ function App({
|
|
|
1880
1761
|
const setShowMcpViewer = useCliStore((state) => state.setShowMcpViewer);
|
|
1881
1762
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1882
1763
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1883
|
-
const pendingBackgroundTrigger = useCliStore((state) => state.pendingBackgroundTrigger);
|
|
1884
|
-
const setPendingBackgroundTrigger = useCliStore((state) => state.setPendingBackgroundTrigger);
|
|
1885
|
-
useEffect4(() => {
|
|
1886
|
-
if (pendingBackgroundTrigger && !isThinking && onBackgroundCompletion) {
|
|
1887
|
-
setPendingBackgroundTrigger(false);
|
|
1888
|
-
onBackgroundCompletion();
|
|
1889
|
-
}
|
|
1890
|
-
}, [pendingBackgroundTrigger, isThinking, setPendingBackgroundTrigger, onBackgroundCompletion]);
|
|
1891
1764
|
const toggleAutoAcceptEdits = useCliStore((state) => state.toggleAutoAcceptEdits);
|
|
1892
|
-
|
|
1765
|
+
useInput5((_input, key) => {
|
|
1893
1766
|
if (key.tab && key.shift) {
|
|
1894
1767
|
toggleAutoAcceptEdits();
|
|
1895
1768
|
}
|
|
1896
1769
|
});
|
|
1897
|
-
const [isBashMode, setIsBashMode] =
|
|
1898
|
-
const handleSubmit =
|
|
1770
|
+
const [isBashMode, setIsBashMode] = useState4(false);
|
|
1771
|
+
const handleSubmit = React13.useCallback(
|
|
1899
1772
|
async (input) => {
|
|
1900
1773
|
const trimmed = input.trim();
|
|
1901
1774
|
if (!trimmed) return;
|
|
@@ -1924,7 +1797,7 @@ ${errorBlock}`;
|
|
|
1924
1797
|
},
|
|
1925
1798
|
[onMessage, onCommand, setIsThinking]
|
|
1926
1799
|
);
|
|
1927
|
-
return /* @__PURE__ */
|
|
1800
|
+
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
|
|
1928
1801
|
ConfigEditor,
|
|
1929
1802
|
{
|
|
1930
1803
|
config,
|
|
@@ -1934,8 +1807,8 @@ ${errorBlock}`;
|
|
|
1934
1807
|
}
|
|
1935
1808
|
)) : showMcpViewer && config ? (
|
|
1936
1809
|
/* MCP Viewer - full screen takeover */
|
|
1937
|
-
/* @__PURE__ */
|
|
1938
|
-
) : /* @__PURE__ */
|
|
1810
|
+
/* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(McpViewer, { config, mcpManager, onClose: () => setShowMcpViewer(false) }))
|
|
1811
|
+
) : /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(Static, { items: messages }, (message) => /* @__PURE__ */ React13.createElement(Box12, { key: message.id, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(MessageItem, { message }))), /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, pendingMessages.map((message) => /* @__PURE__ */ React13.createElement(Box12, { key: message.id, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(MessageItem, { message })))), permissionPrompt && /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
|
|
1939
1812
|
PermissionPrompt,
|
|
1940
1813
|
{
|
|
1941
1814
|
toolName: permissionPrompt.toolName,
|
|
@@ -1944,7 +1817,7 @@ ${errorBlock}`;
|
|
|
1944
1817
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1945
1818
|
onResponse: onPermissionResponse
|
|
1946
1819
|
}
|
|
1947
|
-
)), !permissionPrompt && /* @__PURE__ */
|
|
1820
|
+
)), !permissionPrompt && /* @__PURE__ */ React13.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React13.createElement(Box12, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React13.createElement(Box12, { borderStyle: "single", borderColor: isBashMode ? "yellow" : "cyan", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
|
|
1948
1821
|
InputPrompt,
|
|
1949
1822
|
{
|
|
1950
1823
|
onSubmit: handleSubmit,
|
|
@@ -1957,24 +1830,24 @@ ${errorBlock}`;
|
|
|
1957
1830
|
onPrefillConsumed,
|
|
1958
1831
|
onBashModeChange: setIsBashMode
|
|
1959
1832
|
}
|
|
1960
|
-
)), /* @__PURE__ */
|
|
1833
|
+
)), /* @__PURE__ */ React13.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens })));
|
|
1961
1834
|
}
|
|
1962
1835
|
|
|
1963
1836
|
// src/components/MessageList.tsx
|
|
1964
|
-
import
|
|
1965
|
-
import { Box as
|
|
1837
|
+
import React14 from "react";
|
|
1838
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
1966
1839
|
function stripThinkingTags(content) {
|
|
1967
1840
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1968
1841
|
}
|
|
1969
|
-
var MessageList =
|
|
1842
|
+
var MessageList = React14.memo(
|
|
1970
1843
|
function MessageList2({ messages }) {
|
|
1971
1844
|
if (messages.length === 0) {
|
|
1972
|
-
return /* @__PURE__ */
|
|
1845
|
+
return /* @__PURE__ */ React14.createElement(Box13, { paddingY: 1 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1973
1846
|
}
|
|
1974
|
-
return /* @__PURE__ */
|
|
1847
|
+
return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React14.createElement(Box13, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React14.createElement(Text13, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " tools used,", " ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1975
1848
|
if (step.type === "thought") {
|
|
1976
|
-
return /* @__PURE__ */
|
|
1977
|
-
|
|
1849
|
+
return /* @__PURE__ */ React14.createElement(Box13, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React14.createElement(
|
|
1850
|
+
Text13,
|
|
1978
1851
|
{
|
|
1979
1852
|
dimColor: true
|
|
1980
1853
|
},
|
|
@@ -1986,10 +1859,10 @@ var MessageList = React16.memo(
|
|
|
1986
1859
|
const toolInput = step.metadata?.toolInput;
|
|
1987
1860
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1988
1861
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1989
|
-
return /* @__PURE__ */
|
|
1862
|
+
return /* @__PURE__ */ React14.createElement(Box13, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1990
1863
|
}
|
|
1991
1864
|
return null;
|
|
1992
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1865
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
|
|
1993
1866
|
},
|
|
1994
1867
|
(prevProps, nextProps) => {
|
|
1995
1868
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -2000,9 +1873,9 @@ var MessageList = React16.memo(
|
|
|
2000
1873
|
);
|
|
2001
1874
|
|
|
2002
1875
|
// src/components/TrustLocationSelector.tsx
|
|
2003
|
-
import
|
|
2004
|
-
import { Box as
|
|
2005
|
-
import
|
|
1876
|
+
import React15 from "react";
|
|
1877
|
+
import { Box as Box14, Text as Text14 } from "ink";
|
|
1878
|
+
import SelectInput2 from "ink-select-input";
|
|
2006
1879
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
2007
1880
|
const items = [];
|
|
2008
1881
|
if (inProject) {
|
|
@@ -2022,24 +1895,24 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
2022
1895
|
const handleSelect = (item) => {
|
|
2023
1896
|
onSelect(item.value);
|
|
2024
1897
|
};
|
|
2025
|
-
return /* @__PURE__ */
|
|
2026
|
-
|
|
1898
|
+
return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React15.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text14, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React15.createElement(
|
|
1899
|
+
SelectInput2,
|
|
2027
1900
|
{
|
|
2028
1901
|
items,
|
|
2029
1902
|
onSelect: handleSelect,
|
|
2030
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1903
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
2031
1904
|
}
|
|
2032
|
-
), /* @__PURE__ */
|
|
1905
|
+
), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
|
|
2033
1906
|
}
|
|
2034
1907
|
|
|
2035
1908
|
// src/components/RewindSelector.tsx
|
|
2036
|
-
import
|
|
2037
|
-
import { Box as
|
|
2038
|
-
import
|
|
1909
|
+
import React16, { useState as useState5 } from "react";
|
|
1910
|
+
import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
|
|
1911
|
+
import SelectInput3 from "ink-select-input";
|
|
2039
1912
|
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
2040
|
-
const [step, setStep] =
|
|
2041
|
-
const [selectedMessageIndex, setSelectedMessageIndex] =
|
|
2042
|
-
|
|
1913
|
+
const [step, setStep] = useState5("selection");
|
|
1914
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState5(null);
|
|
1915
|
+
useInput6((input, key) => {
|
|
2043
1916
|
if (key.escape) {
|
|
2044
1917
|
if (step === "confirmation") {
|
|
2045
1918
|
setStep("selection");
|
|
@@ -2076,14 +1949,14 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
2076
1949
|
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
2077
1950
|
}, 0);
|
|
2078
1951
|
if (step === "selection") {
|
|
2079
|
-
return /* @__PURE__ */
|
|
2080
|
-
|
|
1952
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text15, { bold: true }, "Select a point to rewind to:")), /* @__PURE__ */ React16.createElement(
|
|
1953
|
+
SelectInput3,
|
|
2081
1954
|
{
|
|
2082
1955
|
items,
|
|
2083
1956
|
onSelect: handleSelectionSelect,
|
|
2084
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1957
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
2085
1958
|
}
|
|
2086
|
-
), /* @__PURE__ */
|
|
1959
|
+
), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
2087
1960
|
}
|
|
2088
1961
|
const confirmationItems = [
|
|
2089
1962
|
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
@@ -2091,24 +1964,24 @@ function RewindSelector({ messages, onSelect, onCancel }) {
|
|
|
2091
1964
|
];
|
|
2092
1965
|
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
2093
1966
|
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
2094
|
-
return /* @__PURE__ */
|
|
2095
|
-
|
|
1967
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React16.createElement(Box15, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React16.createElement(Text15, { bold: true }, "Confirm rewind operation:"), /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "Message: ", selectedPreview), /* @__PURE__ */ React16.createElement(Text15, { color: "yellow" }, "This will remove ", messagesToRemove.length, " message(s) (", tokensToRemove.toLocaleString(), " tokens)"), /* @__PURE__ */ React16.createElement(Text15, { color: "cyan" }, "The selected message will be placed in your input for editing.")), /* @__PURE__ */ React16.createElement(
|
|
1968
|
+
SelectInput3,
|
|
2096
1969
|
{
|
|
2097
1970
|
items: confirmationItems,
|
|
2098
1971
|
onSelect: handleConfirmationSelect,
|
|
2099
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1972
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
2100
1973
|
}
|
|
2101
|
-
), /* @__PURE__ */
|
|
1974
|
+
), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
2102
1975
|
}
|
|
2103
1976
|
|
|
2104
1977
|
// src/components/SessionSelector.tsx
|
|
2105
|
-
import
|
|
2106
|
-
import { Box as
|
|
2107
|
-
import
|
|
1978
|
+
import React17, { useState as useState6 } from "react";
|
|
1979
|
+
import { Box as Box16, Text as Text16, useInput as useInput7 } from "ink";
|
|
1980
|
+
import SelectInput4 from "ink-select-input";
|
|
2108
1981
|
function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
2109
|
-
const [step, setStep] =
|
|
2110
|
-
const [selectedSession, setSelectedSession] =
|
|
2111
|
-
|
|
1982
|
+
const [step, setStep] = useState6("selection");
|
|
1983
|
+
const [selectedSession, setSelectedSession] = useState6(null);
|
|
1984
|
+
useInput7((_input, key) => {
|
|
2112
1985
|
if (key.escape) {
|
|
2113
1986
|
if (step === "confirmation") {
|
|
2114
1987
|
setStep("selection");
|
|
@@ -2158,33 +2031,33 @@ function SessionSelector({ sessions, currentSession, onSelect, onCancel }) {
|
|
|
2158
2031
|
}
|
|
2159
2032
|
};
|
|
2160
2033
|
if (step === "selection") {
|
|
2161
|
-
return /* @__PURE__ */
|
|
2162
|
-
|
|
2034
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React17.createElement(Box16, { marginBottom: 1 }, /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "Select a session to resume:")), /* @__PURE__ */ React17.createElement(
|
|
2035
|
+
SelectInput4,
|
|
2163
2036
|
{
|
|
2164
2037
|
items,
|
|
2165
2038
|
onSelect: handleSelectionSelect,
|
|
2166
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
2039
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React17.createElement(Box16, null, /* @__PURE__ */ React17.createElement(Text16, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
2167
2040
|
}
|
|
2168
|
-
), /* @__PURE__ */
|
|
2041
|
+
), /* @__PURE__ */ React17.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
2169
2042
|
}
|
|
2170
2043
|
const confirmationItems = [
|
|
2171
2044
|
{ label: "Yes, resume this session", value: "confirm" },
|
|
2172
2045
|
{ label: "No, go back to selection", value: "cancel" }
|
|
2173
2046
|
];
|
|
2174
|
-
return /* @__PURE__ */
|
|
2175
|
-
|
|
2047
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React17.createElement(Box16, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React17.createElement(Text16, { bold: true }, 'Resume session: "', selectedSession?.name, '"'), /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, selectedSession?.messages.length, " messages | ", selectedSession?.model, " |", " ", selectedSession?.metadata?.totalTokens?.toLocaleString() ?? 0, " tokens"), hasUnsavedWork && /* @__PURE__ */ React17.createElement(Text16, { color: "yellow" }, "Warning: Your current session has unsaved messages. Use /save first if needed.")), /* @__PURE__ */ React17.createElement(
|
|
2048
|
+
SelectInput4,
|
|
2176
2049
|
{
|
|
2177
2050
|
items: confirmationItems,
|
|
2178
2051
|
onSelect: handleConfirmationSelect,
|
|
2179
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
2052
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React17.createElement(Box16, null, /* @__PURE__ */ React17.createElement(Text16, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
2180
2053
|
}
|
|
2181
|
-
), /* @__PURE__ */
|
|
2054
|
+
), /* @__PURE__ */ React17.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
2182
2055
|
}
|
|
2183
2056
|
|
|
2184
2057
|
// src/components/LoginFlow.tsx
|
|
2185
|
-
import
|
|
2186
|
-
import { Box as
|
|
2187
|
-
import
|
|
2058
|
+
import React18, { useState as useState7, useEffect as useEffect3 } from "react";
|
|
2059
|
+
import { Box as Box17, Text as Text17 } from "ink";
|
|
2060
|
+
import Spinner2 from "ink-spinner";
|
|
2188
2061
|
import jwt from "jsonwebtoken";
|
|
2189
2062
|
import open from "open";
|
|
2190
2063
|
|
|
@@ -2299,11 +2172,11 @@ var OAuthClient = class {
|
|
|
2299
2172
|
|
|
2300
2173
|
// src/components/LoginFlow.tsx
|
|
2301
2174
|
function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, onError }) {
|
|
2302
|
-
const [status, setStatus] =
|
|
2303
|
-
const [deviceFlow, setDeviceFlow] =
|
|
2304
|
-
const [statusMessage, setStatusMessage] =
|
|
2305
|
-
const [error, setError] =
|
|
2306
|
-
|
|
2175
|
+
const [status, setStatus] = useState7("initiating");
|
|
2176
|
+
const [deviceFlow, setDeviceFlow] = useState7(null);
|
|
2177
|
+
const [statusMessage, setStatusMessage] = useState7("Initiating device authorization...");
|
|
2178
|
+
const [error, setError] = useState7(null);
|
|
2179
|
+
useEffect3(() => {
|
|
2307
2180
|
const runLoginFlow = async () => {
|
|
2308
2181
|
const oauth = new OAuthClient(apiUrl);
|
|
2309
2182
|
try {
|
|
@@ -2338,7 +2211,7 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
2338
2211
|
};
|
|
2339
2212
|
runLoginFlow();
|
|
2340
2213
|
}, [apiUrl, configStore, onSuccess, onError]);
|
|
2341
|
-
|
|
2214
|
+
useEffect3(() => {
|
|
2342
2215
|
if (deviceFlow && status === "waiting") {
|
|
2343
2216
|
open(deviceFlow.verification_uri_complete).catch((err) => {
|
|
2344
2217
|
console.error("Failed to auto-open browser:", err);
|
|
@@ -2346,15 +2219,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
2346
2219
|
}
|
|
2347
2220
|
}, [deviceFlow, status]);
|
|
2348
2221
|
if (status === "initiating") {
|
|
2349
|
-
return /* @__PURE__ */
|
|
2222
|
+
return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React18.createElement(Box17, null, /* @__PURE__ */ React18.createElement(Text17, { color: "cyan" }, /* @__PURE__ */ React18.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
2350
2223
|
}
|
|
2351
2224
|
if (status === "error") {
|
|
2352
|
-
return /* @__PURE__ */
|
|
2225
|
+
return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React18.createElement(Box17, null, /* @__PURE__ */ React18.createElement(Text17, { color: "red" }, error)));
|
|
2353
2226
|
}
|
|
2354
2227
|
if (status === "success") {
|
|
2355
|
-
return /* @__PURE__ */
|
|
2228
|
+
return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React18.createElement(Box17, null, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
2356
2229
|
}
|
|
2357
|
-
return /* @__PURE__ */
|
|
2230
|
+
return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text17, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React18.createElement(Text17, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text17, null, "And enter this code when prompted:")), /* @__PURE__ */ React18.createElement(Box17, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React18.createElement(Text17, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React18.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "cyan" }, /* @__PURE__ */ React18.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React18.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
2358
2231
|
}
|
|
2359
2232
|
|
|
2360
2233
|
// src/storage/SessionStore.ts
|
|
@@ -3847,16 +3720,8 @@ CORE BEHAVIOR:
|
|
|
3847
3720
|
|
|
3848
3721
|
FOR SOFTWARE ENGINEERING TASKS:
|
|
3849
3722
|
When requested to perform tasks like fixing bugs, adding features, refactoring, or explaining code, follow this sequence:
|
|
3850
|
-
1. **Understand:** Think about the user's request and the relevant codebase context. Use '${TOOL_GREP_SEARCH}' and '${TOOL_GLOB_FILES}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${TOOL_FILE_READ}' to understand context and validate any assumptions you may have.
|
|
3851
|
-
|
|
3852
|
-
IMPORTANT FILE READING RULES:
|
|
3853
|
-
- Read each file ONCE and refer to it in conversation history instead of re-reading
|
|
3854
|
-
- Read files COMPLETELY by default (without offset/limit parameters)
|
|
3855
|
-
- Only use offset/limit for files that are too large to fit in context (thousands of lines)
|
|
3856
|
-
- If you need to read multiple DIFFERENT files, make multiple parallel calls to '${TOOL_FILE_READ}'
|
|
3857
|
-
- NEVER make multiple calls to read the SAME file with different offsets unless it's truly too large
|
|
3858
|
-
|
|
3859
|
-
When the task involves **complex refactoring, codebase exploration or system-wide analysis**, your **first and primary action** must be to delegate to the '${EXPLORE_SUBAGENT_TYPE}' agent using the '${TOOL_SUBAGENT_DELEGATE}' tool. Use it to build a comprehensive understanding of the code, its structure, and dependencies. For **simple, targeted searches** (like finding a specific function name, file path, or variable declaration), you should use '${TOOL_GREP_SEARCH}' or '${TOOL_GLOB_FILES}' directly.
|
|
3723
|
+
1. **Understand:** Think about the user's request and the relevant codebase context. Use '${TOOL_GREP_SEARCH}' and '${TOOL_GLOB_FILES}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${TOOL_FILE_READ}' to understand context and validate any assumptions you may have. If you need to read multiple files, you should make multiple parallel calls to '${TOOL_FILE_READ}'.
|
|
3724
|
+
When the task involves **complex refactoring, codebase exploration or system-wide analysis**, your **first and primary action** must be to delegate to the '${EXPLORE_SUBAGENT_TYPE}' agent using the '${TOOL_SUBAGENT_DELEGATE}' tool. Use it to build a comprehensive understanding of the code, its structure, and dependencies. For **simple, targeted searches** (like finding a specific function name, file path, or variable declaration), you should use '${TOOL_GREP_SEARCH}' or '${TOOL_GLOB_FILES}' directly.
|
|
3860
3725
|
2. **Plan:** Build a coherent and grounded (based on the understanding in step 1) plan for how you intend to resolve the user's task. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.
|
|
3861
3726
|
If '${EXPLORE_SUBAGENT_TYPE}' subagent was used, do not ignore the output of the agent, you must use it as the foundation of your plan. For complex tasks, break them down into smaller, manageable subtasks and use the \`${TOOL_WRITE_TODOS}\` tool to track your progress. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.
|
|
3862
3727
|
3. **Implement:** Use the available tools (e.g., '${TOOL_EDIT_LOCAL_FILE}', '${TOOL_CREATE_FILE}', '${TOOL_BASH_EXECUTE}' ...) to act on the plan, strictly adhering to the project's established conventions.
|
|
@@ -9216,7 +9081,7 @@ import { existsSync as existsSync3, statSync as statSync4 } from "fs";
|
|
|
9216
9081
|
import path7 from "path";
|
|
9217
9082
|
var MAX_FILE_SIZE2 = 10 * 1024 * 1024;
|
|
9218
9083
|
async function readFileContent(params) {
|
|
9219
|
-
const { path: filePath, encoding = "utf-8",
|
|
9084
|
+
const { path: filePath, encoding = "utf-8", maxLines } = params;
|
|
9220
9085
|
const normalizedPath = path7.normalize(filePath);
|
|
9221
9086
|
const resolvedPath = path7.resolve(process.cwd(), normalizedPath);
|
|
9222
9087
|
const cwd = path7.resolve(process.cwd());
|
|
@@ -9238,39 +9103,16 @@ async function readFileContent(params) {
|
|
|
9238
9103
|
throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
|
|
9239
9104
|
}
|
|
9240
9105
|
const content = await fs8.readFile(resolvedPath, encoding);
|
|
9241
|
-
if (typeof content === "string") {
|
|
9106
|
+
if (maxLines && typeof content === "string") {
|
|
9242
9107
|
const lines = content.split("\n");
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
}
|
|
9247
|
-
if (offset >= totalLines) {
|
|
9248
|
-
return `No content to show. File has ${totalLines} lines, but offset is ${offset}.
|
|
9249
|
-
(offset is 0-based, so valid range is 0-${Math.max(0, totalLines - 1)})`;
|
|
9250
|
-
}
|
|
9251
|
-
if (limit !== void 0 && limit > 0) {
|
|
9252
|
-
const endLine = Math.min(offset + limit, totalLines);
|
|
9253
|
-
const paginatedContent = lines.slice(offset, endLine).join("\n");
|
|
9254
|
-
if (endLine < totalLines) {
|
|
9255
|
-
const nextOffset = endLine;
|
|
9256
|
-
return `${paginatedContent}
|
|
9108
|
+
if (lines.length > maxLines) {
|
|
9109
|
+
const truncatedContent = lines.slice(0, maxLines).join("\n");
|
|
9110
|
+
return `${truncatedContent}
|
|
9257
9111
|
|
|
9258
|
-
...
|
|
9259
|
-
To read more, use offset: ${nextOffset}`;
|
|
9260
|
-
}
|
|
9261
|
-
return `${paginatedContent}
|
|
9262
|
-
|
|
9263
|
-
... Showing lines ${offset + 1}-${endLine} of ${totalLines} total lines (${stats.size} bytes total). End of file reached.`;
|
|
9112
|
+
... (truncated: ${lines.length - maxLines} more lines, ${stats.size} bytes total)`;
|
|
9264
9113
|
}
|
|
9265
|
-
if (offset > 0) {
|
|
9266
|
-
const paginatedContent = lines.slice(offset).join("\n");
|
|
9267
|
-
return `${paginatedContent}
|
|
9268
|
-
|
|
9269
|
-
... Showing lines ${offset + 1}-${totalLines} of ${totalLines} total lines (${stats.size} bytes total).`;
|
|
9270
|
-
}
|
|
9271
|
-
return content;
|
|
9272
9114
|
}
|
|
9273
|
-
return `[Binary content, ${stats.size} bytes, base64 encoded]
|
|
9115
|
+
return typeof content === "string" ? content : `[Binary content, ${stats.size} bytes, base64 encoded]
|
|
9274
9116
|
${content}`;
|
|
9275
9117
|
}
|
|
9276
9118
|
async function checkIfBinary(filePath) {
|
|
@@ -9307,7 +9149,7 @@ var fileReadTool = {
|
|
|
9307
9149
|
},
|
|
9308
9150
|
toolSchema: {
|
|
9309
9151
|
name: "file_read",
|
|
9310
|
-
description: "Read the contents of a file from the local filesystem. Supports text files with various encodings. Files are restricted to the current working directory and subdirectories for security.
|
|
9152
|
+
description: "Read the contents of a file from the local filesystem. Supports text files with various encodings. Files are restricted to the current working directory and subdirectories for security.",
|
|
9311
9153
|
parameters: {
|
|
9312
9154
|
type: "object",
|
|
9313
9155
|
properties: {
|
|
@@ -9320,13 +9162,9 @@ var fileReadTool = {
|
|
|
9320
9162
|
description: "File encoding (default: utf-8). Use base64 for binary files.",
|
|
9321
9163
|
enum: ["utf-8", "ascii", "base64"]
|
|
9322
9164
|
},
|
|
9323
|
-
|
|
9165
|
+
maxLines: {
|
|
9324
9166
|
type: "number",
|
|
9325
|
-
description: "
|
|
9326
|
-
},
|
|
9327
|
-
limit: {
|
|
9328
|
-
type: "number",
|
|
9329
|
-
description: "OPTIONAL: Maximum number of lines to read from offset. Only use for extremely large files (thousands of lines) that cannot fit in context. Default behavior is to read the entire file, which is preferred for most cases."
|
|
9167
|
+
description: "Maximum number of lines to return (optional). Useful for previewing large files. Returns full file by default."
|
|
9330
9168
|
}
|
|
9331
9169
|
},
|
|
9332
9170
|
required: ["path"]
|
|
@@ -11361,7 +11199,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
11361
11199
|
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
11362
11200
|
return result2;
|
|
11363
11201
|
}
|
|
11364
|
-
const { useCliStore: useCliStore2 } = await import("./store-
|
|
11202
|
+
const { useCliStore: useCliStore2 } = await import("./store-JNTO6SRG.js");
|
|
11365
11203
|
if (useCliStore2.getState().autoAcceptEdits) {
|
|
11366
11204
|
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
11367
11205
|
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
@@ -13036,40 +12874,6 @@ var ServerLlmBackend = class {
|
|
|
13036
12874
|
}
|
|
13037
12875
|
};
|
|
13038
12876
|
|
|
13039
|
-
// src/llm/NotifyingLlmBackend.ts
|
|
13040
|
-
var NotifyingLlmBackend = class {
|
|
13041
|
-
constructor(inner, backgroundManager) {
|
|
13042
|
-
this.inner = inner;
|
|
13043
|
-
this.backgroundManager = backgroundManager;
|
|
13044
|
-
}
|
|
13045
|
-
get currentModel() {
|
|
13046
|
-
return this.inner.currentModel;
|
|
13047
|
-
}
|
|
13048
|
-
set currentModel(model) {
|
|
13049
|
-
this.inner.currentModel = model;
|
|
13050
|
-
}
|
|
13051
|
-
async complete(model, messages, options, callback) {
|
|
13052
|
-
const notifications = this.backgroundManager.drainNotifications();
|
|
13053
|
-
let effectiveMessages = messages;
|
|
13054
|
-
if (notifications.length > 0) {
|
|
13055
|
-
const notificationText = notifications.join("\n\n---\n\n");
|
|
13056
|
-
const notificationMessage = {
|
|
13057
|
-
role: "user",
|
|
13058
|
-
content: `[System Notification]
|
|
13059
|
-
|
|
13060
|
-
${notificationText}
|
|
13061
|
-
|
|
13062
|
-
Please acknowledge these background agent results and incorporate them into your current work.`
|
|
13063
|
-
};
|
|
13064
|
-
effectiveMessages = [...messages, notificationMessage];
|
|
13065
|
-
}
|
|
13066
|
-
return this.inner.complete(model, effectiveMessages, options, callback);
|
|
13067
|
-
}
|
|
13068
|
-
async getModelInfo() {
|
|
13069
|
-
return this.inner.getModelInfo();
|
|
13070
|
-
}
|
|
13071
|
-
};
|
|
13072
|
-
|
|
13073
12877
|
// src/auth/ApiClient.ts
|
|
13074
12878
|
import axios11 from "axios";
|
|
13075
12879
|
var ApiClient = class {
|
|
@@ -13207,7 +13011,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
13207
13011
|
// package.json
|
|
13208
13012
|
var package_default = {
|
|
13209
13013
|
name: "@bike4mind/cli",
|
|
13210
|
-
version: "0.2.25-
|
|
13014
|
+
version: "0.2.25-fix-anthropic-overloaded-fallback.18535+44e7b5760",
|
|
13211
13015
|
type: "module",
|
|
13212
13016
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
13213
13017
|
license: "UNLICENSED",
|
|
@@ -13315,10 +13119,10 @@ var package_default = {
|
|
|
13315
13119
|
},
|
|
13316
13120
|
devDependencies: {
|
|
13317
13121
|
"@bike4mind/agents": "0.1.0",
|
|
13318
|
-
"@bike4mind/common": "2.47.2-
|
|
13319
|
-
"@bike4mind/mcp": "1.28.1-
|
|
13320
|
-
"@bike4mind/services": "2.45.2-
|
|
13321
|
-
"@bike4mind/utils": "2.3.4-
|
|
13122
|
+
"@bike4mind/common": "2.47.2-fix-anthropic-overloaded-fallback.18535+44e7b5760",
|
|
13123
|
+
"@bike4mind/mcp": "1.28.1-fix-anthropic-overloaded-fallback.18535+44e7b5760",
|
|
13124
|
+
"@bike4mind/services": "2.45.2-fix-anthropic-overloaded-fallback.18535+44e7b5760",
|
|
13125
|
+
"@bike4mind/utils": "2.3.4-fix-anthropic-overloaded-fallback.18535+44e7b5760",
|
|
13322
13126
|
"@types/better-sqlite3": "^7.6.13",
|
|
13323
13127
|
"@types/diff": "^5.0.9",
|
|
13324
13128
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -13335,7 +13139,7 @@ var package_default = {
|
|
|
13335
13139
|
optionalDependencies: {
|
|
13336
13140
|
"@vscode/ripgrep": "^1.17.0"
|
|
13337
13141
|
},
|
|
13338
|
-
gitHead: "
|
|
13142
|
+
gitHead: "44e7b5760cfc839f3777b505abfbac911e341c26"
|
|
13339
13143
|
};
|
|
13340
13144
|
|
|
13341
13145
|
// src/config/constants.ts
|
|
@@ -13933,7 +13737,7 @@ Describe the expected output format here.
|
|
|
13933
13737
|
};
|
|
13934
13738
|
|
|
13935
13739
|
// src/agents/delegateTool.ts
|
|
13936
|
-
function createAgentDelegateTool(orchestrator, agentStore, parentSessionId
|
|
13740
|
+
function createAgentDelegateTool(orchestrator, agentStore, parentSessionId) {
|
|
13937
13741
|
const agents = agentStore.getAllAgents();
|
|
13938
13742
|
const agentDescriptions = agents.map((a) => `- **${a.name}**: ${a.description}`).join("\n");
|
|
13939
13743
|
const agentNames = agentStore.getAgentNames();
|
|
@@ -13950,21 +13754,13 @@ function createAgentDelegateTool(orchestrator, agentStore, parentSessionId, back
|
|
|
13950
13754
|
const available = agentNames.join(", ");
|
|
13951
13755
|
throw new Error(`agent_delegate: unknown agent "${params.agent}". Available agents: ${available}`);
|
|
13952
13756
|
}
|
|
13953
|
-
const
|
|
13757
|
+
const result = await orchestrator.delegateToAgent({
|
|
13954
13758
|
task: params.task,
|
|
13955
13759
|
agentName: params.agent,
|
|
13956
13760
|
thoroughness: params.thoroughness,
|
|
13957
13761
|
variables: params.variables,
|
|
13958
13762
|
parentSessionId
|
|
13959
|
-
};
|
|
13960
|
-
if (params.run_in_background && backgroundManager) {
|
|
13961
|
-
const jobId = backgroundManager.spawn({
|
|
13962
|
-
...spawnOptions,
|
|
13963
|
-
groupDescription: params.group_description
|
|
13964
|
-
});
|
|
13965
|
-
return `Background agent started. Job ID: ${jobId}. Use check_agent_status tool with this job ID to retrieve results when ready.`;
|
|
13966
|
-
}
|
|
13967
|
-
const result = await orchestrator.delegateToAgent(spawnOptions);
|
|
13763
|
+
});
|
|
13968
13764
|
return result.summary;
|
|
13969
13765
|
},
|
|
13970
13766
|
toolSchema: {
|
|
@@ -14015,14 +13811,6 @@ ${agentDescriptions}
|
|
|
14015
13811
|
type: "object",
|
|
14016
13812
|
additionalProperties: { type: "string" },
|
|
14017
13813
|
description: 'Custom variables to substitute in agent system prompt. For example: { "DOMAIN": "authentication", "STRICTNESS": "high" }'
|
|
14018
|
-
},
|
|
14019
|
-
run_in_background: {
|
|
14020
|
-
type: "boolean",
|
|
14021
|
-
description: "Run the agent in the background (non-blocking). Returns a job ID immediately. Use check_agent_status to poll for results. Useful for parallel work or long-running tasks."
|
|
14022
|
-
},
|
|
14023
|
-
group_description: {
|
|
14024
|
-
type: "string",
|
|
14025
|
-
description: 'Short description of what this group of background agents is working on (e.g., "Implementing user authentication"). Only needed for the first background agent in a group. All background agents spawned in the same turn are automatically grouped.'
|
|
14026
13814
|
}
|
|
14027
13815
|
},
|
|
14028
13816
|
required: ["task", "agent"]
|
|
@@ -14031,399 +13819,6 @@ ${agentDescriptions}
|
|
|
14031
13819
|
};
|
|
14032
13820
|
}
|
|
14033
13821
|
|
|
14034
|
-
// src/agents/BackgroundAgentManager.ts
|
|
14035
|
-
import { randomBytes as randomBytes4 } from "crypto";
|
|
14036
|
-
var DEFAULT_MAX_CONCURRENT = 4;
|
|
14037
|
-
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
14038
|
-
function isTerminalStatus(status) {
|
|
14039
|
-
return TERMINAL_STATUSES.has(status);
|
|
14040
|
-
}
|
|
14041
|
-
var DEFAULT_MAX_JOB_AGE_MS = 60 * 60 * 1e3;
|
|
14042
|
-
var MAX_JOBS_BEFORE_CLEANUP = 100;
|
|
14043
|
-
function pluralize(count) {
|
|
14044
|
-
return count === 1 ? "" : "s";
|
|
14045
|
-
}
|
|
14046
|
-
var BackgroundAgentManager = class {
|
|
14047
|
-
constructor(orchestrator, maxConcurrent = DEFAULT_MAX_CONCURRENT) {
|
|
14048
|
-
this.jobs = /* @__PURE__ */ new Map();
|
|
14049
|
-
this.queue = [];
|
|
14050
|
-
// Job IDs waiting to start
|
|
14051
|
-
this.runningCount = 0;
|
|
14052
|
-
this.onStatusChange = null;
|
|
14053
|
-
this.onGroupCompletion = null;
|
|
14054
|
-
this.pendingNotifications = [];
|
|
14055
|
-
this.turnGroups = /* @__PURE__ */ new Map();
|
|
14056
|
-
this.currentTurnId = null;
|
|
14057
|
-
this.orchestrator = orchestrator;
|
|
14058
|
-
this.maxConcurrent = maxConcurrent;
|
|
14059
|
-
}
|
|
14060
|
-
/**
|
|
14061
|
-
* Set the current turn ID. Jobs spawned while this is set will be grouped.
|
|
14062
|
-
* Call with null to clear (after agent.run() completes).
|
|
14063
|
-
*/
|
|
14064
|
-
setCurrentTurn(turnId) {
|
|
14065
|
-
this.currentTurnId = turnId;
|
|
14066
|
-
}
|
|
14067
|
-
/**
|
|
14068
|
-
* Get the current turn ID (for testing/debugging)
|
|
14069
|
-
*/
|
|
14070
|
-
getCurrentTurnId() {
|
|
14071
|
-
return this.currentTurnId;
|
|
14072
|
-
}
|
|
14073
|
-
/**
|
|
14074
|
-
* Set a callback that fires whenever a job changes status.
|
|
14075
|
-
* Used to update the Zustand store / UI.
|
|
14076
|
-
*/
|
|
14077
|
-
setOnStatusChange(callback) {
|
|
14078
|
-
this.onStatusChange = callback;
|
|
14079
|
-
}
|
|
14080
|
-
/**
|
|
14081
|
-
* Set a callback that fires when all jobs in a turn group complete.
|
|
14082
|
-
* Used to notify the user that background work is done.
|
|
14083
|
-
*/
|
|
14084
|
-
setOnGroupCompletion(callback) {
|
|
14085
|
-
this.onGroupCompletion = callback;
|
|
14086
|
-
}
|
|
14087
|
-
/**
|
|
14088
|
-
* Spawn an agent in the background. Returns the job ID immediately.
|
|
14089
|
-
* If concurrency limit is reached, the job is queued and starts when a slot opens.
|
|
14090
|
-
*/
|
|
14091
|
-
spawn(options) {
|
|
14092
|
-
const id = `bg-${randomBytes4(4).toString("hex")}`;
|
|
14093
|
-
const abortController = new AbortController();
|
|
14094
|
-
const isQueued = this.runningCount >= this.maxConcurrent;
|
|
14095
|
-
const job = {
|
|
14096
|
-
id,
|
|
14097
|
-
agentName: options.agentName,
|
|
14098
|
-
task: options.task,
|
|
14099
|
-
status: isQueued ? "queued" : "running",
|
|
14100
|
-
startTime: Date.now(),
|
|
14101
|
-
turnId: this.currentTurnId ?? void 0,
|
|
14102
|
-
groupDescription: options.groupDescription
|
|
14103
|
-
};
|
|
14104
|
-
const internal = { job, options, abortController };
|
|
14105
|
-
this.jobs.set(id, internal);
|
|
14106
|
-
this.notifyStatusChange(job);
|
|
14107
|
-
if (this.currentTurnId) {
|
|
14108
|
-
let group = this.turnGroups.get(this.currentTurnId);
|
|
14109
|
-
if (!group) {
|
|
14110
|
-
group = {
|
|
14111
|
-
turnId: this.currentTurnId,
|
|
14112
|
-
description: options.groupDescription,
|
|
14113
|
-
jobIds: [],
|
|
14114
|
-
notifications: []
|
|
14115
|
-
};
|
|
14116
|
-
this.turnGroups.set(this.currentTurnId, group);
|
|
14117
|
-
} else if (options.groupDescription && !group.description) {
|
|
14118
|
-
group.description = options.groupDescription;
|
|
14119
|
-
}
|
|
14120
|
-
group.jobIds.push(id);
|
|
14121
|
-
}
|
|
14122
|
-
if (isQueued) {
|
|
14123
|
-
this.queue.push(id);
|
|
14124
|
-
} else {
|
|
14125
|
-
this.startJob(internal);
|
|
14126
|
-
}
|
|
14127
|
-
return id;
|
|
14128
|
-
}
|
|
14129
|
-
/**
|
|
14130
|
-
* Get a job by ID (public-facing snapshot without internals)
|
|
14131
|
-
*/
|
|
14132
|
-
getJob(id) {
|
|
14133
|
-
return this.jobs.get(id)?.job;
|
|
14134
|
-
}
|
|
14135
|
-
/**
|
|
14136
|
-
* Get the full result of a completed job
|
|
14137
|
-
*/
|
|
14138
|
-
getResult(id) {
|
|
14139
|
-
return this.jobs.get(id)?.result;
|
|
14140
|
-
}
|
|
14141
|
-
/**
|
|
14142
|
-
* List all jobs (public-facing snapshots)
|
|
14143
|
-
*/
|
|
14144
|
-
listJobs() {
|
|
14145
|
-
return Array.from(this.jobs.values()).map((j) => j.job);
|
|
14146
|
-
}
|
|
14147
|
-
/**
|
|
14148
|
-
* Cancel a running or queued job
|
|
14149
|
-
*/
|
|
14150
|
-
cancelJob(id) {
|
|
14151
|
-
const internal = this.jobs.get(id);
|
|
14152
|
-
if (!internal) return false;
|
|
14153
|
-
if (internal.job.status === "queued") {
|
|
14154
|
-
this.queue = this.queue.filter((qid) => qid !== id);
|
|
14155
|
-
this.updateJob(id, { status: "cancelled", endTime: Date.now() });
|
|
14156
|
-
return true;
|
|
14157
|
-
}
|
|
14158
|
-
if (internal.job.status === "running") {
|
|
14159
|
-
internal.abortController.abort();
|
|
14160
|
-
this.updateJob(id, { status: "cancelled", endTime: Date.now() });
|
|
14161
|
-
this.runningCount--;
|
|
14162
|
-
this.processQueue();
|
|
14163
|
-
return true;
|
|
14164
|
-
}
|
|
14165
|
-
return false;
|
|
14166
|
-
}
|
|
14167
|
-
/**
|
|
14168
|
-
* Drain all pending notifications (called by the LLM wrapper before each completion).
|
|
14169
|
-
* Returns notification strings and clears the queue.
|
|
14170
|
-
*/
|
|
14171
|
-
drainNotifications() {
|
|
14172
|
-
return this.pendingNotifications.splice(0);
|
|
14173
|
-
}
|
|
14174
|
-
/**
|
|
14175
|
-
* Handle job completion - routes to grouped or immediate notification
|
|
14176
|
-
*/
|
|
14177
|
-
handleJobCompletion(job, status, content) {
|
|
14178
|
-
if (job.turnId) {
|
|
14179
|
-
const group = this.turnGroups.get(job.turnId);
|
|
14180
|
-
if (group) {
|
|
14181
|
-
group.notifications.push({
|
|
14182
|
-
jobId: job.id,
|
|
14183
|
-
agentName: job.agentName,
|
|
14184
|
-
task: job.task,
|
|
14185
|
-
status,
|
|
14186
|
-
content
|
|
14187
|
-
});
|
|
14188
|
-
this.checkTurnGroupCompletion(job.turnId);
|
|
14189
|
-
return;
|
|
14190
|
-
}
|
|
14191
|
-
}
|
|
14192
|
-
this.pendingNotifications.push(content);
|
|
14193
|
-
}
|
|
14194
|
-
/**
|
|
14195
|
-
* Check if all jobs in a turn group have completed
|
|
14196
|
-
*/
|
|
14197
|
-
checkTurnGroupCompletion(turnId) {
|
|
14198
|
-
const group = this.turnGroups.get(turnId);
|
|
14199
|
-
if (!group) return;
|
|
14200
|
-
const allComplete = group.jobIds.every((jobId) => {
|
|
14201
|
-
const internal = this.jobs.get(jobId);
|
|
14202
|
-
return internal && isTerminalStatus(internal.job.status);
|
|
14203
|
-
});
|
|
14204
|
-
if (allComplete) {
|
|
14205
|
-
const notification = this.createConsolidatedNotification(group);
|
|
14206
|
-
this.pendingNotifications.push(notification);
|
|
14207
|
-
if (this.onGroupCompletion) {
|
|
14208
|
-
this.onGroupCompletion(notification, group.description);
|
|
14209
|
-
}
|
|
14210
|
-
this.turnGroups.delete(turnId);
|
|
14211
|
-
this.cleanupOldJobs();
|
|
14212
|
-
}
|
|
14213
|
-
}
|
|
14214
|
-
/**
|
|
14215
|
-
* Create a consolidated notification for a completed turn group
|
|
14216
|
-
*/
|
|
14217
|
-
createConsolidatedNotification(group) {
|
|
14218
|
-
const statusCounts = { completed: 0, failed: 0, cancelled: 0 };
|
|
14219
|
-
for (const notification of group.notifications) {
|
|
14220
|
-
statusCounts[notification.status]++;
|
|
14221
|
-
}
|
|
14222
|
-
const total = group.notifications.length;
|
|
14223
|
-
const statsSummary = Object.entries(statusCounts).filter(([, count]) => count > 0).map(([status, count]) => `${count} ${status}`).join(", ");
|
|
14224
|
-
const agentCount = `${total} agent${pluralize(total)} finished`;
|
|
14225
|
-
const header = group.description ? `[Background Agents Completed] "${group.description}" - ${agentCount} (${statsSummary})` : `[Background Agents Completed] ${agentCount} (${statsSummary})`;
|
|
14226
|
-
const details = group.notifications.map((n) => {
|
|
14227
|
-
const statusLabel = n.status.toUpperCase();
|
|
14228
|
-
return `=== Agent "${n.agentName}" (job ${n.jobId}) - ${statusLabel} ===
|
|
14229
|
-
Task: ${n.task}
|
|
14230
|
-
${n.status === "completed" ? "Result" : "Error"}:
|
|
14231
|
-
${n.content}`;
|
|
14232
|
-
}).join("\n\n");
|
|
14233
|
-
return `${header}
|
|
14234
|
-
|
|
14235
|
-
${details}`;
|
|
14236
|
-
}
|
|
14237
|
-
startJob(internal) {
|
|
14238
|
-
this.runningCount++;
|
|
14239
|
-
this.updateJob(internal.job.id, { status: "running" });
|
|
14240
|
-
const { job, options } = internal;
|
|
14241
|
-
internal.promise = this.orchestrator.delegateToAgent(options).then((result) => {
|
|
14242
|
-
this.updateJob(job.id, {
|
|
14243
|
-
status: "completed",
|
|
14244
|
-
endTime: Date.now(),
|
|
14245
|
-
resultSummary: result.summary
|
|
14246
|
-
});
|
|
14247
|
-
internal.result = result;
|
|
14248
|
-
this.handleJobCompletion(job, "completed", result.summary);
|
|
14249
|
-
return result;
|
|
14250
|
-
}).catch((error) => {
|
|
14251
|
-
const isCancelled = internal.abortController.signal.aborted;
|
|
14252
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
14253
|
-
const status = isCancelled ? "cancelled" : "failed";
|
|
14254
|
-
this.updateJob(job.id, {
|
|
14255
|
-
status,
|
|
14256
|
-
endTime: Date.now(),
|
|
14257
|
-
error: errorMsg
|
|
14258
|
-
});
|
|
14259
|
-
this.handleJobCompletion(job, status, errorMsg);
|
|
14260
|
-
}).finally(() => {
|
|
14261
|
-
this.runningCount--;
|
|
14262
|
-
this.processQueue();
|
|
14263
|
-
});
|
|
14264
|
-
}
|
|
14265
|
-
/** Start the next queued job if there's capacity */
|
|
14266
|
-
processQueue() {
|
|
14267
|
-
while (this.queue.length > 0 && this.runningCount < this.maxConcurrent) {
|
|
14268
|
-
const nextId = this.queue.shift();
|
|
14269
|
-
const internal = this.jobs.get(nextId);
|
|
14270
|
-
if (internal && internal.job.status === "queued") {
|
|
14271
|
-
this.startJob(internal);
|
|
14272
|
-
}
|
|
14273
|
-
}
|
|
14274
|
-
}
|
|
14275
|
-
updateJob(id, updates) {
|
|
14276
|
-
const internal = this.jobs.get(id);
|
|
14277
|
-
if (!internal) return;
|
|
14278
|
-
Object.assign(internal.job, updates);
|
|
14279
|
-
this.notifyStatusChange(internal.job);
|
|
14280
|
-
}
|
|
14281
|
-
notifyStatusChange(job) {
|
|
14282
|
-
if (this.onStatusChange) {
|
|
14283
|
-
this.onStatusChange({ ...job });
|
|
14284
|
-
}
|
|
14285
|
-
}
|
|
14286
|
-
/**
|
|
14287
|
-
* Clean up old completed jobs to prevent memory leaks.
|
|
14288
|
-
* Removes jobs that have been in a terminal state for longer than maxAgeMs.
|
|
14289
|
-
* Also removes jobs when total count exceeds MAX_JOBS_BEFORE_CLEANUP.
|
|
14290
|
-
*/
|
|
14291
|
-
cleanupOldJobs(maxAgeMs = DEFAULT_MAX_JOB_AGE_MS) {
|
|
14292
|
-
const now = Date.now();
|
|
14293
|
-
let cleanedCount = 0;
|
|
14294
|
-
for (const [id, internal] of this.jobs.entries()) {
|
|
14295
|
-
if (isTerminalStatus(internal.job.status) && internal.job.endTime && now - internal.job.endTime > maxAgeMs) {
|
|
14296
|
-
this.jobs.delete(id);
|
|
14297
|
-
cleanedCount++;
|
|
14298
|
-
}
|
|
14299
|
-
}
|
|
14300
|
-
if (this.jobs.size > MAX_JOBS_BEFORE_CLEANUP) {
|
|
14301
|
-
const completedJobs = Array.from(this.jobs.entries()).filter(([, j]) => isTerminalStatus(j.job.status)).sort((a, b) => (a[1].job.endTime || 0) - (b[1].job.endTime || 0));
|
|
14302
|
-
const toRemove = completedJobs.slice(0, this.jobs.size - MAX_JOBS_BEFORE_CLEANUP + 10);
|
|
14303
|
-
for (const [id] of toRemove) {
|
|
14304
|
-
this.jobs.delete(id);
|
|
14305
|
-
cleanedCount++;
|
|
14306
|
-
}
|
|
14307
|
-
}
|
|
14308
|
-
return cleanedCount;
|
|
14309
|
-
}
|
|
14310
|
-
/**
|
|
14311
|
-
* Get the current number of jobs (for monitoring)
|
|
14312
|
-
*/
|
|
14313
|
-
getJobCount() {
|
|
14314
|
-
let running = 0;
|
|
14315
|
-
let queued = 0;
|
|
14316
|
-
let completed = 0;
|
|
14317
|
-
for (const internal of this.jobs.values()) {
|
|
14318
|
-
switch (internal.job.status) {
|
|
14319
|
-
case "running":
|
|
14320
|
-
running++;
|
|
14321
|
-
break;
|
|
14322
|
-
case "queued":
|
|
14323
|
-
queued++;
|
|
14324
|
-
break;
|
|
14325
|
-
default:
|
|
14326
|
-
completed++;
|
|
14327
|
-
}
|
|
14328
|
-
}
|
|
14329
|
-
return { total: this.jobs.size, running, queued, completed };
|
|
14330
|
-
}
|
|
14331
|
-
};
|
|
14332
|
-
|
|
14333
|
-
// src/agents/backgroundTools.ts
|
|
14334
|
-
function createBackgroundAgentTools(manager) {
|
|
14335
|
-
const checkAgentStatus = {
|
|
14336
|
-
toolFn: async (args) => {
|
|
14337
|
-
const { job_id } = args;
|
|
14338
|
-
if (!job_id) throw new Error("check_agent_status: job_id is required");
|
|
14339
|
-
const job = manager.getJob(job_id);
|
|
14340
|
-
if (!job) return `No background agent found with ID: ${job_id}`;
|
|
14341
|
-
switch (job.status) {
|
|
14342
|
-
case "queued":
|
|
14343
|
-
return `Agent "${job.agentName}" is queued (waiting for a concurrency slot). Task: ${job.task}`;
|
|
14344
|
-
case "running": {
|
|
14345
|
-
const elapsed = Math.round((Date.now() - job.startTime) / 1e3);
|
|
14346
|
-
return `Agent "${job.agentName}" is still running (${elapsed}s elapsed). Task: ${job.task}`;
|
|
14347
|
-
}
|
|
14348
|
-
case "completed": {
|
|
14349
|
-
const result = manager.getResult(job_id);
|
|
14350
|
-
return result?.summary || `Agent "${job.agentName}" completed but no summary available.`;
|
|
14351
|
-
}
|
|
14352
|
-
case "failed":
|
|
14353
|
-
return `Agent "${job.agentName}" failed: ${job.error || "Unknown error"}`;
|
|
14354
|
-
case "cancelled":
|
|
14355
|
-
return `Agent "${job.agentName}" was cancelled.`;
|
|
14356
|
-
}
|
|
14357
|
-
},
|
|
14358
|
-
toolSchema: {
|
|
14359
|
-
name: "check_agent_status",
|
|
14360
|
-
description: "Check the status and retrieve results of a background agent job. Use after spawning an agent with run_in_background: true.",
|
|
14361
|
-
parameters: {
|
|
14362
|
-
type: "object",
|
|
14363
|
-
properties: {
|
|
14364
|
-
job_id: {
|
|
14365
|
-
type: "string",
|
|
14366
|
-
description: "The job ID returned by agent_delegate when run_in_background was true"
|
|
14367
|
-
}
|
|
14368
|
-
},
|
|
14369
|
-
required: ["job_id"]
|
|
14370
|
-
}
|
|
14371
|
-
}
|
|
14372
|
-
};
|
|
14373
|
-
const listBackgroundAgents = {
|
|
14374
|
-
toolFn: async () => {
|
|
14375
|
-
const jobs = manager.listJobs();
|
|
14376
|
-
if (jobs.length === 0) return "No background agents.";
|
|
14377
|
-
return jobs.map((job) => {
|
|
14378
|
-
const elapsed = Math.round(((job.endTime || Date.now()) - job.startTime) / 1e3);
|
|
14379
|
-
const statusIcons = {
|
|
14380
|
-
queued: "\u{1F550}",
|
|
14381
|
-
running: "\u23F3",
|
|
14382
|
-
completed: "\u2705",
|
|
14383
|
-
failed: "\u274C",
|
|
14384
|
-
cancelled: "\u{1F6AB}"
|
|
14385
|
-
};
|
|
14386
|
-
const statusIcon = statusIcons[job.status] || "\u2753";
|
|
14387
|
-
return `${statusIcon} [${job.id}] ${job.agentName} (${job.status}, ${elapsed}s) - ${job.task.slice(0, 80)}`;
|
|
14388
|
-
}).join("\n");
|
|
14389
|
-
},
|
|
14390
|
-
toolSchema: {
|
|
14391
|
-
name: "list_background_agents",
|
|
14392
|
-
description: "List all background agent jobs with their current status.",
|
|
14393
|
-
parameters: {
|
|
14394
|
-
type: "object",
|
|
14395
|
-
properties: {}
|
|
14396
|
-
}
|
|
14397
|
-
}
|
|
14398
|
-
};
|
|
14399
|
-
const cancelBackgroundAgent = {
|
|
14400
|
-
toolFn: async (args) => {
|
|
14401
|
-
const { job_id } = args;
|
|
14402
|
-
if (!job_id) throw new Error("cancel_background_agent: job_id is required");
|
|
14403
|
-
const cancelled = manager.cancelJob(job_id);
|
|
14404
|
-
if (cancelled) return `Background agent ${job_id} has been cancelled.`;
|
|
14405
|
-
const job = manager.getJob(job_id);
|
|
14406
|
-
if (!job) return `No background agent found with ID: ${job_id}`;
|
|
14407
|
-
return `Cannot cancel agent ${job_id} - status is already "${job.status}".`;
|
|
14408
|
-
},
|
|
14409
|
-
toolSchema: {
|
|
14410
|
-
name: "cancel_background_agent",
|
|
14411
|
-
description: "Cancel a running background agent job.",
|
|
14412
|
-
parameters: {
|
|
14413
|
-
type: "object",
|
|
14414
|
-
properties: {
|
|
14415
|
-
job_id: {
|
|
14416
|
-
type: "string",
|
|
14417
|
-
description: "The job ID of the background agent to cancel"
|
|
14418
|
-
}
|
|
14419
|
-
},
|
|
14420
|
-
required: ["job_id"]
|
|
14421
|
-
}
|
|
14422
|
-
}
|
|
14423
|
-
};
|
|
14424
|
-
return [checkAgentStatus, listBackgroundAgents, cancelBackgroundAgent];
|
|
14425
|
-
}
|
|
14426
|
-
|
|
14427
13822
|
// src/tools/skillTool.ts
|
|
14428
13823
|
async function executeHook(script, context) {
|
|
14429
13824
|
const result = await runShellCommand({
|
|
@@ -14798,7 +14193,7 @@ var usageCache = null;
|
|
|
14798
14193
|
function CliApp() {
|
|
14799
14194
|
const { exit } = useApp();
|
|
14800
14195
|
const imageRenderer = new ImageRenderer();
|
|
14801
|
-
const [state, setState] =
|
|
14196
|
+
const [state, setState] = useState8({
|
|
14802
14197
|
session: null,
|
|
14803
14198
|
sessionStore: new SessionStore(),
|
|
14804
14199
|
configStore: new ConfigStore(),
|
|
@@ -14818,20 +14213,18 @@ function CliApp() {
|
|
|
14818
14213
|
orchestrator: null,
|
|
14819
14214
|
agentStore: null,
|
|
14820
14215
|
abortController: null,
|
|
14821
|
-
contextContent: ""
|
|
14822
|
-
backgroundManager: null
|
|
14216
|
+
contextContent: ""
|
|
14823
14217
|
});
|
|
14824
|
-
const [isInitialized, setIsInitialized] =
|
|
14825
|
-
const [initError, setInitError] =
|
|
14826
|
-
const [commandHistory, setCommandHistory] =
|
|
14218
|
+
const [isInitialized, setIsInitialized] = useState8(false);
|
|
14219
|
+
const [initError, setInitError] = useState8(null);
|
|
14220
|
+
const [commandHistory, setCommandHistory] = useState8([]);
|
|
14827
14221
|
const imageStoreInitPromise = useRef3(null);
|
|
14828
14222
|
const setStoreSession = useCliStore((state2) => state2.setSession);
|
|
14829
|
-
const
|
|
14830
|
-
const dequeuePermissionPrompt = useCliStore((state2) => state2.dequeuePermissionPrompt);
|
|
14223
|
+
const setPermissionPrompt = useCliStore((state2) => state2.setPermissionPrompt);
|
|
14831
14224
|
const setShowConfigEditor = useCliStore((state2) => state2.setShowConfigEditor);
|
|
14832
14225
|
const setShowMcpViewer = useCliStore((state2) => state2.setShowMcpViewer);
|
|
14833
14226
|
const setExitRequested = useCliStore((state2) => state2.setExitRequested);
|
|
14834
|
-
const performCleanup =
|
|
14227
|
+
const performCleanup = useCallback(async () => {
|
|
14835
14228
|
const cleanupTasks = [];
|
|
14836
14229
|
if (state.session) {
|
|
14837
14230
|
cleanupTasks.push(
|
|
@@ -14866,7 +14259,7 @@ function CliApp() {
|
|
|
14866
14259
|
process.exit(0);
|
|
14867
14260
|
}, 100);
|
|
14868
14261
|
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
14869
|
-
|
|
14262
|
+
useInput8((input, key) => {
|
|
14870
14263
|
if (key.escape) {
|
|
14871
14264
|
if (state.abortController) {
|
|
14872
14265
|
logger.debug("[ABORT] ESC pressed - aborting current operation...");
|
|
@@ -14898,7 +14291,7 @@ function CliApp() {
|
|
|
14898
14291
|
}
|
|
14899
14292
|
}
|
|
14900
14293
|
});
|
|
14901
|
-
const init =
|
|
14294
|
+
const init = useCallback(async () => {
|
|
14902
14295
|
try {
|
|
14903
14296
|
const config = await state.configStore.load();
|
|
14904
14297
|
const history = await state.commandHistoryStore.load();
|
|
@@ -15015,17 +14408,16 @@ function CliApp() {
|
|
|
15015
14408
|
config.tools.disabled || []
|
|
15016
14409
|
// denied tools from project config
|
|
15017
14410
|
);
|
|
15018
|
-
let permissionPromptCounter = 0;
|
|
15019
14411
|
const promptFn = (toolName, args, preview) => {
|
|
15020
14412
|
return new Promise((resolve3) => {
|
|
15021
14413
|
const canBeTrusted = permissionManager.canBeTrusted(toolName);
|
|
15022
|
-
const
|
|
15023
|
-
const prompt = { id, toolName, args, preview, canBeTrusted, resolve: resolve3 };
|
|
14414
|
+
const prompt = { toolName, args, preview, canBeTrusted, resolve: resolve3 };
|
|
15024
14415
|
setState((prev) => ({
|
|
15025
14416
|
...prev,
|
|
15026
|
-
permissionManager
|
|
14417
|
+
permissionManager,
|
|
14418
|
+
permissionPrompt: prompt
|
|
15027
14419
|
}));
|
|
15028
|
-
|
|
14420
|
+
setPermissionPrompt(prompt);
|
|
15029
14421
|
});
|
|
15030
14422
|
};
|
|
15031
14423
|
const agentContext = {
|
|
@@ -15071,17 +14463,7 @@ function CliApp() {
|
|
|
15071
14463
|
agentStore,
|
|
15072
14464
|
enableParallelToolExecution: config.preferences.enableParallelToolExecution === true
|
|
15073
14465
|
});
|
|
15074
|
-
const
|
|
15075
|
-
backgroundManager.setOnStatusChange((job) => {
|
|
15076
|
-
useCliStore.getState().upsertBackgroundAgent(job);
|
|
15077
|
-
});
|
|
15078
|
-
backgroundManager.setOnGroupCompletion((notification, groupDescription) => {
|
|
15079
|
-
useCliStore.getState().addCompletedGroupNotification(notification, groupDescription);
|
|
15080
|
-
useCliStore.getState().setPendingBackgroundTrigger(true);
|
|
15081
|
-
});
|
|
15082
|
-
const agentDelegateTool = createAgentDelegateTool(orchestrator, agentStore, newSession.id, backgroundManager);
|
|
15083
|
-
const backgroundTools = createBackgroundAgentTools(backgroundManager);
|
|
15084
|
-
const notifyingLlm = new NotifyingLlmBackend(llm, backgroundManager);
|
|
14466
|
+
const agentDelegateTool = createAgentDelegateTool(orchestrator, agentStore, newSession.id);
|
|
15085
14467
|
const todoStore = createTodoStore();
|
|
15086
14468
|
const writeTodosTool = createWriteTodosTool(todoStore);
|
|
15087
14469
|
const enableSkillTool = config.preferences.enableSkillTool !== false;
|
|
@@ -15090,14 +14472,13 @@ function CliApp() {
|
|
|
15090
14472
|
subagentOrchestrator: orchestrator,
|
|
15091
14473
|
sessionId: newSession.id
|
|
15092
14474
|
}) : null;
|
|
15093
|
-
const cliTools = [agentDelegateTool,
|
|
14475
|
+
const cliTools = [agentDelegateTool, writeTodosTool];
|
|
15094
14476
|
if (skillTool) {
|
|
15095
14477
|
cliTools.push(skillTool);
|
|
15096
14478
|
}
|
|
15097
14479
|
const allTools = [...b4mTools2, ...mcpTools, ...cliTools];
|
|
15098
14480
|
console.log(`\u{1F4C2} Working directory: ${process.cwd()}`);
|
|
15099
|
-
|
|
15100
|
-
console.log(`\u{1F916} Subagent delegation enabled (${agentNamesList}) + background execution`);
|
|
14481
|
+
console.log(`\u{1F916} Subagent delegation enabled (explore, plan, review)`);
|
|
15101
14482
|
if (skillTool) {
|
|
15102
14483
|
const skillCount = state.customCommandStore.getCommandCount();
|
|
15103
14484
|
if (skillCount > 0) {
|
|
@@ -15134,7 +14515,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
15134
14515
|
const agent = new ReActAgent({
|
|
15135
14516
|
userId: config.userId,
|
|
15136
14517
|
logger: silentLogger,
|
|
15137
|
-
llm
|
|
14518
|
+
llm,
|
|
15138
14519
|
model: modelInfo.id,
|
|
15139
14520
|
tools: allTools,
|
|
15140
14521
|
maxIterations,
|
|
@@ -15186,10 +14567,8 @@ ${contextResult.mergedContent}` : "";
|
|
|
15186
14567
|
// Store orchestrator for step handler updates
|
|
15187
14568
|
agentStore,
|
|
15188
14569
|
// Store agent store for agent management commands
|
|
15189
|
-
contextContent: contextResult.mergedContent
|
|
14570
|
+
contextContent: contextResult.mergedContent
|
|
15190
14571
|
// Store raw context for compact instructions
|
|
15191
|
-
backgroundManager
|
|
15192
|
-
// Store for grouped notification turn tracking
|
|
15193
14572
|
}));
|
|
15194
14573
|
setStoreSession(newSession);
|
|
15195
14574
|
setIsInitialized(true);
|
|
@@ -15203,11 +14582,11 @@ ${contextResult.mergedContent}` : "";
|
|
|
15203
14582
|
setCommandHistory,
|
|
15204
14583
|
setIsInitialized,
|
|
15205
14584
|
setInitError,
|
|
15206
|
-
|
|
14585
|
+
setPermissionPrompt,
|
|
15207
14586
|
setStoreSession,
|
|
15208
14587
|
setState
|
|
15209
14588
|
]);
|
|
15210
|
-
|
|
14589
|
+
useEffect4(() => {
|
|
15211
14590
|
init();
|
|
15212
14591
|
}, [init]);
|
|
15213
14592
|
const handleCustomCommandMessage = async (fullTemplate, displayMessage) => {
|
|
@@ -15283,19 +14662,12 @@ ${contextResult.mergedContent}` : "";
|
|
|
15283
14662
|
content: msg.content
|
|
15284
14663
|
}));
|
|
15285
14664
|
const cliConfig = await state.configStore.get();
|
|
15286
|
-
const
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
signal: abortController.signal,
|
|
15293
|
-
parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
|
|
15294
|
-
isReadOnlyTool
|
|
15295
|
-
});
|
|
15296
|
-
} finally {
|
|
15297
|
-
state.backgroundManager?.setCurrentTurn(null);
|
|
15298
|
-
}
|
|
14665
|
+
const result = await state.agent.run(messageContent, {
|
|
14666
|
+
previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
|
|
14667
|
+
signal: abortController.signal,
|
|
14668
|
+
parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
|
|
14669
|
+
isReadOnlyTool
|
|
14670
|
+
});
|
|
15299
14671
|
const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
|
|
15300
14672
|
if (permissionDenied) {
|
|
15301
14673
|
console.log("\n\u26A0\uFE0F Action denied by user\n");
|
|
@@ -15496,19 +14868,12 @@ ${contextResult.mergedContent}` : "";
|
|
|
15496
14868
|
content: msg.content
|
|
15497
14869
|
}));
|
|
15498
14870
|
const cliConfig = await state.configStore.get();
|
|
15499
|
-
const
|
|
15500
|
-
|
|
15501
|
-
|
|
15502
|
-
|
|
15503
|
-
|
|
15504
|
-
|
|
15505
|
-
signal: abortController.signal,
|
|
15506
|
-
parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
|
|
15507
|
-
isReadOnlyTool
|
|
15508
|
-
});
|
|
15509
|
-
} finally {
|
|
15510
|
-
state.backgroundManager?.setCurrentTurn(null);
|
|
15511
|
-
}
|
|
14871
|
+
const result = await state.agent.run(messageContent, {
|
|
14872
|
+
previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
|
|
14873
|
+
signal: abortController.signal,
|
|
14874
|
+
parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
|
|
14875
|
+
isReadOnlyTool
|
|
14876
|
+
});
|
|
15512
14877
|
const permissionDenied = result.finalAnswer.startsWith("Permission denied for tool");
|
|
15513
14878
|
if (permissionDenied) {
|
|
15514
14879
|
console.log("\n\u26A0\uFE0F Action denied by user\n");
|
|
@@ -15584,88 +14949,7 @@ ${contextResult.mergedContent}` : "";
|
|
|
15584
14949
|
useCliStore.getState().setIsThinking(false);
|
|
15585
14950
|
}
|
|
15586
14951
|
};
|
|
15587
|
-
const
|
|
15588
|
-
if (!state.agent || !state.session) {
|
|
15589
|
-
return;
|
|
15590
|
-
}
|
|
15591
|
-
const authTokens = await state.configStore.getAuthTokens();
|
|
15592
|
-
if (!authTokens || new Date(authTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
15593
|
-
return;
|
|
15594
|
-
}
|
|
15595
|
-
useCliStore.getState().setIsThinking(true);
|
|
15596
|
-
const abortController = new AbortController();
|
|
15597
|
-
setState((prev) => ({ ...prev, abortController }));
|
|
15598
|
-
try {
|
|
15599
|
-
const pendingAssistantMessage = {
|
|
15600
|
-
id: uuidv411(),
|
|
15601
|
-
role: "assistant",
|
|
15602
|
-
content: "...",
|
|
15603
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15604
|
-
metadata: {
|
|
15605
|
-
steps: []
|
|
15606
|
-
}
|
|
15607
|
-
};
|
|
15608
|
-
useCliStore.getState().addPendingMessage(pendingAssistantMessage);
|
|
15609
|
-
const recentMessages = state.session.messages.slice(-20);
|
|
15610
|
-
const previousMessages = recentMessages.filter((msg) => msg.role === "user" || msg.role === "assistant").map((msg) => ({
|
|
15611
|
-
role: msg.role,
|
|
15612
|
-
content: msg.content
|
|
15613
|
-
}));
|
|
15614
|
-
const cliConfig = await state.configStore.get();
|
|
15615
|
-
const result = await state.agent.run(
|
|
15616
|
-
"[System: Background agents have completed. Review and summarize the results.]",
|
|
15617
|
-
{
|
|
15618
|
-
previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
|
|
15619
|
-
signal: abortController.signal,
|
|
15620
|
-
parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
|
|
15621
|
-
isReadOnlyTool
|
|
15622
|
-
}
|
|
15623
|
-
);
|
|
15624
|
-
const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
|
|
15625
|
-
const currentSession = useCliStore.getState().session;
|
|
15626
|
-
if (!currentSession) return;
|
|
15627
|
-
const continuationMessage = {
|
|
15628
|
-
id: uuidv411(),
|
|
15629
|
-
role: "assistant",
|
|
15630
|
-
content: "---\n\n**Background Agent Results:**\n\n" + result.finalAnswer,
|
|
15631
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15632
|
-
metadata: {
|
|
15633
|
-
isContinuation: true,
|
|
15634
|
-
// Flag to skip "Assistant" header in MessageItem
|
|
15635
|
-
steps: result.steps.map(formatStep),
|
|
15636
|
-
tokenUsage: {
|
|
15637
|
-
prompt: 0,
|
|
15638
|
-
completion: 0,
|
|
15639
|
-
total: result.completionInfo.totalTokens
|
|
15640
|
-
}
|
|
15641
|
-
}
|
|
15642
|
-
};
|
|
15643
|
-
const updatedSession = {
|
|
15644
|
-
...currentSession,
|
|
15645
|
-
messages: [...currentSession.messages, continuationMessage],
|
|
15646
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15647
|
-
metadata: {
|
|
15648
|
-
...currentSession.metadata,
|
|
15649
|
-
totalTokens: currentSession.metadata.totalTokens + result.completionInfo.totalTokens,
|
|
15650
|
-
toolCallCount: currentSession.metadata.toolCallCount + successfulToolCalls
|
|
15651
|
-
}
|
|
15652
|
-
};
|
|
15653
|
-
useCliStore.getState().clearPendingMessages();
|
|
15654
|
-
setState((prev) => ({ ...prev, session: updatedSession }));
|
|
15655
|
-
setStoreSession(updatedSession);
|
|
15656
|
-
await state.sessionStore.save(updatedSession);
|
|
15657
|
-
} catch (error) {
|
|
15658
|
-
useCliStore.getState().clearPendingMessages();
|
|
15659
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
15660
|
-
return;
|
|
15661
|
-
}
|
|
15662
|
-
console.error("Error processing background results:", error);
|
|
15663
|
-
} finally {
|
|
15664
|
-
setState((prev) => ({ ...prev, abortController: null }));
|
|
15665
|
-
useCliStore.getState().setIsThinking(false);
|
|
15666
|
-
}
|
|
15667
|
-
};
|
|
15668
|
-
const handleBashCommand = useCallback2(
|
|
14952
|
+
const handleBashCommand = useCallback(
|
|
15669
14953
|
(command) => {
|
|
15670
14954
|
if (!state.session) return;
|
|
15671
14955
|
let output;
|
|
@@ -16640,12 +15924,12 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
16640
15924
|
}
|
|
16641
15925
|
};
|
|
16642
15926
|
if (initError) {
|
|
16643
|
-
return /* @__PURE__ */
|
|
15927
|
+
return /* @__PURE__ */ React19.createElement(Box18, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React19.createElement(Text18, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React19.createElement(Text18, null, initError), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
16644
15928
|
}
|
|
16645
15929
|
if (state.trustLocationSelector) {
|
|
16646
15930
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
16647
15931
|
const inProject = projectDir !== null;
|
|
16648
|
-
return /* @__PURE__ */
|
|
15932
|
+
return /* @__PURE__ */ React19.createElement(
|
|
16649
15933
|
TrustLocationSelector,
|
|
16650
15934
|
{
|
|
16651
15935
|
inProject,
|
|
@@ -16663,7 +15947,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
16663
15947
|
);
|
|
16664
15948
|
}
|
|
16665
15949
|
if (state.rewindSelector && state.session) {
|
|
16666
|
-
return /* @__PURE__ */
|
|
15950
|
+
return /* @__PURE__ */ React19.createElement(
|
|
16667
15951
|
RewindSelector,
|
|
16668
15952
|
{
|
|
16669
15953
|
messages: state.session.messages,
|
|
@@ -16681,7 +15965,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
16681
15965
|
);
|
|
16682
15966
|
}
|
|
16683
15967
|
if (state.sessionSelector) {
|
|
16684
|
-
return /* @__PURE__ */
|
|
15968
|
+
return /* @__PURE__ */ React19.createElement(
|
|
16685
15969
|
SessionSelector,
|
|
16686
15970
|
{
|
|
16687
15971
|
sessions: state.sessionSelector.sessions,
|
|
@@ -16701,7 +15985,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
16701
15985
|
}
|
|
16702
15986
|
if (state.showLoginFlow) {
|
|
16703
15987
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
16704
|
-
return /* @__PURE__ */
|
|
15988
|
+
return /* @__PURE__ */ React19.createElement(
|
|
16705
15989
|
LoginFlow,
|
|
16706
15990
|
{
|
|
16707
15991
|
apiUrl: loginApiUrl,
|
|
@@ -16724,14 +16008,13 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
16724
16008
|
);
|
|
16725
16009
|
}
|
|
16726
16010
|
if (!isInitialized) {
|
|
16727
|
-
return /* @__PURE__ */
|
|
16011
|
+
return /* @__PURE__ */ React19.createElement(Box18, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React19.createElement(Text18, null, "\u{1F680} Initializing..."));
|
|
16728
16012
|
}
|
|
16729
16013
|
const allCommands = mergeCommands(state.customCommandStore.getAllCommands());
|
|
16730
|
-
return /* @__PURE__ */
|
|
16014
|
+
return /* @__PURE__ */ React19.createElement(
|
|
16731
16015
|
App,
|
|
16732
16016
|
{
|
|
16733
16017
|
onMessage: handleMessage,
|
|
16734
|
-
onBackgroundCompletion: handleBackgroundCompletion,
|
|
16735
16018
|
onCommand: handleCommand,
|
|
16736
16019
|
onBashCommand: handleBashCommand,
|
|
16737
16020
|
onImageDetected: handleImageDetected,
|
|
@@ -16746,10 +16029,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
16746
16029
|
},
|
|
16747
16030
|
mcpManager: state.mcpManager ?? void 0,
|
|
16748
16031
|
onPermissionResponse: (response) => {
|
|
16749
|
-
|
|
16750
|
-
|
|
16751
|
-
|
|
16752
|
-
|
|
16032
|
+
if (state.permissionPrompt) {
|
|
16033
|
+
state.permissionPrompt.resolve({ action: response });
|
|
16034
|
+
setState((prev) => ({ ...prev, permissionPrompt: null }));
|
|
16035
|
+
setPermissionPrompt(null);
|
|
16753
16036
|
}
|
|
16754
16037
|
}
|
|
16755
16038
|
}
|
|
@@ -16770,6 +16053,6 @@ var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "d
|
|
|
16770
16053
|
if (isDevMode) {
|
|
16771
16054
|
logger.debug("\u{1F527} Running in development mode (using TypeScript source)\n");
|
|
16772
16055
|
}
|
|
16773
|
-
render(/* @__PURE__ */
|
|
16056
|
+
render(/* @__PURE__ */ React19.createElement(CliApp, null), {
|
|
16774
16057
|
exitOnCtrlC: false
|
|
16775
16058
|
});
|