@modeloslab/modelcode 0.1.4 → 0.1.8
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.mjs
CHANGED
|
@@ -48,11 +48,12 @@ import {
|
|
|
48
48
|
removeCard,
|
|
49
49
|
removeJob,
|
|
50
50
|
renderBoard,
|
|
51
|
-
|
|
51
|
+
renderDiffPlain,
|
|
52
52
|
route,
|
|
53
53
|
runProc,
|
|
54
54
|
runSubagent,
|
|
55
55
|
runTeam,
|
|
56
|
+
saveConfig,
|
|
56
57
|
searchFacts,
|
|
57
58
|
searchSessions,
|
|
58
59
|
shellInvocation,
|
|
@@ -64,7 +65,7 @@ import {
|
|
|
64
65
|
validateMnemonic,
|
|
65
66
|
walletFromMnemonic,
|
|
66
67
|
which
|
|
67
|
-
} from "./main-
|
|
68
|
+
} from "./main-pj08pbgh.mjs";
|
|
68
69
|
import"./main-p2xnn95s.mjs";
|
|
69
70
|
import {
|
|
70
71
|
__require
|
|
@@ -93,6 +94,7 @@ var ConfigSchema = exports_external.object({
|
|
|
93
94
|
autoSummary: exports_external.boolean().default(true),
|
|
94
95
|
theme: exports_external.enum(["amber", "mono", "ocean"]).default("amber"),
|
|
95
96
|
teamMemoryUrl: exports_external.string().optional(),
|
|
97
|
+
autoFixLaunchErrors: exports_external.boolean().default(false),
|
|
96
98
|
spendCapMdl: exports_external.number().default(0),
|
|
97
99
|
providers: exports_external.record(exports_external.object({ baseUrl: exports_external.string(), apiKey: exports_external.string().optional(), model: exports_external.string().optional() })).default({})
|
|
98
100
|
});
|
|
@@ -119,7 +121,7 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
119
121
|
raw.apiKey = envKey;
|
|
120
122
|
return ConfigSchema.parse(raw);
|
|
121
123
|
}
|
|
122
|
-
function
|
|
124
|
+
function saveConfig2(patch) {
|
|
123
125
|
const p = join(globalDir2(), "config.json");
|
|
124
126
|
let cur = {};
|
|
125
127
|
try {
|
|
@@ -155,7 +157,7 @@ var bash = {
|
|
|
155
157
|
async run(args, ctx) {
|
|
156
158
|
const cmd = String(args.command ?? "");
|
|
157
159
|
const timeout = Number(args.timeout_ms) || 120000;
|
|
158
|
-
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, onChunk: (s) => ctx.onStream?.(s) });
|
|
160
|
+
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, signal: ctx.signal, onChunk: (s) => ctx.onStream?.(s) });
|
|
159
161
|
return `exit ${r.code}
|
|
160
162
|
${(r.stdout + (r.stderr ? `
|
|
161
163
|
[stderr]
|
|
@@ -226,7 +228,7 @@ var edit = {
|
|
|
226
228
|
const updated = args.replace_all ? src.split(oldS).join(newS) : src.replace(oldS, newS);
|
|
227
229
|
writeFileSync2(p, updated);
|
|
228
230
|
return `edited ${args.path} (${count} replacement${count === 1 ? "" : "s"})
|
|
229
|
-
${
|
|
231
|
+
${renderDiffPlain(oldS, newS)}`;
|
|
230
232
|
}
|
|
231
233
|
};
|
|
232
234
|
var glob = {
|
|
@@ -379,6 +381,15 @@ Tool discipline (important): call each tool ONCE for a given action — never re
|
|
|
379
381
|
A tool result is authoritative: once a write/edit succeeds, trust it and move on. Do NOT claim a task is
|
|
380
382
|
done in the same message where you call the tool to do it — call the tool first, then confirm only after
|
|
381
383
|
you see its result. Keep confirmations to one short sentence.`;
|
|
384
|
+
var LAUNCH_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?(dev|start|serve)\b|\b(vite|next|nuxt|astro|remix|webpack[\- ]dev[\- ]server|nodemon|tsx\s+watch|ts-node-dev)\b|\b(uvicorn|gunicorn|flask\s+run|manage\.py\s+runserver|rails\s+s(erver)?|go\s+run|cargo\s+run|dotnet\s+run|php\s+artisan\s+serve|air|nest\s+start)\b/i;
|
|
385
|
+
function isLaunchCommand(cmd) {
|
|
386
|
+
return LAUNCH_RE.test(cmd);
|
|
387
|
+
}
|
|
388
|
+
function looksLikeLaunchError(result) {
|
|
389
|
+
if (!/^exit\s+([1-9]\d*|124)\b/m.test(result))
|
|
390
|
+
return false;
|
|
391
|
+
return /\b(error|exception|cannot find module|module not found|eaddrinuse|syntaxerror|typeerror|referenceerror|failed to compile|command not found|traceback|panic:|address already in use|unexpected token|ENOENT|MODULE_NOT_FOUND)\b/i.test(result);
|
|
392
|
+
}
|
|
382
393
|
|
|
383
394
|
class Agent {
|
|
384
395
|
cfg;
|
|
@@ -501,6 +512,7 @@ ${summary}` : "Earlier conversation was auto-compacted to free context (summary
|
|
|
501
512
|
recordTurn(this.ctx.cwd, "user", userText);
|
|
502
513
|
newTurn();
|
|
503
514
|
this._doneCalls.clear();
|
|
515
|
+
this._launchHandled.clear();
|
|
504
516
|
const ac = this.abortController = new AbortController;
|
|
505
517
|
const signal = ac.signal;
|
|
506
518
|
let turnGrains = 0;
|
|
@@ -550,6 +562,26 @@ ${tail}`;
|
|
|
550
562
|
}
|
|
551
563
|
this.history.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content });
|
|
552
564
|
toolCount++;
|
|
565
|
+
if (call.function.name === "bash" && !this._launchHandled.has(call.id)) {
|
|
566
|
+
let bashCmd = "";
|
|
567
|
+
try {
|
|
568
|
+
bashCmd = String(JSON.parse(call.function.arguments || "{}").command ?? "");
|
|
569
|
+
} catch {}
|
|
570
|
+
if (isLaunchCommand(bashCmd) && looksLikeLaunchError(content)) {
|
|
571
|
+
this._launchHandled.add(call.id);
|
|
572
|
+
let decision = this.cfg.autoFixLaunchErrors ? "always" : "no";
|
|
573
|
+
if (!this.cfg.autoFixLaunchErrors && this.h.onLaunchError) {
|
|
574
|
+
decision = await this.h.onLaunchError(bashCmd);
|
|
575
|
+
}
|
|
576
|
+
if (decision === "always") {
|
|
577
|
+
this.cfg.autoFixLaunchErrors = true;
|
|
578
|
+
saveConfig({ autoFixLaunchErrors: true });
|
|
579
|
+
}
|
|
580
|
+
if (decision === "yes" || decision === "always") {
|
|
581
|
+
this.history.push({ role: "system", content: `The launch command \`${bashCmd}\` failed to start. Diagnose the root cause from the error output above, fix it (install missing deps, fix config/code, free the port, etc.), then re-run the launch to verify it starts cleanly.` });
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
553
585
|
}
|
|
554
586
|
}
|
|
555
587
|
} catch (e) {
|
|
@@ -577,6 +609,7 @@ ${tail}`;
|
|
|
577
609
|
}
|
|
578
610
|
}
|
|
579
611
|
_suggested = false;
|
|
612
|
+
_launchHandled = new Set;
|
|
580
613
|
_doneCalls = new Map;
|
|
581
614
|
async execToolCall(call) {
|
|
582
615
|
const tool = getTool(call.function.name);
|
|
@@ -602,11 +635,11 @@ ${tail}`;
|
|
|
602
635
|
if (!gate.allowed) {
|
|
603
636
|
result = `blocked by hook: ${gate.reason}`;
|
|
604
637
|
} else if (needsConfirm && !await this.h.confirm(tool.name, args)) {
|
|
605
|
-
result = "
|
|
638
|
+
result = "declined by user";
|
|
606
639
|
} else {
|
|
607
640
|
if (MUTATING_TOOLS.has(tool.name) && typeof args.path === "string")
|
|
608
641
|
snapshot(this.ctx.cwd, args.path);
|
|
609
|
-
const runCtx = { ...this.ctx, onStream: (c) => this.h.onToolStream?.(c), onCost: (g) => this.h.onCost(g) };
|
|
642
|
+
const runCtx = { ...this.ctx, onStream: (c) => this.h.onToolStream?.(c), onCost: (g) => this.h.onCost(g), signal: this.abortController?.signal };
|
|
610
643
|
try {
|
|
611
644
|
result = await tool.run(args, runCtx);
|
|
612
645
|
} catch (e) {
|
|
@@ -625,7 +658,7 @@ ${tail}`;
|
|
|
625
658
|
}
|
|
626
659
|
}
|
|
627
660
|
this.h.onToolResult(tool.name, result);
|
|
628
|
-
if (!result.startsWith("error") && !result.startsWith("
|
|
661
|
+
if (!result.startsWith("error") && !result.startsWith("declined") && !result.startsWith("blocked"))
|
|
629
662
|
this._doneCalls.set(sig, result);
|
|
630
663
|
return { content: result, edited };
|
|
631
664
|
}
|
|
@@ -1310,9 +1343,6 @@ function registerTaskTools() {
|
|
|
1310
1343
|
register(schedule);
|
|
1311
1344
|
}
|
|
1312
1345
|
|
|
1313
|
-
// src/tips/tips.ts
|
|
1314
|
-
var FREQUENCY = Math.max(1, Number(process.env.MODELCODE_TIP_FREQUENCY || "1"));
|
|
1315
|
-
|
|
1316
1346
|
// src/wallet/store.ts
|
|
1317
1347
|
import { join as join5 } from "path";
|
|
1318
1348
|
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
@@ -1382,7 +1412,7 @@ async function interactiveLogin(rl, cfg) {
|
|
|
1382
1412
|
`);
|
|
1383
1413
|
return false;
|
|
1384
1414
|
}
|
|
1385
|
-
|
|
1415
|
+
saveConfig2({ apiKey: key });
|
|
1386
1416
|
cfg.apiKey = key;
|
|
1387
1417
|
stdout.write(`${C.green}\u2713 logged in${C.reset} (key saved to ~/.modelcode/config.json)
|
|
1388
1418
|
`);
|
|
@@ -1410,7 +1440,7 @@ async function interactiveLogin(rl, cfg) {
|
|
|
1410
1440
|
}
|
|
1411
1441
|
try {
|
|
1412
1442
|
const { apiKey, depositAddr } = await registerApiKey(address);
|
|
1413
|
-
|
|
1443
|
+
saveConfig2({ apiKey });
|
|
1414
1444
|
cfg.apiKey = apiKey;
|
|
1415
1445
|
stdout.write(`${C.green}\u2713 API key created + saved${C.reset}
|
|
1416
1446
|
` + `${C.dim}To buy credits: send MDL to your wallet, then run ${C.reset}modelcode credits fund <mdl>${C.dim} ` + `(it funds the API bank ${depositAddr ? depositAddr.slice(0, 16) + "\u2026" : ""} and claims credits).${C.reset}
|
|
@@ -1440,7 +1470,7 @@ async function maybeAutoKey(address) {
|
|
|
1440
1470
|
return;
|
|
1441
1471
|
try {
|
|
1442
1472
|
const { apiKey, depositAddr } = await registerApiKey(address);
|
|
1443
|
-
|
|
1473
|
+
saveConfig2({ apiKey });
|
|
1444
1474
|
stdout.write(`${C.green}API key created & saved${C.reset} (${apiKey.slice(0, 12)}\u2026)
|
|
1445
1475
|
` + `deposit address: ${depositAddr}
|
|
1446
1476
|
fund it in one command: ${C.cyan}modelcode credits fund <mdl>${C.reset}
|
|
@@ -1609,7 +1639,7 @@ async function main() {
|
|
|
1609
1639
|
s.resume();
|
|
1610
1640
|
await new Promise((r) => setImmediate(r));
|
|
1611
1641
|
}
|
|
1612
|
-
const { runTui } = await import("./tui-
|
|
1642
|
+
const { runTui } = await import("./tui-pgwd6eyk.mjs");
|
|
1613
1643
|
return runTui(cfg, resume);
|
|
1614
1644
|
}
|
|
1615
1645
|
if (cmd === "tui")
|
|
@@ -7921,6 +7921,7 @@ var ConfigSchema = exports_external.object({
|
|
|
7921
7921
|
autoSummary: exports_external.boolean().default(true),
|
|
7922
7922
|
theme: exports_external.enum(["amber", "mono", "ocean"]).default("amber"),
|
|
7923
7923
|
teamMemoryUrl: exports_external.string().optional(),
|
|
7924
|
+
autoFixLaunchErrors: exports_external.boolean().default(false),
|
|
7924
7925
|
spendCapMdl: exports_external.number().default(0),
|
|
7925
7926
|
providers: exports_external.record(exports_external.object({ baseUrl: exports_external.string(), apiKey: exports_external.string().optional(), model: exports_external.string().optional() })).default({})
|
|
7926
7927
|
});
|
|
@@ -8073,16 +8074,54 @@ function extsFromGlob(glob) {
|
|
|
8073
8074
|
function runProc(cmd, opts = {}) {
|
|
8074
8075
|
return new Promise((resolve) => {
|
|
8075
8076
|
const [bin, ...args] = cmd;
|
|
8076
|
-
const so = { cwd: opts.cwd, env: { ...process.env, ...opts.env }, stdio: ["ignore", "pipe", "pipe"] };
|
|
8077
|
+
const so = { cwd: opts.cwd, env: { ...process.env, ...opts.env }, stdio: ["ignore", "pipe", "pipe"], detached: !isWin };
|
|
8077
8078
|
const p = spawn(bin, args, so);
|
|
8078
|
-
let stdout = "", stderr = "";
|
|
8079
|
+
let stdout = "", stderr = "", done = false;
|
|
8079
8080
|
const dOut = new StringDecoder("utf8"), dErr = new StringDecoder("utf8");
|
|
8081
|
+
const finish = (code) => {
|
|
8082
|
+
if (done)
|
|
8083
|
+
return;
|
|
8084
|
+
done = true;
|
|
8085
|
+
if (killer)
|
|
8086
|
+
clearTimeout(killer);
|
|
8087
|
+
resolve({ code, stdout: stdout + dOut.end(), stderr: stderr + dErr.end() });
|
|
8088
|
+
};
|
|
8089
|
+
const killTree = (signal) => {
|
|
8090
|
+
try {
|
|
8091
|
+
if (isWin)
|
|
8092
|
+
spawnSync("taskkill", ["/pid", String(p.pid), "/T", "/F"], { stdio: "ignore" });
|
|
8093
|
+
else if (p.pid)
|
|
8094
|
+
process.kill(-p.pid, signal);
|
|
8095
|
+
else
|
|
8096
|
+
p.kill(signal);
|
|
8097
|
+
} catch {
|
|
8098
|
+
try {
|
|
8099
|
+
p.kill(signal);
|
|
8100
|
+
} catch {}
|
|
8101
|
+
}
|
|
8102
|
+
};
|
|
8080
8103
|
let killer = null;
|
|
8081
8104
|
if (opts.timeoutMs)
|
|
8082
8105
|
killer = setTimeout(() => {
|
|
8083
|
-
|
|
8084
|
-
setTimeout(() =>
|
|
8106
|
+
killTree("SIGTERM");
|
|
8107
|
+
setTimeout(() => {
|
|
8108
|
+
killTree("SIGKILL");
|
|
8109
|
+
finish(124);
|
|
8110
|
+
}, 2000);
|
|
8085
8111
|
}, opts.timeoutMs);
|
|
8112
|
+
if (opts.signal) {
|
|
8113
|
+
if (opts.signal.aborted) {
|
|
8114
|
+
killTree("SIGKILL");
|
|
8115
|
+
finish(130);
|
|
8116
|
+
} else
|
|
8117
|
+
opts.signal.addEventListener("abort", () => {
|
|
8118
|
+
killTree("SIGTERM");
|
|
8119
|
+
setTimeout(() => {
|
|
8120
|
+
killTree("SIGKILL");
|
|
8121
|
+
finish(130);
|
|
8122
|
+
}, 500);
|
|
8123
|
+
}, { once: true });
|
|
8124
|
+
}
|
|
8086
8125
|
p.stdout?.on("data", (d) => {
|
|
8087
8126
|
const s = dOut.write(d);
|
|
8088
8127
|
stdout += s;
|
|
@@ -8093,15 +8132,10 @@ function runProc(cmd, opts = {}) {
|
|
|
8093
8132
|
stderr += s;
|
|
8094
8133
|
opts.onChunk?.(s, true);
|
|
8095
8134
|
});
|
|
8096
|
-
p.on("close", (code) =>
|
|
8097
|
-
if (killer)
|
|
8098
|
-
clearTimeout(killer);
|
|
8099
|
-
resolve({ code: code ?? 0, stdout: stdout + dOut.end(), stderr: stderr + dErr.end() });
|
|
8100
|
-
});
|
|
8135
|
+
p.on("close", (code) => finish(code ?? 0));
|
|
8101
8136
|
p.on("error", (e) => {
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
resolve({ code: 1, stdout, stderr: stderr + String(e) });
|
|
8137
|
+
stderr += String(e);
|
|
8138
|
+
finish(1);
|
|
8105
8139
|
});
|
|
8106
8140
|
});
|
|
8107
8141
|
}
|
|
@@ -15047,37 +15081,43 @@ function toolSchemas() {
|
|
|
15047
15081
|
|
|
15048
15082
|
// src/ui/diff.ts
|
|
15049
15083
|
var A = { red: "\x1B[31m", green: "\x1B[32m", gray: "\x1B[90m", cyan: "\x1B[36m", yellow: "\x1B[33m", mag: "\x1B[35m", reset: "\x1B[0m" };
|
|
15050
|
-
function
|
|
15084
|
+
function renderDiffPlain(before, after) {
|
|
15051
15085
|
const a = before.split(`
|
|
15052
15086
|
`), b = after.split(`
|
|
15053
15087
|
`);
|
|
15054
|
-
const n = Math.min(a.length,
|
|
15088
|
+
const n = Math.min(a.length, 4000), m = Math.min(b.length, 4000);
|
|
15055
15089
|
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
15056
15090
|
for (let i2 = n - 1;i2 >= 0; i2--)
|
|
15057
15091
|
for (let j2 = m - 1;j2 >= 0; j2--)
|
|
15058
15092
|
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
15059
|
-
const out =
|
|
15093
|
+
const out = [];
|
|
15060
15094
|
let i = 0, j = 0;
|
|
15061
15095
|
while (i < n && j < m) {
|
|
15062
15096
|
if (a[i] === b[j]) {
|
|
15063
|
-
out.push(
|
|
15097
|
+
out.push(` ${a[i]}`);
|
|
15064
15098
|
i++;
|
|
15065
15099
|
j++;
|
|
15066
15100
|
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
15067
|
-
out.push(
|
|
15101
|
+
out.push(`- ${a[i]}`);
|
|
15068
15102
|
i++;
|
|
15069
15103
|
} else {
|
|
15070
|
-
out.push(
|
|
15104
|
+
out.push(`+ ${b[j]}`);
|
|
15071
15105
|
j++;
|
|
15072
15106
|
}
|
|
15073
15107
|
}
|
|
15074
15108
|
while (i < n)
|
|
15075
|
-
out.push(
|
|
15109
|
+
out.push(`- ${a[i++]}`);
|
|
15076
15110
|
while (j < m)
|
|
15077
|
-
out.push(
|
|
15111
|
+
out.push(`+ ${b[j++]}`);
|
|
15078
15112
|
return out.join(`
|
|
15079
15113
|
`);
|
|
15080
15114
|
}
|
|
15115
|
+
var KEYWORDS = /\b(const|let|var|function|class|interface|type|import|export|return|if|else|for|while|async|await|def|fn|struct|enum|public|private|func|package|use|impl|match|new)\b/g;
|
|
15116
|
+
var STRINGS = /("[^"]*"|'[^']*'|`[^`]*`)/g;
|
|
15117
|
+
var COMMENTS = /(\/\/[^\n]*|#[^\n]*)/g;
|
|
15118
|
+
function highlight(code) {
|
|
15119
|
+
return code.replace(COMMENTS, (m) => `${A.gray}${m}${A.reset}`).replace(STRINGS, (m) => `${A.yellow}${m}${A.reset}`).replace(KEYWORDS, (m) => `${A.mag}${m}${A.reset}`).replace(/\b(\d+(?:\.\d+)?)\b/g, (m) => `${A.cyan}${m}${A.reset}`);
|
|
15120
|
+
}
|
|
15081
15121
|
|
|
15082
15122
|
// src/memory/store.ts
|
|
15083
15123
|
import { join as join7 } from "path";
|
|
@@ -24315,4 +24355,4 @@ async function decryptWallet(enc, password, network = MAINNET) {
|
|
|
24315
24355
|
}
|
|
24316
24356
|
return w;
|
|
24317
24357
|
}
|
|
24318
|
-
export { exports_external, register, getTool, toolSchemas,
|
|
24358
|
+
export { exports_external, register, getTool, toolSchemas, renderDiffPlain, highlight, shellInvocation, shellName, globMatch, runProc, which, spawnProc, globalDir, loadConfig, saveConfig, rememberFact, allFacts, searchFacts, deleteFact, recordTurn, searchSessions, parseFrontmatter, loadSubagents, chat2 as chat, route, loadProjectContext, addEntity, addRelation, query, indexFile, indexCodebaseIncremental, indexCodebase, loadSkills, buildTurnContext, preToolUse, postToolUse, userPromptSubmit, fireEvent, runSubagent, runTeam, isImagePath, imageToDataUrl, estimateTokens, contextStatus, compactionThreshold, findCompactionCut, newTurn, snapshot, undo, depth, MUTATING_TOOLS, isProtectedBuiltin, recordUse, recordPatch, recordCreate, getUsage, allUsage, activityCount, analyzeImpact, impactSummary, lspFor, closeAllLsp, COLUMNS, addCard, moveCard, removeCard, renderBoard, addJob, listJobs, removeJob, startScheduler, MAINNET, generateMnemonic2 as generateMnemonic, validateMnemonic2 as validateMnemonic, walletFromMnemonic, importWalletHex, encryptWallet, decryptWallet, createTransfer };
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
getUsage,
|
|
24
24
|
globMatch,
|
|
25
25
|
globalDir,
|
|
26
|
+
highlight,
|
|
26
27
|
imageToDataUrl,
|
|
27
28
|
impactSummary,
|
|
28
29
|
indexCodebase,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
removeCard,
|
|
52
53
|
removeJob,
|
|
53
54
|
renderBoard,
|
|
54
|
-
|
|
55
|
+
renderDiffPlain,
|
|
55
56
|
route,
|
|
56
57
|
runProc,
|
|
57
58
|
runSubagent,
|
|
@@ -68,7 +69,7 @@ import {
|
|
|
68
69
|
undo,
|
|
69
70
|
userPromptSubmit,
|
|
70
71
|
which
|
|
71
|
-
} from "./main-
|
|
72
|
+
} from "./main-pj08pbgh.mjs";
|
|
72
73
|
import"./main-p2xnn95s.mjs";
|
|
73
74
|
import {
|
|
74
75
|
__commonJS,
|
|
@@ -21593,7 +21594,7 @@ function formatMarkdown(src) {
|
|
|
21593
21594
|
continue;
|
|
21594
21595
|
}
|
|
21595
21596
|
if (inFence) {
|
|
21596
|
-
out.push(
|
|
21597
|
+
out.push(` ${highlight(line)}`);
|
|
21597
21598
|
continue;
|
|
21598
21599
|
}
|
|
21599
21600
|
const h = line.match(/^(#{1,6})\s+(.*)$/);
|
|
@@ -21692,6 +21693,11 @@ function Chip({ label, color }) {
|
|
|
21692
21693
|
]
|
|
21693
21694
|
}, undefined, true, undefined, this);
|
|
21694
21695
|
}
|
|
21696
|
+
function truncPath(p, max2 = 60) {
|
|
21697
|
+
if (p.length <= max2)
|
|
21698
|
+
return p;
|
|
21699
|
+
return "…" + p.slice(p.length - max2 + 1);
|
|
21700
|
+
}
|
|
21695
21701
|
function C_KEY(color, k) {
|
|
21696
21702
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21697
21703
|
color,
|
|
@@ -21703,7 +21709,7 @@ function C_KEY(color, k) {
|
|
|
21703
21709
|
]
|
|
21704
21710
|
}, undefined, true, undefined, this);
|
|
21705
21711
|
}
|
|
21706
|
-
function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct, walletMdl, mode, busy, toolStream, turnStart, turnTokens, confirmReq, history, onConfirm, onSubmit, onInterrupt, onExit }) {
|
|
21712
|
+
function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct, walletMdl, mode, busy, toolStream, turnStart, turnTokens, confirmReq, launchReq, history, onConfirm, onLaunchDecision, onSubmit, onInterrupt, onExit }) {
|
|
21707
21713
|
const t = getTheme(theme);
|
|
21708
21714
|
const [input, setInput] = import_react34.useState("");
|
|
21709
21715
|
const [histIdx, setHistIdx] = import_react34.useState(-1);
|
|
@@ -21737,6 +21743,16 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21737
21743
|
armExit();
|
|
21738
21744
|
return;
|
|
21739
21745
|
}
|
|
21746
|
+
if (launchReq) {
|
|
21747
|
+
const k = ch.toLowerCase();
|
|
21748
|
+
if (k === "y")
|
|
21749
|
+
onLaunchDecision("yes");
|
|
21750
|
+
else if (k === "a")
|
|
21751
|
+
onLaunchDecision("always");
|
|
21752
|
+
else if (k === "n" || key.return || key.escape)
|
|
21753
|
+
onLaunchDecision("no");
|
|
21754
|
+
return;
|
|
21755
|
+
}
|
|
21740
21756
|
if (confirmReq) {
|
|
21741
21757
|
const k = ch.toLowerCase();
|
|
21742
21758
|
if (k === "y")
|
|
@@ -21829,12 +21845,85 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21829
21845
|
}, i, true, undefined, this);
|
|
21830
21846
|
if (l.kind === "tool")
|
|
21831
21847
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21832
|
-
color: t.tool,
|
|
21833
21848
|
children: [
|
|
21834
|
-
|
|
21835
|
-
|
|
21849
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21850
|
+
color: t.tool,
|
|
21851
|
+
children: "⏺ "
|
|
21852
|
+
}, undefined, false, undefined, this),
|
|
21853
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21854
|
+
children: l.text
|
|
21855
|
+
}, undefined, false, undefined, this)
|
|
21856
|
+
]
|
|
21857
|
+
}, i, true, undefined, this);
|
|
21858
|
+
if (l.kind === "result")
|
|
21859
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21860
|
+
flexDirection: "row",
|
|
21861
|
+
children: [
|
|
21862
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21863
|
+
color: t.dim,
|
|
21864
|
+
children: " ⎿ "
|
|
21865
|
+
}, undefined, false, undefined, this),
|
|
21866
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21867
|
+
flexShrink: 1,
|
|
21868
|
+
flexGrow: 1,
|
|
21869
|
+
children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21870
|
+
color: t.dim,
|
|
21871
|
+
children: l.text.split(`
|
|
21872
|
+
`).join(`
|
|
21873
|
+
`)
|
|
21874
|
+
}, undefined, false, undefined, this)
|
|
21875
|
+
}, undefined, false, undefined, this)
|
|
21876
|
+
]
|
|
21877
|
+
}, i, true, undefined, this);
|
|
21878
|
+
if (l.kind === "diff") {
|
|
21879
|
+
const rows = l.text.split(`
|
|
21880
|
+
`);
|
|
21881
|
+
const header = rows[0] && !/^[+\- ] /.test(rows[0]) ? rows.shift() : null;
|
|
21882
|
+
let ln = 0;
|
|
21883
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21884
|
+
flexDirection: "column",
|
|
21885
|
+
children: [
|
|
21886
|
+
header ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21887
|
+
children: [
|
|
21888
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21889
|
+
color: t.tool,
|
|
21890
|
+
children: "⏺ "
|
|
21891
|
+
}, undefined, false, undefined, this),
|
|
21892
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21893
|
+
children: header
|
|
21894
|
+
}, undefined, false, undefined, this)
|
|
21895
|
+
]
|
|
21896
|
+
}, undefined, true, undefined, this) : null,
|
|
21897
|
+
rows.slice(0, 60).map((row, k) => {
|
|
21898
|
+
const add2 = row.startsWith("+ "), del = row.startsWith("- ");
|
|
21899
|
+
if (!del)
|
|
21900
|
+
ln++;
|
|
21901
|
+
const num = String(ln).padStart(4);
|
|
21902
|
+
const body = row.slice(2);
|
|
21903
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21904
|
+
children: [
|
|
21905
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21906
|
+
color: t.dim,
|
|
21907
|
+
children: ` ${add2 ? " " : num} `
|
|
21908
|
+
}, undefined, false, undefined, this),
|
|
21909
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21910
|
+
backgroundColor: add2 ? "#10301a" : del ? "#3a1418" : undefined,
|
|
21911
|
+
color: add2 ? t.ok : del ? t.warn : t.dim,
|
|
21912
|
+
children: [
|
|
21913
|
+
add2 ? "+ " : del ? "- " : " ",
|
|
21914
|
+
body
|
|
21915
|
+
]
|
|
21916
|
+
}, undefined, true, undefined, this)
|
|
21917
|
+
]
|
|
21918
|
+
}, k, true, undefined, this);
|
|
21919
|
+
}),
|
|
21920
|
+
rows.length > 60 ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21921
|
+
color: t.dim,
|
|
21922
|
+
children: ` … +${rows.length - 60} more diff lines`
|
|
21923
|
+
}, undefined, false, undefined, this) : null
|
|
21836
21924
|
]
|
|
21837
21925
|
}, i, true, undefined, this);
|
|
21926
|
+
}
|
|
21838
21927
|
if (l.kind === "error")
|
|
21839
21928
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21840
21929
|
flexDirection: "column",
|
|
@@ -21864,21 +21953,36 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21864
21953
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21865
21954
|
borderStyle: "round",
|
|
21866
21955
|
borderColor: t.accent,
|
|
21867
|
-
paddingX:
|
|
21868
|
-
|
|
21956
|
+
paddingX: 2,
|
|
21957
|
+
paddingY: 1,
|
|
21958
|
+
flexDirection: "column",
|
|
21959
|
+
alignItems: "center",
|
|
21869
21960
|
children: [
|
|
21870
21961
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21871
21962
|
bold: true,
|
|
21872
21963
|
color: t.accent,
|
|
21873
|
-
children: "
|
|
21964
|
+
children: "✻ modelcode"
|
|
21874
21965
|
}, undefined, false, undefined, this),
|
|
21875
21966
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21876
21967
|
color: t.dim,
|
|
21968
|
+
children: "the AI coding agent on modelOS"
|
|
21969
|
+
}, undefined, false, undefined, this),
|
|
21970
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21877
21971
|
children: [
|
|
21878
|
-
|
|
21879
|
-
|
|
21972
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21973
|
+
color: t.user,
|
|
21974
|
+
children: model
|
|
21975
|
+
}, undefined, false, undefined, this),
|
|
21976
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21977
|
+
color: t.dim,
|
|
21978
|
+
children: " · pay-per-use in MDL · no rate limits"
|
|
21979
|
+
}, undefined, false, undefined, this)
|
|
21880
21980
|
]
|
|
21881
|
-
}, undefined, true, undefined, this)
|
|
21981
|
+
}, undefined, true, undefined, this),
|
|
21982
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21983
|
+
color: t.dim,
|
|
21984
|
+
children: truncPath(process.cwd())
|
|
21985
|
+
}, undefined, false, undefined, this)
|
|
21882
21986
|
]
|
|
21883
21987
|
}, undefined, true, undefined, this),
|
|
21884
21988
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Static, {
|
|
@@ -21938,6 +22042,34 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21938
22042
|
]
|
|
21939
22043
|
}, c2.cmd, true, undefined, this))
|
|
21940
22044
|
}, undefined, false, undefined, this) : null,
|
|
22045
|
+
launchReq ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
22046
|
+
flexDirection: "column",
|
|
22047
|
+
borderStyle: "round",
|
|
22048
|
+
borderColor: t.warn,
|
|
22049
|
+
paddingX: 1,
|
|
22050
|
+
marginTop: 1,
|
|
22051
|
+
children: [
|
|
22052
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
22053
|
+
color: t.warn,
|
|
22054
|
+
bold: true,
|
|
22055
|
+
children: "launch failed — fix it?"
|
|
22056
|
+
}, undefined, false, undefined, this),
|
|
22057
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
22058
|
+
color: t.dim,
|
|
22059
|
+
children: launchReq.cmd
|
|
22060
|
+
}, undefined, false, undefined, this),
|
|
22061
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
22062
|
+
children: [
|
|
22063
|
+
C_KEY(t.user, "y"),
|
|
22064
|
+
" accept ",
|
|
22065
|
+
C_KEY(t.user, "n"),
|
|
22066
|
+
" decline ",
|
|
22067
|
+
C_KEY(t.accent, "a"),
|
|
22068
|
+
" at all times (every project)"
|
|
22069
|
+
]
|
|
22070
|
+
}, undefined, true, undefined, this)
|
|
22071
|
+
]
|
|
22072
|
+
}, undefined, true, undefined, this) : null,
|
|
21941
22073
|
confirmReq ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21942
22074
|
flexDirection: "column",
|
|
21943
22075
|
borderStyle: "round",
|
|
@@ -21961,13 +22093,13 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21961
22093
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21962
22094
|
children: [
|
|
21963
22095
|
C_KEY(t.user, "y"),
|
|
21964
|
-
"
|
|
22096
|
+
" accept ",
|
|
21965
22097
|
C_KEY(t.user, "a"),
|
|
21966
22098
|
" always (project) ",
|
|
21967
22099
|
C_KEY(t.user, "g"),
|
|
21968
|
-
" global ",
|
|
22100
|
+
" always (global) ",
|
|
21969
22101
|
C_KEY(t.warn, "n"),
|
|
21970
|
-
"
|
|
22102
|
+
" decline"
|
|
21971
22103
|
]
|
|
21972
22104
|
}, undefined, true, undefined, this)
|
|
21973
22105
|
]
|
|
@@ -22062,6 +22194,15 @@ Tool discipline (important): call each tool ONCE for a given action — never re
|
|
|
22062
22194
|
A tool result is authoritative: once a write/edit succeeds, trust it and move on. Do NOT claim a task is
|
|
22063
22195
|
done in the same message where you call the tool to do it — call the tool first, then confirm only after
|
|
22064
22196
|
you see its result. Keep confirmations to one short sentence.`;
|
|
22197
|
+
var LAUNCH_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?(dev|start|serve)\b|\b(vite|next|nuxt|astro|remix|webpack[\- ]dev[\- ]server|nodemon|tsx\s+watch|ts-node-dev)\b|\b(uvicorn|gunicorn|flask\s+run|manage\.py\s+runserver|rails\s+s(erver)?|go\s+run|cargo\s+run|dotnet\s+run|php\s+artisan\s+serve|air|nest\s+start)\b/i;
|
|
22198
|
+
function isLaunchCommand(cmd) {
|
|
22199
|
+
return LAUNCH_RE.test(cmd);
|
|
22200
|
+
}
|
|
22201
|
+
function looksLikeLaunchError(result2) {
|
|
22202
|
+
if (!/^exit\s+([1-9]\d*|124)\b/m.test(result2))
|
|
22203
|
+
return false;
|
|
22204
|
+
return /\b(error|exception|cannot find module|module not found|eaddrinuse|syntaxerror|typeerror|referenceerror|failed to compile|command not found|traceback|panic:|address already in use|unexpected token|ENOENT|MODULE_NOT_FOUND)\b/i.test(result2);
|
|
22205
|
+
}
|
|
22065
22206
|
|
|
22066
22207
|
class Agent {
|
|
22067
22208
|
cfg;
|
|
@@ -22184,6 +22325,7 @@ ${summary}` : "Earlier conversation was auto-compacted to free context (summary
|
|
|
22184
22325
|
recordTurn(this.ctx.cwd, "user", userText);
|
|
22185
22326
|
newTurn();
|
|
22186
22327
|
this._doneCalls.clear();
|
|
22328
|
+
this._launchHandled.clear();
|
|
22187
22329
|
const ac = this.abortController = new AbortController;
|
|
22188
22330
|
const signal = ac.signal;
|
|
22189
22331
|
let turnGrains = 0;
|
|
@@ -22233,6 +22375,26 @@ ${tail2}`;
|
|
|
22233
22375
|
}
|
|
22234
22376
|
this.history.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content });
|
|
22235
22377
|
toolCount++;
|
|
22378
|
+
if (call.function.name === "bash" && !this._launchHandled.has(call.id)) {
|
|
22379
|
+
let bashCmd = "";
|
|
22380
|
+
try {
|
|
22381
|
+
bashCmd = String(JSON.parse(call.function.arguments || "{}").command ?? "");
|
|
22382
|
+
} catch {}
|
|
22383
|
+
if (isLaunchCommand(bashCmd) && looksLikeLaunchError(content)) {
|
|
22384
|
+
this._launchHandled.add(call.id);
|
|
22385
|
+
let decision = this.cfg.autoFixLaunchErrors ? "always" : "no";
|
|
22386
|
+
if (!this.cfg.autoFixLaunchErrors && this.h.onLaunchError) {
|
|
22387
|
+
decision = await this.h.onLaunchError(bashCmd);
|
|
22388
|
+
}
|
|
22389
|
+
if (decision === "always") {
|
|
22390
|
+
this.cfg.autoFixLaunchErrors = true;
|
|
22391
|
+
saveConfig({ autoFixLaunchErrors: true });
|
|
22392
|
+
}
|
|
22393
|
+
if (decision === "yes" || decision === "always") {
|
|
22394
|
+
this.history.push({ role: "system", content: `The launch command \`${bashCmd}\` failed to start. Diagnose the root cause from the error output above, fix it (install missing deps, fix config/code, free the port, etc.), then re-run the launch to verify it starts cleanly.` });
|
|
22395
|
+
}
|
|
22396
|
+
}
|
|
22397
|
+
}
|
|
22236
22398
|
}
|
|
22237
22399
|
}
|
|
22238
22400
|
} catch (e) {
|
|
@@ -22260,6 +22422,7 @@ ${tail2}`;
|
|
|
22260
22422
|
}
|
|
22261
22423
|
}
|
|
22262
22424
|
_suggested = false;
|
|
22425
|
+
_launchHandled = new Set;
|
|
22263
22426
|
_doneCalls = new Map;
|
|
22264
22427
|
async execToolCall(call) {
|
|
22265
22428
|
const tool = getTool(call.function.name);
|
|
@@ -22285,11 +22448,11 @@ ${tail2}`;
|
|
|
22285
22448
|
if (!gate.allowed) {
|
|
22286
22449
|
result2 = `blocked by hook: ${gate.reason}`;
|
|
22287
22450
|
} else if (needsConfirm && !await this.h.confirm(tool.name, args)) {
|
|
22288
|
-
result2 = "
|
|
22451
|
+
result2 = "declined by user";
|
|
22289
22452
|
} else {
|
|
22290
22453
|
if (MUTATING_TOOLS.has(tool.name) && typeof args.path === "string")
|
|
22291
22454
|
snapshot(this.ctx.cwd, args.path);
|
|
22292
|
-
const runCtx = { ...this.ctx, onStream: (c2) => this.h.onToolStream?.(c2), onCost: (g) => this.h.onCost(g) };
|
|
22455
|
+
const runCtx = { ...this.ctx, onStream: (c2) => this.h.onToolStream?.(c2), onCost: (g) => this.h.onCost(g), signal: this.abortController?.signal };
|
|
22293
22456
|
try {
|
|
22294
22457
|
result2 = await tool.run(args, runCtx);
|
|
22295
22458
|
} catch (e) {
|
|
@@ -22308,7 +22471,7 @@ ${tail2}`;
|
|
|
22308
22471
|
}
|
|
22309
22472
|
}
|
|
22310
22473
|
this.h.onToolResult(tool.name, result2);
|
|
22311
|
-
if (!result2.startsWith("error") && !result2.startsWith("
|
|
22474
|
+
if (!result2.startsWith("error") && !result2.startsWith("declined") && !result2.startsWith("blocked"))
|
|
22312
22475
|
this._doneCalls.set(sig, result2);
|
|
22313
22476
|
return { content: result2, edited };
|
|
22314
22477
|
}
|
|
@@ -23015,7 +23178,7 @@ var bash = {
|
|
|
23015
23178
|
async run(args, ctx) {
|
|
23016
23179
|
const cmd = String(args.command ?? "");
|
|
23017
23180
|
const timeout = Number(args.timeout_ms) || 120000;
|
|
23018
|
-
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, onChunk: (s) => ctx.onStream?.(s) });
|
|
23181
|
+
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, signal: ctx.signal, onChunk: (s) => ctx.onStream?.(s) });
|
|
23019
23182
|
return `exit ${r.code}
|
|
23020
23183
|
${(r.stdout + (r.stderr ? `
|
|
23021
23184
|
[stderr]
|
|
@@ -23086,7 +23249,7 @@ var edit = {
|
|
|
23086
23249
|
const updated = args.replace_all ? src.split(oldS).join(newS) : src.replace(oldS, newS);
|
|
23087
23250
|
writeFileSync6(p, updated);
|
|
23088
23251
|
return `edited ${args.path} (${count} replacement${count === 1 ? "" : "s"})
|
|
23089
|
-
${
|
|
23252
|
+
${renderDiffPlain(oldS, newS)}`;
|
|
23090
23253
|
}
|
|
23091
23254
|
};
|
|
23092
23255
|
var glob = {
|
|
@@ -23819,6 +23982,26 @@ function imageMentions(line, cwd2) {
|
|
|
23819
23982
|
|
|
23820
23983
|
// src/ui/tui.tsx
|
|
23821
23984
|
var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
|
|
23985
|
+
function toolUseLabel(name, args) {
|
|
23986
|
+
const cap = (s, n = 160) => s.length > n ? s.slice(0, n).trim() + "…" : s;
|
|
23987
|
+
if (name === "bash")
|
|
23988
|
+
return cap(String(args.command ?? "").split(`
|
|
23989
|
+
`)[0] ?? "");
|
|
23990
|
+
if (name === "edit" || name === "write" || name === "read")
|
|
23991
|
+
return `${name} ${cap(String(args.path ?? ""), 80)}`;
|
|
23992
|
+
const keys2 = Object.entries(args).map(([k, v]) => `${k}=${cap(String(v), 40)}`).join(" ");
|
|
23993
|
+
return cap(`${name} ${keys2}`);
|
|
23994
|
+
}
|
|
23995
|
+
function capLines(s, max2) {
|
|
23996
|
+
const lines = s.replace(/\s+$/, "").split(`
|
|
23997
|
+
`);
|
|
23998
|
+
if (lines.length <= max2)
|
|
23999
|
+
return lines.join(`
|
|
24000
|
+
`);
|
|
24001
|
+
return lines.slice(0, max2).join(`
|
|
24002
|
+
`) + `
|
|
24003
|
+
… +${lines.length - max2} lines`;
|
|
24004
|
+
}
|
|
23822
24005
|
function Root({ cfg, resume, mcpCount }) {
|
|
23823
24006
|
const [lines, setLines] = import_react35.useState([
|
|
23824
24007
|
{ kind: "system", text: `ready · model ${cfg.model} — ask me anything, or /help` },
|
|
@@ -23831,16 +24014,20 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23831
24014
|
const [walletMdl, setWalletMdl] = import_react35.useState(null);
|
|
23832
24015
|
const [mode, setMode] = import_react35.useState("default");
|
|
23833
24016
|
const [confirmReq, setConfirmReq] = import_react35.useState(null);
|
|
24017
|
+
const [launchReq, setLaunchReq] = import_react35.useState(null);
|
|
23834
24018
|
const [history, setHistory] = import_react35.useState([]);
|
|
23835
24019
|
const [toolStream, setToolStream] = import_react35.useState("");
|
|
23836
24020
|
const [turnStart, setTurnStart] = import_react35.useState(0);
|
|
23837
24021
|
const [turnTokens, setTurnTokens] = import_react35.useState(0);
|
|
23838
24022
|
const mdlUsd = import_react35.useRef(null);
|
|
23839
24023
|
const streamBuf = import_react35.useRef("");
|
|
24024
|
+
const toolStreamRef = import_react35.useRef("");
|
|
23840
24025
|
const tokensRef = import_react35.useRef(0);
|
|
24026
|
+
const pumpRef = import_react35.useRef(null);
|
|
23841
24027
|
const busyRef = import_react35.useRef(false);
|
|
23842
24028
|
const queueRef = import_react35.useRef([]);
|
|
23843
24029
|
const confirmResolve = import_react35.useRef(null);
|
|
24030
|
+
const launchResolve = import_react35.useRef(null);
|
|
23844
24031
|
const sessionIdRef = import_react35.useRef(newSessionId());
|
|
23845
24032
|
const sessionMdlRef = import_react35.useRef(0);
|
|
23846
24033
|
const mcpCountRef = import_react35.useRef(mcpCount);
|
|
@@ -23869,29 +24056,46 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23869
24056
|
confirmResolve.current?.(decision !== "no");
|
|
23870
24057
|
confirmResolve.current = null;
|
|
23871
24058
|
}, [confirmReq, cwd2]);
|
|
24059
|
+
const onLaunchError = import_react35.useCallback((cmd) => {
|
|
24060
|
+
setLaunchReq({ cmd });
|
|
24061
|
+
return new Promise((resolve3) => {
|
|
24062
|
+
launchResolve.current = resolve3;
|
|
24063
|
+
});
|
|
24064
|
+
}, []);
|
|
24065
|
+
const onLaunchDecision = import_react35.useCallback((d) => {
|
|
24066
|
+
setLaunchReq(null);
|
|
24067
|
+
launchResolve.current?.(d);
|
|
24068
|
+
launchResolve.current = null;
|
|
24069
|
+
}, []);
|
|
23872
24070
|
const agent = import_react35.useRef(new Agent(cfg, { cwd: cwd2 }, {
|
|
23873
24071
|
onAssistantDelta: (txt) => {
|
|
23874
24072
|
streamBuf.current += txt;
|
|
23875
|
-
setStreaming(streamBuf.current);
|
|
23876
24073
|
tokensRef.current += Math.max(1, Math.round(txt.length / 4));
|
|
23877
|
-
setTurnTokens(tokensRef.current);
|
|
23878
24074
|
},
|
|
23879
24075
|
onToolStart: (name, args) => {
|
|
23880
24076
|
flushAssistant();
|
|
24077
|
+
toolStreamRef.current = "";
|
|
23881
24078
|
setToolStream("");
|
|
23882
|
-
add2({ kind: "tool", text:
|
|
24079
|
+
add2({ kind: "tool", text: toolUseLabel(name, args) });
|
|
23883
24080
|
},
|
|
23884
|
-
onToolResult: (
|
|
24081
|
+
onToolResult: (name, r) => {
|
|
24082
|
+
toolStreamRef.current = "";
|
|
23885
24083
|
setToolStream("");
|
|
23886
|
-
|
|
24084
|
+
if ((name === "edit" || name === "write") && /^[+\-] /m.test(r))
|
|
24085
|
+
add2({ kind: "diff", text: r });
|
|
24086
|
+
else
|
|
24087
|
+
add2({ kind: "result", text: capLines(r, 8) });
|
|
24088
|
+
},
|
|
24089
|
+
onToolStream: (chunk2) => {
|
|
24090
|
+
toolStreamRef.current = (toolStreamRef.current + chunk2).slice(-2000);
|
|
23887
24091
|
},
|
|
23888
|
-
onToolStream: (chunk2) => setToolStream((s) => (s + chunk2).slice(-2000)),
|
|
23889
24092
|
onCost: (g) => setSessionMdl((s) => {
|
|
23890
24093
|
const v = s + g / 1e8;
|
|
23891
24094
|
sessionMdlRef.current = v;
|
|
23892
24095
|
return v;
|
|
23893
24096
|
}),
|
|
23894
24097
|
confirm,
|
|
24098
|
+
onLaunchError,
|
|
23895
24099
|
onSuggestSkill: (n) => add2({ kind: "system", text: `\uD83D\uDCA1 that took ${n} steps — /skill-create to save it` }),
|
|
23896
24100
|
onCompact: (cs) => add2({ kind: "system", text: `\uD83D\uDDDC context auto-compacted (now ~${cs.pct}% full)` })
|
|
23897
24101
|
})).current;
|
|
@@ -23932,6 +24136,7 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23932
24136
|
return;
|
|
23933
24137
|
}
|
|
23934
24138
|
streamBuf.current = "";
|
|
24139
|
+
toolStreamRef.current = "";
|
|
23935
24140
|
setStreaming("");
|
|
23936
24141
|
setToolStream("");
|
|
23937
24142
|
tokensRef.current = 0;
|
|
@@ -23939,6 +24144,13 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23939
24144
|
setTurnStart(Date.now());
|
|
23940
24145
|
busyRef.current = true;
|
|
23941
24146
|
setBusy(true);
|
|
24147
|
+
if (pumpRef.current)
|
|
24148
|
+
clearInterval(pumpRef.current);
|
|
24149
|
+
pumpRef.current = setInterval(() => {
|
|
24150
|
+
setStreaming(streamBuf.current);
|
|
24151
|
+
setToolStream(toolStreamRef.current);
|
|
24152
|
+
setTurnTokens(tokensRef.current);
|
|
24153
|
+
}, 80);
|
|
23942
24154
|
try {
|
|
23943
24155
|
await agent.send(expandFileMentions(prompt, cwd2), imageMentions(prompt, cwd2));
|
|
23944
24156
|
flushAssistant();
|
|
@@ -23955,9 +24167,14 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23955
24167
|
flushAssistant();
|
|
23956
24168
|
add2({ kind: "error", text: e.message });
|
|
23957
24169
|
} finally {
|
|
24170
|
+
if (pumpRef.current) {
|
|
24171
|
+
clearInterval(pumpRef.current);
|
|
24172
|
+
pumpRef.current = null;
|
|
24173
|
+
}
|
|
23958
24174
|
setStreaming("");
|
|
23959
24175
|
setToolStream("");
|
|
23960
24176
|
streamBuf.current = "";
|
|
24177
|
+
toolStreamRef.current = "";
|
|
23961
24178
|
busyRef.current = false;
|
|
23962
24179
|
setBusy(false);
|
|
23963
24180
|
setContextPct(agent.contextStatus().pct);
|
|
@@ -24051,8 +24268,10 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
24051
24268
|
turnStart,
|
|
24052
24269
|
turnTokens,
|
|
24053
24270
|
confirmReq,
|
|
24271
|
+
launchReq,
|
|
24054
24272
|
history,
|
|
24055
24273
|
onConfirm,
|
|
24274
|
+
onLaunchDecision,
|
|
24056
24275
|
onSubmit,
|
|
24057
24276
|
onInterrupt,
|
|
24058
24277
|
onExit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modeloslab/modelcode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "modelOS-native AI coding agent CLI — remembers like Hermes, codes like Claude Code, runs on modelOS (pay-per-use in MDL, no rate limits). Knowledge-graph memory, subagents, MCP, vision, spend caps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|