@menteeai/menteeswe 0.1.7 → 0.1.9
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/cli.js +272 -142
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -107,7 +107,12 @@ function killTree(child) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
function killActiveChild() {
|
|
110
|
-
if (activeChild)
|
|
110
|
+
if (!activeChild) return;
|
|
111
|
+
try {
|
|
112
|
+
activeChild.kill("SIGKILL");
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
killTree(activeChild);
|
|
111
116
|
}
|
|
112
117
|
async function runShellCommand(command, cwd, timeoutMs = 12e4, maxChars = 1e5) {
|
|
113
118
|
return new Promise((resolve) => {
|
|
@@ -246,17 +251,17 @@ function friendlyToolName(name) {
|
|
|
246
251
|
function formatEvent(event) {
|
|
247
252
|
switch (event.type) {
|
|
248
253
|
case "task_started":
|
|
249
|
-
return chalk.bold.magenta(`\u25B8
|
|
254
|
+
return "\n" + chalk.bold.magenta(`\u25B8 Task: ${event.message ?? ""}`);
|
|
250
255
|
case "model_request":
|
|
251
|
-
return chalk.gray("\u25CF
|
|
256
|
+
return chalk.gray("\u25CF thinking...");
|
|
252
257
|
case "info":
|
|
253
|
-
return chalk.cyan.bold(`\u{1F4AD}
|
|
258
|
+
return chalk.cyan.bold(`\u{1F4AD} ${event.message ?? ""}`);
|
|
254
259
|
case "tool_started": {
|
|
255
260
|
const tool = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
256
261
|
const color = CATEGORY_COLOR[TOOL_CATEGORY[tool] ?? "other"];
|
|
257
262
|
const friendly = friendlyToolName(tool);
|
|
258
263
|
const preview = tool && event.message?.startsWith(tool) ? event.message.slice(tool.length).trim() : event.message ?? "";
|
|
259
|
-
return color(`\u2699
|
|
264
|
+
return color(`\u2699 ${friendly} (${preview})...`);
|
|
260
265
|
}
|
|
261
266
|
case "tool_completed": {
|
|
262
267
|
const tool = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
@@ -264,14 +269,32 @@ function formatEvent(event) {
|
|
|
264
269
|
const output = typeof event.data?.output === "string" ? event.data.output.trim() : "";
|
|
265
270
|
const firstLine = (output.split("\n")[0] ?? event.message ?? "").slice(0, 160);
|
|
266
271
|
const color = success ? CATEGORY_COLOR[TOOL_CATEGORY[tool] ?? "other"] : chalk.red;
|
|
267
|
-
return color(` ${success ? "\u2713" : "\u2717"}
|
|
272
|
+
return color(` ${success ? "\u2713" : "\u2717"} ${firstLine}`);
|
|
268
273
|
}
|
|
269
274
|
case "approval":
|
|
270
|
-
return chalk.magenta(`\u{1F510}
|
|
275
|
+
return chalk.magenta(`\u{1F510} Asking permission to use ${event.message ?? "tool"}...`);
|
|
271
276
|
case "warning":
|
|
272
|
-
return chalk.yellow(`\u26A0
|
|
277
|
+
return chalk.yellow(`\u26A0 ${event.message ?? ""}`);
|
|
273
278
|
case "error":
|
|
274
|
-
return chalk.red(`\u2716
|
|
279
|
+
return chalk.red(`\u2716 ${event.message ?? ""}`);
|
|
280
|
+
case "patch": {
|
|
281
|
+
const filePath = typeof event.data?.path === "string" ? event.data.path : "file";
|
|
282
|
+
const oldStr = typeof event.data?.old_string === "string" ? event.data.old_string : "";
|
|
283
|
+
const newStr = typeof event.data?.new_string === "string" ? event.data.new_string : "";
|
|
284
|
+
const MAX = 60;
|
|
285
|
+
const oldLines = oldStr.split(/\r?\n/);
|
|
286
|
+
const newLines = newStr.split(/\r?\n/);
|
|
287
|
+
const out = [chalk.bold.magenta(`\u270E ${filePath}`)];
|
|
288
|
+
for (const line of oldLines.slice(0, MAX)) {
|
|
289
|
+
out.push(chalk.bgRed.white(` - ${line}`));
|
|
290
|
+
}
|
|
291
|
+
if (oldLines.length > MAX) out.push(chalk.bgRed.white(` \u2026 ${oldLines.length - MAX} more removed line(s)`));
|
|
292
|
+
for (const line of newLines.slice(0, MAX)) {
|
|
293
|
+
out.push(chalk.bgGreen.black(` + ${line}`));
|
|
294
|
+
}
|
|
295
|
+
if (newLines.length > MAX) out.push(chalk.bgGreen.black(` \u2026 ${newLines.length - MAX} more added line(s)`));
|
|
296
|
+
return out.join("\n");
|
|
297
|
+
}
|
|
275
298
|
case "task_completed": {
|
|
276
299
|
const success = event.data?.success === true;
|
|
277
300
|
const usage = event.data?.usage;
|
|
@@ -292,10 +315,12 @@ function formatEvent(event) {
|
|
|
292
315
|
if (modifiedFiles.length > 0) stats.push(`files ${modifiedFiles.length}`);
|
|
293
316
|
const sep = chalk.dim("\u2500".repeat(48));
|
|
294
317
|
const statsLine = chalk.cyan(` ${stats.join(" \xB7 ")}`);
|
|
295
|
-
const
|
|
318
|
+
const cancelled = event.data?.cancelled === true;
|
|
319
|
+
const statusLine = cancelled ? chalk.yellow.bold("\u2716 Task cancelled") : success ? chalk.green.bold("\u2714 Task completed") : chalk.red.bold("\u2716 Task failed");
|
|
296
320
|
const filesLine = modifiedFiles.length > 0 ? chalk.cyan.dim(` \u{1F4DD} ${modifiedFiles.length} file(s): ${modifiedFiles.join(", ")}`) : "";
|
|
297
321
|
const tipLine = success ? chalk.dim(" Next: type a new task, or /help for commands") : chalk.yellow.dim(" The task did not finish \u2014 check the steps above and retry");
|
|
298
|
-
const
|
|
322
|
+
const detailsLine = toolCalls > 0 ? chalk.dim(` \u{1F50D} press d to toggle ${toolCalls} tool call(s)`) : "";
|
|
323
|
+
const block = [sep, statusLine, statsLine, filesLine, tipLine, detailsLine, sep, ""];
|
|
299
324
|
return block.filter((line) => line !== "").join("\n");
|
|
300
325
|
}
|
|
301
326
|
default:
|
|
@@ -459,6 +484,9 @@ function buildSystemPrompt(cwd) {
|
|
|
459
484
|
const tree = projectTree(cwd).trim() || "(empty)";
|
|
460
485
|
return `You are MenteE, an autonomous software-engineering agent working inside a repository on the user's machine.
|
|
461
486
|
|
|
487
|
+
# Your design philosophy
|
|
488
|
+
You are built around a tool harness: every meaningful action (reading, searching, editing, running, git) goes through a tool. Use the harness decisively and efficiently. Prefer precise tool calls over long prose so token usage stays very low while your engineering stays sharp and intelligent. A good session does more with fewer tokens \u2014 call exactly the tool needed, read only what you must, and let the harness do the heavy lifting.
|
|
489
|
+
|
|
462
490
|
# Environment
|
|
463
491
|
- Workspace: ${cwd}
|
|
464
492
|
- Platform: ${os2.platform()} (${os2.release()}), shell commands run with the system shell
|
|
@@ -479,6 +507,8 @@ ${tree}
|
|
|
479
507
|
# Tool usage notes
|
|
480
508
|
- Call only the tools needed for the immediate next step. Do NOT read files you already understand, and do NOT read build/config files (e.g. *.config.ts, tsconfig.json) unless the task specifically needs them.
|
|
481
509
|
- Prefer search_code over reading many files.
|
|
510
|
+
- NEVER start long-running servers, dev servers, or watchers (e.g. 'npm run dev', 'npm start', 'vite', 'npm run watch', 'python -m http.server') yourself. These block and never exit. If the user needs to run the app, give them the exact command to run in their OWN terminal. Only run commands that exit on their own.
|
|
511
|
+
- If a task result requires running the app to verify, say so and provide the command \u2014 do not run it for them.
|
|
482
512
|
|
|
483
513
|
# Research strategy (IMPORTANT \u2014 HARD RULE)
|
|
484
514
|
- The file tree above already shows the whole structure. Do NOT read files just to "see what's there".
|
|
@@ -631,11 +661,11 @@ function isRateLimitError(error) {
|
|
|
631
661
|
}
|
|
632
662
|
return /\b429\b|max rpm|rate limit|too many requests/i.test(error.message);
|
|
633
663
|
}
|
|
634
|
-
async function generateWithRetry(provider, model, system, messages, tools, bus) {
|
|
664
|
+
async function generateWithRetry(provider, model, system, messages, tools, bus, onToken) {
|
|
635
665
|
let lastError = null;
|
|
636
666
|
for (let attempt = 0; attempt <= RATE_LIMIT_MAX_ATTEMPTS; attempt++) {
|
|
637
667
|
try {
|
|
638
|
-
return await provider.generate({ system, messages, tools: tools.schemas() }, model);
|
|
668
|
+
return await provider.generate({ system, messages, tools: tools.schemas(), onToken }, model);
|
|
639
669
|
} catch (error) {
|
|
640
670
|
lastError = error;
|
|
641
671
|
if (attempt >= RATE_LIMIT_MAX_ATTEMPTS || !isRateLimitError(lastError)) {
|
|
@@ -719,10 +749,24 @@ ${systemExtra}` : "");
|
|
|
719
749
|
state.iteration += 1;
|
|
720
750
|
state.usage.modelRequests += 1;
|
|
721
751
|
bus.emit("model_request", `iteration ${state.iteration}/${maxIterations}`);
|
|
752
|
+
let thinkBuf = "";
|
|
753
|
+
let thinkTimer = null;
|
|
754
|
+
const flushThink = () => {
|
|
755
|
+
if (thinkBuf) {
|
|
756
|
+
bus.emit("thinking", thinkBuf);
|
|
757
|
+
thinkBuf = "";
|
|
758
|
+
}
|
|
759
|
+
thinkTimer = null;
|
|
760
|
+
};
|
|
761
|
+
const onToken = (delta) => {
|
|
762
|
+
thinkBuf += delta;
|
|
763
|
+
if (thinkTimer == null) thinkTimer = setTimeout(flushThink, 80);
|
|
764
|
+
};
|
|
722
765
|
let response;
|
|
723
766
|
try {
|
|
724
|
-
response = await generateWithRetry(provider, model, system, messages, tools, bus);
|
|
767
|
+
response = await generateWithRetry(provider, model, system, messages, tools, bus, onToken);
|
|
725
768
|
} catch (error) {
|
|
769
|
+
flushThink();
|
|
726
770
|
const message = error.message;
|
|
727
771
|
let hint = "";
|
|
728
772
|
if (/insufficient balance|no resource package|recharge/i.test(message)) {
|
|
@@ -744,6 +788,7 @@ ${systemExtra}` : "");
|
|
|
744
788
|
});
|
|
745
789
|
return { success: false, finalText, state };
|
|
746
790
|
}
|
|
791
|
+
flushThink();
|
|
747
792
|
bus.emit(
|
|
748
793
|
"model_response",
|
|
749
794
|
void 0,
|
|
@@ -772,7 +817,7 @@ ${systemExtra}` : "");
|
|
|
772
817
|
content: response.content,
|
|
773
818
|
tool_calls: response.toolCalls
|
|
774
819
|
});
|
|
775
|
-
if (response.content && response.content.trim()) {
|
|
820
|
+
if (!onToken && response.content && response.content.trim()) {
|
|
776
821
|
bus.emit("info", response.content.trim().slice(0, 300));
|
|
777
822
|
}
|
|
778
823
|
for (const call of response.toolCalls) {
|
|
@@ -866,6 +911,13 @@ ${systemExtra}` : "");
|
|
|
866
911
|
success: result.success,
|
|
867
912
|
output: result.output.slice(0, 2e3)
|
|
868
913
|
});
|
|
914
|
+
if (tool.name === "apply_patch" && result.success && result.data?.kind === "patch") {
|
|
915
|
+
bus.emit("patch", void 0, {
|
|
916
|
+
path: result.data.path,
|
|
917
|
+
old_string: result.data.old_string,
|
|
918
|
+
new_string: result.data.new_string
|
|
919
|
+
});
|
|
920
|
+
}
|
|
869
921
|
messages.push({
|
|
870
922
|
role: "tool",
|
|
871
923
|
tool_call_id: call.id,
|
|
@@ -894,7 +946,8 @@ ${systemExtra}` : "");
|
|
|
894
946
|
toolCalls: state.totalToolCalls,
|
|
895
947
|
modifiedFiles: state.modifiedFiles,
|
|
896
948
|
usage: state.usage,
|
|
897
|
-
durationMs: Date.now() - state.startedAt
|
|
949
|
+
durationMs: Date.now() - state.startedAt,
|
|
950
|
+
cancelled: signal?.aborted === true
|
|
898
951
|
});
|
|
899
952
|
return { success, finalText, state };
|
|
900
953
|
}
|
|
@@ -1079,7 +1132,7 @@ var init_KeyDialog = __esm({
|
|
|
1079
1132
|
var version;
|
|
1080
1133
|
var init_package = __esm({
|
|
1081
1134
|
"package.json"() {
|
|
1082
|
-
version = "0.1.
|
|
1135
|
+
version = "0.1.9";
|
|
1083
1136
|
}
|
|
1084
1137
|
});
|
|
1085
1138
|
|
|
@@ -1089,11 +1142,12 @@ __export(App_exports, {
|
|
|
1089
1142
|
App: () => App
|
|
1090
1143
|
});
|
|
1091
1144
|
import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
|
|
1092
|
-
import { Box as Box4, Static, Text as Text4, useApp, useInput as useInput4 } from "ink";
|
|
1145
|
+
import { Box as Box4, Static, Text as Text4, useApp, useInput as useInput4, useStdout } from "ink";
|
|
1093
1146
|
import TextInput2 from "ink-text-input";
|
|
1094
1147
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1095
1148
|
function App(props) {
|
|
1096
1149
|
const { exit } = useApp();
|
|
1150
|
+
const { stdout } = useStdout();
|
|
1097
1151
|
const bus = useMemo2(() => new EventBus(), []);
|
|
1098
1152
|
const bridge = useMemo2(() => new ApprovalBridge(), []);
|
|
1099
1153
|
const [log, setLog] = useState3([]);
|
|
@@ -1116,24 +1170,31 @@ function App(props) {
|
|
|
1116
1170
|
const [toolHistory, setToolHistory] = useState3([]);
|
|
1117
1171
|
const [inspectorIndex, setInspectorIndex] = useState3(null);
|
|
1118
1172
|
const [detailsOpen, setDetailsOpen] = useState3(false);
|
|
1119
|
-
const [
|
|
1173
|
+
const [showDetails, setShowDetails] = useState3(false);
|
|
1174
|
+
const [thinkingText, setThinkingText] = useState3("");
|
|
1120
1175
|
const pendingTool = useRef(null);
|
|
1121
1176
|
const counter = useRef(0);
|
|
1122
1177
|
const stateRef = useRef({ running, approval, dialog, factoryResult, providerName, model });
|
|
1123
1178
|
stateRef.current = { running, approval, dialog, factoryResult, providerName, model };
|
|
1124
1179
|
const abortRef = useRef(null);
|
|
1125
|
-
const appendLog = (text) => {
|
|
1180
|
+
const appendLog = (text, noise = false) => {
|
|
1126
1181
|
if (!text) return;
|
|
1127
1182
|
counter.current += 1;
|
|
1128
|
-
const line = { id: counter.current, text };
|
|
1183
|
+
const line = { id: counter.current, text, noise };
|
|
1129
1184
|
setLog((prev) => [...prev, line]);
|
|
1130
1185
|
};
|
|
1131
1186
|
useEffect2(() => {
|
|
1132
|
-
const
|
|
1187
|
+
const TOOL_NOISE = /* @__PURE__ */ new Set(["tool_started", "tool_completed", "model_request"]);
|
|
1133
1188
|
const unsubscribeLog = bus.subscribe((event) => {
|
|
1134
|
-
if (
|
|
1135
|
-
|
|
1189
|
+
if (event.type === "thinking") {
|
|
1190
|
+
const delta = (event.message ?? "").replace(/[\r\n]+/g, " ");
|
|
1191
|
+
if (delta) setThinkingText((prev) => (prev + delta).slice(-4e3));
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (event.type === "model_request" || event.type === "task_completed") {
|
|
1195
|
+
setThinkingText("");
|
|
1136
1196
|
}
|
|
1197
|
+
appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
|
|
1137
1198
|
if (event.type === "tool_started") {
|
|
1138
1199
|
const name = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
1139
1200
|
const target = typeof event.message === "string" ? event.message : "";
|
|
@@ -1166,7 +1227,7 @@ function App(props) {
|
|
|
1166
1227
|
unsubscribeLog();
|
|
1167
1228
|
unsubscribeLog2();
|
|
1168
1229
|
};
|
|
1169
|
-
}, [bus, bridge,
|
|
1230
|
+
}, [bus, bridge, showDetails]);
|
|
1170
1231
|
useEffect2(() => {
|
|
1171
1232
|
if (!running) return;
|
|
1172
1233
|
const timer = setInterval(() => setFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
|
|
@@ -1185,8 +1246,11 @@ function App(props) {
|
|
|
1185
1246
|
return;
|
|
1186
1247
|
}
|
|
1187
1248
|
const timer = setTimeout(() => {
|
|
1188
|
-
|
|
1189
|
-
|
|
1249
|
+
const rest = finalPending.slice(typedText.length);
|
|
1250
|
+
const match = /^(\S+\s*)/.exec(rest);
|
|
1251
|
+
const step = match ? match[0].length : 1;
|
|
1252
|
+
setTypedText(finalPending.slice(0, typedText.length + step));
|
|
1253
|
+
}, 55);
|
|
1190
1254
|
return () => clearTimeout(timer);
|
|
1191
1255
|
}, [typing, finalPending, typedText]);
|
|
1192
1256
|
const updateConfig = (mutate) => {
|
|
@@ -1331,6 +1395,7 @@ function App(props) {
|
|
|
1331
1395
|
setToolHistory([]);
|
|
1332
1396
|
setInspectorIndex(null);
|
|
1333
1397
|
setDetailsOpen(false);
|
|
1398
|
+
setShowDetails(false);
|
|
1334
1399
|
setRunning(true);
|
|
1335
1400
|
const ac = new AbortController();
|
|
1336
1401
|
abortRef.current = ac;
|
|
@@ -1384,6 +1449,7 @@ function App(props) {
|
|
|
1384
1449
|
useInput4((input, key) => {
|
|
1385
1450
|
if (key.ctrl && input === "c") {
|
|
1386
1451
|
if (stateRef.current.running) {
|
|
1452
|
+
appendLog("\u23F9 Interrupted by you \u2014 stopping the current task and returning to the prompt.");
|
|
1387
1453
|
killActiveChild();
|
|
1388
1454
|
abortRef.current?.abort();
|
|
1389
1455
|
return;
|
|
@@ -1392,12 +1458,14 @@ function App(props) {
|
|
|
1392
1458
|
setTimeout(() => process.exit(0), 50);
|
|
1393
1459
|
return;
|
|
1394
1460
|
}
|
|
1461
|
+
if (key.meta && (input === "d" || input === "D")) {
|
|
1462
|
+
setShowDetails((v) => !v);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1395
1465
|
if (stateRef.current.dialog || stateRef.current.approval || stateRef.current.running) return;
|
|
1396
1466
|
if (key.meta && (input === "m" || input === "M")) openModelDialog();
|
|
1397
1467
|
else if (key.ctrl && input === "p") setDialog({ kind: "provider" });
|
|
1398
1468
|
else if (key.ctrl && input === "k") setDialog({ kind: "key" });
|
|
1399
|
-
else if (input === "d" || input === "D") cycleInspector();
|
|
1400
|
-
else if (input === "t" || input === "T") setShowTools((v) => !v);
|
|
1401
1469
|
});
|
|
1402
1470
|
const modelLabel = model ?? factoryResult.provider?.defaultModel ?? (factoryResult.error ? "no key" : "");
|
|
1403
1471
|
const header = /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
@@ -1408,114 +1476,117 @@ function App(props) {
|
|
|
1408
1476
|
" \xB7 ",
|
|
1409
1477
|
providerName,
|
|
1410
1478
|
modelLabel ? `:${modelLabel}` : "",
|
|
1411
|
-
" \xB7
|
|
1412
|
-
|
|
1479
|
+
" \xB7 details ",
|
|
1480
|
+
showDetails ? "open" : "collapsed",
|
|
1413
1481
|
" \xB7 ",
|
|
1414
1482
|
props.cwd
|
|
1415
1483
|
] }),
|
|
1416
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7
|
|
1484
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
|
|
1417
1485
|
] }, "header");
|
|
1486
|
+
const cols = stdout.columns || 80;
|
|
1487
|
+
const marquee = thinkingText.slice(-Math.max(1, cols - 4));
|
|
1418
1488
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1419
|
-
/* @__PURE__ */ jsx4(
|
|
1420
|
-
|
|
1421
|
-
{
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
" "
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
error: modelError,
|
|
1463
|
-
current: model ?? factoryResult.provider?.defaultModel,
|
|
1464
|
-
onSelect: (id) => {
|
|
1465
|
-
setDialog(null);
|
|
1466
|
-
setModelAndPersist(id);
|
|
1467
|
-
},
|
|
1468
|
-
onClose: () => setDialog(null)
|
|
1469
|
-
}
|
|
1470
|
-
) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx4(
|
|
1471
|
-
SelectDialog,
|
|
1472
|
-
{
|
|
1473
|
-
title: "Provider",
|
|
1474
|
-
items: PROVIDER_NAMES,
|
|
1475
|
-
descriptions: PROVIDER_DESCRIPTIONS,
|
|
1476
|
-
current: providerName,
|
|
1477
|
-
onSelect: (name) => {
|
|
1478
|
-
setDialog(null);
|
|
1479
|
-
switchProvider(name);
|
|
1480
|
-
},
|
|
1481
|
-
onClose: () => setDialog(null)
|
|
1482
|
-
}
|
|
1483
|
-
) : dialog?.kind === "key" ? /* @__PURE__ */ jsx4(
|
|
1484
|
-
KeyDialog,
|
|
1485
|
-
{
|
|
1486
|
-
providerName,
|
|
1487
|
-
onSubmit: (value) => {
|
|
1488
|
-
setDialog(null);
|
|
1489
|
-
saveKey(value);
|
|
1490
|
-
},
|
|
1491
|
-
onClose: () => setDialog(null)
|
|
1492
|
-
}
|
|
1493
|
-
) : approval ? /* @__PURE__ */ jsx4(
|
|
1494
|
-
ApprovalPrompt,
|
|
1495
|
-
{
|
|
1496
|
-
request: approval,
|
|
1497
|
-
onDecision: (decision) => {
|
|
1498
|
-
bridge.resolve(decision);
|
|
1499
|
-
if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
|
|
1489
|
+
/* @__PURE__ */ jsx4(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx4(Box4, { children: item.text }, item.key) }),
|
|
1490
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1491
|
+
log.filter((l) => showDetails || !l.noise).map((line) => /* @__PURE__ */ jsx4(Text4, { children: line.text }, line.id)),
|
|
1492
|
+
finalPending !== null ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
1493
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", children: typedText }),
|
|
1494
|
+
typing ? /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u258C" }) : null
|
|
1495
|
+
] }) : null,
|
|
1496
|
+
detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
|
|
1497
|
+
const rec = toolHistory[inspectorIndex];
|
|
1498
|
+
const outLines = rec.output.split("\n").slice(0, 24).join("\n");
|
|
1499
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
|
|
1500
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
1501
|
+
" tool details (",
|
|
1502
|
+
inspectorIndex + 1,
|
|
1503
|
+
"/",
|
|
1504
|
+
toolHistory.length,
|
|
1505
|
+
") \xB7 press d to cycle "
|
|
1506
|
+
] }),
|
|
1507
|
+
/* @__PURE__ */ jsxs4(Text4, { color: toolColorName(rec.name), bold: true, children: [
|
|
1508
|
+
rec.name,
|
|
1509
|
+
" ",
|
|
1510
|
+
rec.target
|
|
1511
|
+
] }),
|
|
1512
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
1513
|
+
"args: ",
|
|
1514
|
+
rec.args.slice(0, 400) || "(none)"
|
|
1515
|
+
] }),
|
|
1516
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: outLines || "(no output)" })
|
|
1517
|
+
] });
|
|
1518
|
+
})() : null,
|
|
1519
|
+
dialog?.kind === "model" ? /* @__PURE__ */ jsx4(
|
|
1520
|
+
SelectDialog,
|
|
1521
|
+
{
|
|
1522
|
+
title: `Model \u2014 ${providerName}`,
|
|
1523
|
+
items: modelItems,
|
|
1524
|
+
loading: modelLoading,
|
|
1525
|
+
error: modelError,
|
|
1526
|
+
current: model ?? factoryResult.provider?.defaultModel,
|
|
1527
|
+
onSelect: (id) => {
|
|
1528
|
+
setDialog(null);
|
|
1529
|
+
setModelAndPersist(id);
|
|
1530
|
+
},
|
|
1531
|
+
onClose: () => setDialog(null)
|
|
1500
1532
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
|
|
1504
|
-
SPINNER_FRAMES[frame],
|
|
1505
|
-
" "
|
|
1506
|
-
] }),
|
|
1507
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
|
|
1508
|
-
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1509
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u25B8 " }),
|
|
1510
|
-
/* @__PURE__ */ jsx4(
|
|
1511
|
-
TextInput2,
|
|
1533
|
+
) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx4(
|
|
1534
|
+
SelectDialog,
|
|
1512
1535
|
{
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1536
|
+
title: "Provider",
|
|
1537
|
+
items: PROVIDER_NAMES,
|
|
1538
|
+
descriptions: PROVIDER_DESCRIPTIONS,
|
|
1539
|
+
current: providerName,
|
|
1540
|
+
onSelect: (name) => {
|
|
1541
|
+
setDialog(null);
|
|
1542
|
+
switchProvider(name);
|
|
1543
|
+
},
|
|
1544
|
+
onClose: () => setDialog(null)
|
|
1517
1545
|
}
|
|
1518
|
-
)
|
|
1546
|
+
) : dialog?.kind === "key" ? /* @__PURE__ */ jsx4(
|
|
1547
|
+
KeyDialog,
|
|
1548
|
+
{
|
|
1549
|
+
providerName,
|
|
1550
|
+
onSubmit: (value) => {
|
|
1551
|
+
setDialog(null);
|
|
1552
|
+
saveKey(value);
|
|
1553
|
+
},
|
|
1554
|
+
onClose: () => setDialog(null)
|
|
1555
|
+
}
|
|
1556
|
+
) : approval ? /* @__PURE__ */ jsx4(
|
|
1557
|
+
ApprovalPrompt,
|
|
1558
|
+
{
|
|
1559
|
+
request: approval,
|
|
1560
|
+
onDecision: (decision) => {
|
|
1561
|
+
bridge.resolve(decision);
|
|
1562
|
+
if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
) : running ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1567
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
|
|
1568
|
+
SPINNER_FRAMES[frame],
|
|
1569
|
+
" "
|
|
1570
|
+
] }),
|
|
1571
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
|
|
1572
|
+
] }),
|
|
1573
|
+
thinkingText ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1574
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u{1F4AD} " }),
|
|
1575
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", dimColor: true, children: marquee }),
|
|
1576
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u258C" })
|
|
1577
|
+
] }) : null
|
|
1578
|
+
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1579
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u25B8 " }),
|
|
1580
|
+
/* @__PURE__ */ jsx4(
|
|
1581
|
+
TextInput2,
|
|
1582
|
+
{
|
|
1583
|
+
value: taskInput,
|
|
1584
|
+
onChange: setTaskInput,
|
|
1585
|
+
onSubmit: handleSubmit,
|
|
1586
|
+
placeholder: "Describe a task, or /help"
|
|
1587
|
+
}
|
|
1588
|
+
)
|
|
1589
|
+
] })
|
|
1519
1590
|
] })
|
|
1520
1591
|
] });
|
|
1521
1592
|
}
|
|
@@ -1586,6 +1657,20 @@ function toWireTools(tools) {
|
|
|
1586
1657
|
}
|
|
1587
1658
|
}));
|
|
1588
1659
|
}
|
|
1660
|
+
function parseResponse(response) {
|
|
1661
|
+
const choice = response.choices?.[0];
|
|
1662
|
+
const message = choice?.message;
|
|
1663
|
+
const usage = response.usage ? {
|
|
1664
|
+
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
1665
|
+
outputTokens: response.usage.completion_tokens ?? 0
|
|
1666
|
+
} : void 0;
|
|
1667
|
+
return {
|
|
1668
|
+
content: message?.content ?? null,
|
|
1669
|
+
toolCalls: message?.tool_calls ?? [],
|
|
1670
|
+
finishReason: mapFinishReason(choice?.finish_reason),
|
|
1671
|
+
usage
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1589
1674
|
var OpenAICompatProvider = class {
|
|
1590
1675
|
name;
|
|
1591
1676
|
defaultModel;
|
|
@@ -1603,26 +1688,71 @@ var OpenAICompatProvider = class {
|
|
|
1603
1688
|
{ role: "system", content: request.system },
|
|
1604
1689
|
...request.messages
|
|
1605
1690
|
];
|
|
1606
|
-
const
|
|
1691
|
+
const baseParams = {
|
|
1607
1692
|
model: model ?? this.defaultModel,
|
|
1608
1693
|
// the wire format matches our internal shape; keep loose typing at the boundary
|
|
1609
1694
|
messages,
|
|
1610
1695
|
...request.tools.length > 0 ? { tools: toWireTools(request.tools) } : {},
|
|
1611
1696
|
...request.temperature !== void 0 ? { temperature: request.temperature } : {},
|
|
1612
1697
|
...request.maxTokens ? { max_tokens: request.maxTokens } : {}
|
|
1613
|
-
});
|
|
1614
|
-
const choice = response.choices[0];
|
|
1615
|
-
const message = choice?.message;
|
|
1616
|
-
const usage = response.usage ? {
|
|
1617
|
-
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
1618
|
-
outputTokens: response.usage.completion_tokens ?? 0
|
|
1619
|
-
} : void 0;
|
|
1620
|
-
return {
|
|
1621
|
-
content: message?.content ?? null,
|
|
1622
|
-
toolCalls: message?.tool_calls ?? [],
|
|
1623
|
-
finishReason: mapFinishReason(choice?.finish_reason),
|
|
1624
|
-
usage
|
|
1625
1698
|
};
|
|
1699
|
+
if (!request.onToken) {
|
|
1700
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
1701
|
+
return parseResponse(response);
|
|
1702
|
+
}
|
|
1703
|
+
try {
|
|
1704
|
+
const stream = await this.client.chat.completions.create({
|
|
1705
|
+
...baseParams,
|
|
1706
|
+
stream: true,
|
|
1707
|
+
stream_options: { include_usage: true }
|
|
1708
|
+
});
|
|
1709
|
+
let content = "";
|
|
1710
|
+
let finishReason = "stop";
|
|
1711
|
+
let usage;
|
|
1712
|
+
const tcMap = /* @__PURE__ */ new Map();
|
|
1713
|
+
for await (const chunk of stream) {
|
|
1714
|
+
const choice = chunk.choices?.[0];
|
|
1715
|
+
const delta = choice?.delta;
|
|
1716
|
+
if (delta?.content) {
|
|
1717
|
+
content += delta.content;
|
|
1718
|
+
request.onToken(delta.content);
|
|
1719
|
+
}
|
|
1720
|
+
if (delta?.tool_calls) {
|
|
1721
|
+
for (const tc of delta.tool_calls) {
|
|
1722
|
+
const idx = tc.index ?? 0;
|
|
1723
|
+
let acc = tcMap.get(idx);
|
|
1724
|
+
if (!acc) {
|
|
1725
|
+
acc = { args: "" };
|
|
1726
|
+
tcMap.set(idx, acc);
|
|
1727
|
+
}
|
|
1728
|
+
if (tc.id) acc.id = tc.id;
|
|
1729
|
+
if (tc.function?.name) acc.name = tc.function.name;
|
|
1730
|
+
if (tc.function?.arguments) acc.args += tc.function.arguments;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1734
|
+
if (chunk.usage) {
|
|
1735
|
+
usage = {
|
|
1736
|
+
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
1737
|
+
outputTokens: chunk.usage.completion_tokens ?? 0
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
const toolCalls = [...tcMap.entries()].sort((a, b) => a[0] - b[0]).map(([idx, acc]) => ({
|
|
1742
|
+
id: acc.id ?? `call_${idx}`,
|
|
1743
|
+
type: "function",
|
|
1744
|
+
function: { name: acc.name ?? "", arguments: acc.args }
|
|
1745
|
+
}));
|
|
1746
|
+
return {
|
|
1747
|
+
content,
|
|
1748
|
+
toolCalls,
|
|
1749
|
+
finishReason: mapFinishReason(finishReason),
|
|
1750
|
+
usage
|
|
1751
|
+
};
|
|
1752
|
+
} catch {
|
|
1753
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
1754
|
+
return parseResponse(response);
|
|
1755
|
+
}
|
|
1626
1756
|
}
|
|
1627
1757
|
async listModels() {
|
|
1628
1758
|
const page = await this.client.models.list();
|
package/package.json
CHANGED