@modeloslab/modelcode 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -56,6 +56,7 @@ import {
56
56
  searchFacts,
57
57
  searchSessions,
58
58
  shellInvocation,
59
+ shellName,
59
60
  snapshot,
60
61
  spawnProc,
61
62
  toolSchemas,
@@ -63,7 +64,7 @@ import {
63
64
  validateMnemonic,
64
65
  walletFromMnemonic,
65
66
  which
66
- } from "./main-k6vk3448.mjs";
67
+ } from "./main-wr686fnv.mjs";
67
68
  import"./main-p2xnn95s.mjs";
68
69
  import {
69
70
  __require
@@ -141,7 +142,7 @@ function inside(cwd, p) {
141
142
  }
142
143
  var bash = {
143
144
  name: "bash",
144
- description: "Run a shell command in the working directory; returns combined stdout+stderr. Use for builds, tests, git, file ops.",
145
+ description: `Run a shell command in the working directory (shell: ${shellName()}); returns combined stdout+stderr. Use for builds, tests, git, file ops.${shellName() === "powershell" || shellName() === "pwsh" ? " NOTE: this is Windows PowerShell — use PowerShell syntax (Get-ChildItem/ls, Get-Content/cat, 2>$null, Select-Object -First N)." : shellName() === "cmd" ? " NOTE: this is Windows cmd.exe — use cmd syntax." : ""}`,
145
146
  permission: "ask",
146
147
  parameters: {
147
148
  type: "object",
@@ -331,7 +332,9 @@ function registerAgentTool(cfg, onProgress) {
331
332
  if (!agent)
332
333
  return `error: unknown subagent '${args.subagent_type}'. available: ${names.join(", ")}`;
333
334
  const { text, feeGrains } = await runSubagent(cfg, agent, String(args.prompt ?? ""), ctx, onProgress);
334
- return `[${agent.name} · ${(feeGrains / 1e8).toFixed(6)} MDL · free/uncapped]
335
+ ctx.onCost?.(feeGrains);
336
+ onProgress?.(` ↳ ${agent.name} done · ${(feeGrains / 1e8).toFixed(4)} MDL`);
337
+ return `[${agent.name} · ${(feeGrains / 1e8).toFixed(6)} MDL]
335
338
  ${text}`;
336
339
  }
337
340
  };
@@ -352,7 +355,8 @@ ${text}`;
352
355
  async run(args, ctx) {
353
356
  const members = Array.isArray(args.members) ? args.members.map(String) : [];
354
357
  const { transcript, feeGrains } = await runTeam(cfg, members, String(args.prompt ?? ""), ctx, Number(args.rounds) || 2, onProgress);
355
- return `[team · ${(feeGrains / 1e8).toFixed(6)} MDL · free/uncapped]
358
+ ctx.onCost?.(feeGrains);
359
+ return `[team · ${(feeGrains / 1e8).toFixed(6)} MDL]
356
360
  ${transcript}`;
357
361
  }
358
362
  };
@@ -407,6 +411,16 @@ class Agent {
407
411
  note(text) {
408
412
  this.history.push({ role: "system", content: `User steering note (apply going forward): ${text}` });
409
413
  }
414
+ async sideQuestion(question, onDelta) {
415
+ const forked = [
416
+ ...this.history,
417
+ { role: "system", content: "The user has a quick side question. Answer it concisely using the conversation so far. This does NOT change the current task." },
418
+ { role: "user", content: question }
419
+ ];
420
+ const res = await chat(this.cfg, forked, [], onDelta ?? (() => {}), this.modelFor(question));
421
+ this.h.onCost(res.feeGrains);
422
+ return res.content || "(no answer)";
423
+ }
410
424
  get messages() {
411
425
  return [...this.history];
412
426
  }
@@ -592,7 +606,7 @@ ${tail}`;
592
606
  } else {
593
607
  if (MUTATING_TOOLS.has(tool.name) && typeof args.path === "string")
594
608
  snapshot(this.ctx.cwd, args.path);
595
- const runCtx = { ...this.ctx, onStream: (c) => this.h.onToolStream?.(c) };
609
+ const runCtx = { ...this.ctx, onStream: (c) => this.h.onToolStream?.(c), onCost: (g) => this.h.onCost(g) };
596
610
  try {
597
611
  result = await tool.run(args, runCtx);
598
612
  } catch (e) {
@@ -1356,10 +1370,15 @@ async function interactiveLogin(rl, cfg) {
1356
1370
  ` + ` ${C.cyan}3${C.reset}) skip for now
1357
1371
  `);
1358
1372
  const choice = (await rl.question(`${C.green}choose 1-3\u203A${C.reset} `)).trim();
1359
- if (choice === "1" || choice.startsWith("mdlk_")) {
1360
- const key = choice.startsWith("mdlk_") ? choice : (await rl.question("API key (mdlk_\u2026): ")).trim();
1361
- if (!key.startsWith("mdlk_")) {
1362
- stdout.write(`${C.amber}that doesn't look like an mdlk_ key${C.reset}
1373
+ const extractKey = (s) => s.match(/mdlk_[A-Za-z0-9]+/)?.[0] ?? "";
1374
+ if (choice === "1" || /mdlk_/.test(choice)) {
1375
+ let key = extractKey(choice);
1376
+ if (!key)
1377
+ key = extractKey(await rl.question("paste your API key (mdlk_\u2026): "));
1378
+ if (!key)
1379
+ key = extractKey(await rl.question("API key (mdlk_\u2026): "));
1380
+ if (!key) {
1381
+ stdout.write(`${C.amber}that doesn't look like an mdlk_ key \u2014 it should start with "mdlk_"${C.reset}
1363
1382
  `);
1364
1383
  return false;
1365
1384
  }
@@ -1580,8 +1599,17 @@ async function main() {
1580
1599
  const rl = createInterface({ input: stdin, output: stdout });
1581
1600
  await interactiveLogin(rl, cfg);
1582
1601
  rl.close();
1602
+ const s = stdin;
1603
+ s.removeAllListeners("data");
1604
+ s.removeAllListeners("keypress");
1605
+ try {
1606
+ if (s.isTTY)
1607
+ s.setRawMode(false);
1608
+ } catch {}
1609
+ s.resume();
1610
+ await new Promise((r) => setImmediate(r));
1583
1611
  }
1584
- const { runTui } = await import("./tui-2wxjcqb2.mjs");
1612
+ const { runTui } = await import("./tui-sekv1hga.mjs");
1585
1613
  return runTui(cfg, resume);
1586
1614
  }
1587
1615
  if (cmd === "tui")
@@ -7966,7 +7966,19 @@ var isWin = process.platform === "win32";
7966
7966
  function shellInvocation(cmd) {
7967
7967
  if (!isWin)
7968
7968
  return ["bash", "-lc", cmd];
7969
- return hasBash() ? ["bash", "-c", cmd] : ["cmd", "/c", cmd];
7969
+ if (hasBash())
7970
+ return ["bash", "-c", cmd];
7971
+ const ps = powershellBin();
7972
+ if (ps)
7973
+ return [ps, "-NoProfile", "-NonInteractive", "-Command", cmd];
7974
+ return ["cmd", "/c", cmd];
7975
+ }
7976
+ function shellName() {
7977
+ if (!isWin)
7978
+ return "bash";
7979
+ if (hasBash())
7980
+ return "bash";
7981
+ return powershellBin() ?? "cmd";
7970
7982
  }
7971
7983
  var _bash = null;
7972
7984
  function hasBash() {
@@ -7979,6 +7991,19 @@ function hasBash() {
7979
7991
  }
7980
7992
  return _bash;
7981
7993
  }
7994
+ var _ps;
7995
+ function powershellBin() {
7996
+ if (_ps !== undefined)
7997
+ return _ps;
7998
+ for (const bin of ["pwsh", "powershell"]) {
7999
+ try {
8000
+ if (spawnSync(bin, ["-NoProfile", "-Command", "exit 0"], { stdio: "ignore" }).status === 0) {
8001
+ return _ps = bin;
8002
+ }
8003
+ } catch {}
8004
+ }
8005
+ return _ps = null;
8006
+ }
7982
8007
  function packageRoot(startDir) {
7983
8008
  let dir = startDir;
7984
8009
  for (let i = 0;i < 12; i++) {
@@ -21025,6 +21050,7 @@ async function chat2(cfg, messages, tools, onDelta, modelOverride, timeoutMs = 1
21025
21050
  messages,
21026
21051
  max_tokens: cfg.maxTokens,
21027
21052
  stream: true,
21053
+ stream_options: { include_usage: true },
21028
21054
  ...tools.length ? { tools, tool_choice: "auto" } : {}
21029
21055
  };
21030
21056
  const attempt = async (live) => {
@@ -21032,7 +21058,13 @@ async function chat2(cfg, messages, tools, onDelta, modelOverride, timeoutMs = 1
21032
21058
  let content = "";
21033
21059
  let finishReason = null;
21034
21060
  let feeGrains = 0;
21061
+ let promptTokens = 0, completionTokens = 0;
21035
21062
  const calls = {};
21063
+ const readFee = (o) => {
21064
+ const x = o;
21065
+ const v = x?.x_modelos?.fee_grains ?? x?.fee_grains ?? x?.cost_grains;
21066
+ return v != null ? Number(v) || 0 : 0;
21067
+ };
21036
21068
  for await (const chunk of stream) {
21037
21069
  const choice = chunk.choices?.[0];
21038
21070
  const delta = choice?.delta;
@@ -21053,22 +21085,33 @@ async function chat2(cfg, messages, tools, onDelta, modelOverride, timeoutMs = 1
21053
21085
  }
21054
21086
  if (choice?.finish_reason)
21055
21087
  finishReason = choice.finish_reason;
21056
- const x = chunk.x_modelos;
21057
- if (x?.fee_grains)
21058
- feeGrains = Number(x.fee_grains) || feeGrains;
21088
+ feeGrains = readFee(chunk) || readFee(chunk.usage) || feeGrains;
21089
+ const usage = chunk.usage;
21090
+ if (usage) {
21091
+ promptTokens = usage.prompt_tokens ?? promptTokens;
21092
+ completionTokens = usage.completion_tokens ?? completionTokens;
21093
+ }
21059
21094
  }
21060
- return { content, toolCalls: Object.values(calls), finishReason, feeGrains };
21095
+ return { content, toolCalls: Object.values(calls), finishReason, feeGrains, promptTokens, completionTokens };
21061
21096
  };
21062
- try {
21063
- return await attempt(true);
21064
- } catch (e) {
21065
- await new Promise((r) => setTimeout(r, 1500));
21097
+ let lastErr;
21098
+ for (let i = 0;i < 3; i++) {
21066
21099
  try {
21067
21100
  return await attempt(true);
21068
- } catch {
21069
- throw e;
21101
+ } catch (e) {
21102
+ lastErr = e;
21103
+ const status = e.status;
21104
+ if (status && status >= 400 && status < 500)
21105
+ break;
21106
+ if (i < 2)
21107
+ await new Promise((r) => setTimeout(r, 1000 * (i + 1)));
21070
21108
  }
21071
21109
  }
21110
+ const err = lastErr;
21111
+ const reachable = err?.status !== undefined;
21112
+ if (!reachable)
21113
+ throw new Error(`inference API not reachable (${cfg.baseUrl}) — check your connection. ${err?.message ?? err?.code ?? ""}`.trim());
21114
+ throw new Error(`inference API error${err.status ? ` (HTTP ${err.status})` : ""}: ${err.message ?? "request rejected"}`);
21072
21115
  }
21073
21116
 
21074
21117
  // src/router/router.ts
@@ -21387,21 +21430,39 @@ async function runSubagent(cfg2, agent, task, ctx, onProgress, extraTools = [])
21387
21430
  const granted = base ? [...base, ...extraTools] : extraTools.length ? [...allTools(), ...extraTools] : null;
21388
21431
  const tools = granted ? granted.map((t) => ({ type: "function", function: { name: t.name, description: t.description, parameters: t.parameters } })) : toolSchemas();
21389
21432
  const lookup = (name) => granted ? granted.find((t) => t.name === name) : getTool(name);
21390
- const turnCtx = buildTurnContext(task, ctx.cwd, {});
21433
+ const env = `Working directory: ${ctx.cwd}
21434
+ Platform: ${process.platform}
21435
+ Today: ${new Date().toISOString().slice(0, 10)}`;
21436
+ const projSlice = loadProjectContext(ctx.cwd).slice(0, 4000);
21437
+ const turnCtx = (buildTurnContext(task, ctx.cwd, {}) || "").slice(0, 4000);
21438
+ const sys = [agent.systemPrompt, env, projSlice, turnCtx].filter(Boolean).join(`
21439
+
21440
+ `);
21391
21441
  const history = [
21392
- { role: "system", content: agent.systemPrompt + loadProjectContext(ctx.cwd) },
21393
- ...turnCtx ? [{ role: "system", content: turnCtx }] : [],
21442
+ { role: "system", content: sys },
21394
21443
  { role: "user", content: task }
21395
21444
  ];
21396
21445
  let feeGrains = 0;
21397
21446
  const routed = cfg2.autoRoute ? route(task)?.model : undefined;
21398
21447
  const subModel = cfg2.subagentModel || routed || agent.model || cfg2.model;
21448
+ onProgress?.(` ↳ ${agent.name} on ${subModel}`);
21449
+ const RESULT_CAP = Number(process.env.MODELCODE_MAX_TOOL_RESULT || "40000");
21450
+ const capResult = (s) => s.length <= RESULT_CAP ? s : `${s.slice(0, Math.floor(RESULT_CAP * 0.7))}
21451
+
21452
+ …[truncated ${s.length - RESULT_CAP} chars]…
21453
+
21454
+ ${s.slice(-Math.floor(RESULT_CAP * 0.2))}`;
21399
21455
  const finish = (text) => {
21400
21456
  fireEvent("subagentStop", ctx.cwd, { AGENT: agent.name, RESULT: text.slice(0, 4000) }).catch(() => {});
21401
21457
  return { text, feeGrains };
21402
21458
  };
21403
21459
  for (let step = 0;step < MAX_STEPS; step++) {
21404
- const res = await chat2(cfg2, history, tools, () => {}, subModel, 60000);
21460
+ let res;
21461
+ try {
21462
+ res = await chat2(cfg2, history, tools, () => {}, subModel, 120000);
21463
+ } catch (e) {
21464
+ return finish(`error: ${agent.name} (${subModel}) — ${e.message}`);
21465
+ }
21405
21466
  feeGrains += res.feeGrains;
21406
21467
  history.push({ role: "assistant", content: res.content || null, ...res.toolCalls.length ? { tool_calls: res.toolCalls } : {} });
21407
21468
  if (!res.toolCalls.length)
@@ -21423,7 +21484,7 @@ async function runSubagent(cfg2, agent, task, ctx, onProgress, extraTools = [])
21423
21484
  result = `error: ${e.message}`;
21424
21485
  }
21425
21486
  }
21426
- history.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content: result });
21487
+ history.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content: capResult(result) });
21427
21488
  }
21428
21489
  }
21429
21490
  return finish("(subagent hit the step limit without finishing)");
@@ -24254,4 +24315,4 @@ async function decryptWallet(enc, password, network = MAINNET) {
24254
24315
  }
24255
24316
  return w;
24256
24317
  }
24257
- export { exports_external, register, getTool, toolSchemas, renderDiff, shellInvocation, 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 };
24318
+ export { exports_external, register, getTool, toolSchemas, renderDiff, 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 };