miii-agent 0.1.22 → 0.1.23

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.
Files changed (2) hide show
  1. package/dist/cli.js +36 -255
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -163,30 +163,6 @@ async function modelContext(entry, model) {
163
163
  throw err;
164
164
  }
165
165
  }
166
- async function paramCountB(entry, model) {
167
- try {
168
- const info = await makeClient(entry).show({ model });
169
- const details = info.details;
170
- if (details?.parameter_size) {
171
- const m = details.parameter_size.match(/([\d.]+)\s*([BM])/i);
172
- if (m) {
173
- const n = parseFloat(m[1]);
174
- if (!isNaN(n)) return m[2].toUpperCase() === "M" ? n / 1e3 : n;
175
- }
176
- }
177
- const modelInfo = info.model_info;
178
- if (modelInfo) {
179
- const key = Object.keys(modelInfo).find((k) => k.endsWith("parameter_count"));
180
- if (key) {
181
- const val = Number(modelInfo[key]);
182
- if (!isNaN(val) && val > 0) return val / 1e9;
183
- }
184
- }
185
- return null;
186
- } catch {
187
- return null;
188
- }
189
- }
190
166
  async function* chat(entry, model, messages, tools, opts) {
191
167
  if (opts?.signal?.aborted) return;
192
168
  const signal = opts?.signal;
@@ -214,8 +190,7 @@ async function* chat(entry, model, messages, tools, opts) {
214
190
  keep_alive: opts?.keep_alive ?? "10m",
215
191
  options
216
192
  };
217
- if (opts?.format) req.format = opts.format;
218
- else if (tools) req.tools = tools;
193
+ if (tools) req.tools = tools;
219
194
  try {
220
195
  stream = await client.chat(
221
196
  req
@@ -508,9 +483,6 @@ var init_openai = __esm({
508
483
  function active() {
509
484
  return resolveProvider();
510
485
  }
511
- function providerName() {
512
- return active().name;
513
- }
514
486
  function isAvailable3() {
515
487
  const { entry } = active();
516
488
  return entry.type === "ollama" ? isAvailable(entry) : isAvailable2(entry);
@@ -527,16 +499,6 @@ async function modelContext3(model) {
527
499
  const { entry } = active();
528
500
  return entry.type === "ollama" ? modelContext(entry, model) : modelContext2(entry, model);
529
501
  }
530
- async function modelParamCountB(model) {
531
- const { entry } = active();
532
- if (entry.type !== "ollama") return null;
533
- const key = `${entry.baseUrl}:${model}`;
534
- const cached = paramCountCache.get(key);
535
- if (cached !== void 0) return cached;
536
- const params = await paramCountB(entry, model);
537
- paramCountCache.set(key, params);
538
- return params;
539
- }
540
502
  async function* chat3(model, messages, tools, opts) {
541
503
  const { entry } = active();
542
504
  if (entry.type === "ollama") {
@@ -545,70 +507,12 @@ async function* chat3(model, messages, tools, opts) {
545
507
  yield* chat2(entry, model, messages, tools, opts);
546
508
  }
547
509
  }
548
- var paramCountCache;
549
510
  var init_client = __esm({
550
511
  "src/llm/client.ts"() {
551
512
  "use strict";
552
513
  init_config();
553
514
  init_ollama();
554
515
  init_openai();
555
- paramCountCache = /* @__PURE__ */ new Map();
556
- }
557
- });
558
-
559
- // src/llm/grammar.ts
560
- function argProperties(props) {
561
- const out = {};
562
- for (const [key, spec] of Object.entries(props)) {
563
- const node = { type: spec.type };
564
- if (spec.enum && spec.enum.length) node.enum = spec.enum;
565
- out[key] = node;
566
- }
567
- return out;
568
- }
569
- function toolBranch(tool) {
570
- const args2 = {
571
- type: "object",
572
- additionalProperties: false,
573
- properties: argProperties(tool.input_schema.properties)
574
- };
575
- if (tool.input_schema.required && tool.input_schema.required.length) {
576
- args2.required = tool.input_schema.required;
577
- }
578
- return {
579
- type: "object",
580
- additionalProperties: false,
581
- required: ["name", "arguments"],
582
- properties: {
583
- name: { const: tool.name },
584
- arguments: args2
585
- }
586
- };
587
- }
588
- function respondBranch() {
589
- return {
590
- type: "object",
591
- additionalProperties: false,
592
- required: ["name", "arguments"],
593
- properties: {
594
- name: { const: RESPOND_ACTION },
595
- arguments: {
596
- type: "object",
597
- additionalProperties: false,
598
- required: ["message"],
599
- properties: { message: { type: "string" } }
600
- }
601
- }
602
- };
603
- }
604
- function buildToolGrammar(tools) {
605
- return { oneOf: [...tools.map(toolBranch), respondBranch()] };
606
- }
607
- var RESPOND_ACTION;
608
- var init_grammar = __esm({
609
- "src/llm/grammar.ts"() {
610
- "use strict";
611
- RESPOND_ACTION = "respond";
612
516
  }
613
517
  });
614
518
 
@@ -1304,15 +1208,8 @@ var init_context = __esm({
1304
1208
  });
1305
1209
 
1306
1210
  // src/prompt/system.ts
1307
- function buildSystemPrompt(tools, cwd, project, grammarMode = false) {
1211
+ function buildSystemPrompt(tools, cwd, project) {
1308
1212
  const toolLines = tools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
1309
- const actionProtocol = grammarMode ? `
1310
- # Action protocol (strict)
1311
- Every reply is exactly ONE JSON action object, nothing else \u2014 no prose outside it, no markdown, no fences. Decoding is grammar-constrained, so malformed output is impossible; your only job is to choose the right action.
1312
- To use a tool: {"name": "<tool_name>", "arguments": { ...that tool's args }}
1313
- To give your final answer to the user: {"name": "respond", "arguments": {"message": "<your full answer here>"}}
1314
- Call tools until the GOAL is met, then emit a single "respond" action with the complete answer. The "respond" action is the ONLY way to end the turn and talk to the user \u2014 never put your final answer in a tool call.
1315
- ` : "";
1316
1213
  const projectSection = project && project.content.trim() ? `
1317
1214
  # ${CONTEXT_FILENAME} \u2014 project instructions (authoritative, read first)
1318
1215
  The user maintains ${CONTEXT_FILENAME} at ${project.source} to steer how you work in this project: conventions, commands, architecture, do's and don'ts. Treat it as direct instruction from the user, higher priority than your defaults. When it conflicts with a default rule below, ${CONTEXT_FILENAME} wins (except permissions and safety, which you never override).${project.truncated ? `
@@ -1397,7 +1294,7 @@ Ask in a numbered list. One round of questions per turn. Then wait.
1397
1294
  # Tools
1398
1295
  You have access to the following tools. Call them via the function-calling interface.
1399
1296
  ${toolLines}
1400
- ${actionProtocol}
1297
+
1401
1298
  # Loop semantics
1402
1299
  - When you need to act on the filesystem or run a command, emit a tool call.
1403
1300
  - After each tool result, decide: more tool calls, or a final plain-text answer.
@@ -1627,89 +1524,6 @@ function extractFirstJsonObject(s) {
1627
1524
  }
1628
1525
  return null;
1629
1526
  }
1630
- function parseGrammarAction(content, knownToolNames) {
1631
- if (!content) return null;
1632
- let raw = content.trim();
1633
- if (!raw.startsWith("{")) {
1634
- const found = extractFirstJsonObject(raw);
1635
- if (!found) return null;
1636
- raw = found.json;
1637
- }
1638
- let obj;
1639
- try {
1640
- obj = JSON.parse(raw);
1641
- } catch {
1642
- const found = extractFirstJsonObject(raw);
1643
- if (!found) return null;
1644
- try {
1645
- obj = JSON.parse(found.json);
1646
- } catch {
1647
- return null;
1648
- }
1649
- }
1650
- const name = typeof obj.name === "string" ? obj.name : void 0;
1651
- if (!name) return null;
1652
- let args2;
1653
- const wrapped = obj.arguments ?? obj.parameters ?? obj.input ?? obj.args;
1654
- if (typeof wrapped === "string") {
1655
- try {
1656
- const parsed = JSON.parse(wrapped);
1657
- args2 = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1658
- } catch {
1659
- args2 = {};
1660
- }
1661
- } else if (wrapped && typeof wrapped === "object" && !Array.isArray(wrapped)) {
1662
- args2 = wrapped;
1663
- } else {
1664
- const { name: _n, ...rest } = obj;
1665
- args2 = rest;
1666
- }
1667
- if (name === "respond") {
1668
- const message = typeof args2.message === "string" ? args2.message : "";
1669
- return { kind: "respond", message };
1670
- }
1671
- if (!knownToolNames.includes(name)) return null;
1672
- return { kind: "tool", name, arguments: args2 };
1673
- }
1674
- function streamRespondMessage(text) {
1675
- if (!/"name"\s*:\s*"respond"/.test(text)) return null;
1676
- const m = text.match(/"message"\s*:\s*"/);
1677
- if (!m || m.index == null) return null;
1678
- const start = m.index + m[0].length;
1679
- const escapes = {
1680
- n: "\n",
1681
- t: " ",
1682
- r: "\r",
1683
- b: "\b",
1684
- f: "\f",
1685
- '"': '"',
1686
- "\\": "\\",
1687
- "/": "/"
1688
- };
1689
- let out = "";
1690
- let i = start;
1691
- while (i < text.length) {
1692
- const ch = text[i];
1693
- if (ch === '"') return { message: out, complete: true };
1694
- if (ch === "\\") {
1695
- const nx = text[i + 1];
1696
- if (nx === void 0) break;
1697
- if (nx === "u") {
1698
- const hex = text.slice(i + 2, i + 6);
1699
- if (hex.length < 4) break;
1700
- out += String.fromCharCode(parseInt(hex, 16));
1701
- i += 6;
1702
- continue;
1703
- }
1704
- out += escapes[nx] ?? nx;
1705
- i += 2;
1706
- continue;
1707
- }
1708
- out += ch;
1709
- i++;
1710
- }
1711
- return { message: out, complete: false };
1712
- }
1713
1527
  function blocksFromOllama(text, tool_calls, knownToolNames = []) {
1714
1528
  const blocks = [];
1715
1529
  let finalText = text;
@@ -1791,13 +1605,7 @@ function markSeen(name, input, seen) {
1791
1605
  async function* runAgent(opts) {
1792
1606
  const { model, cwd, permissions, hooks, signal, num_ctx } = opts;
1793
1607
  const startTime = Date.now();
1794
- let useGrammar = false;
1795
- if (providerName() === "ollama") {
1796
- const params = await modelParamCountB(model);
1797
- useGrammar = params == null || params <= GRAMMAR_MAX_PARAMS_B;
1798
- }
1799
- const system = buildSystemPrompt(TOOLS, cwd, loadProjectContext(cwd), useGrammar);
1800
- const grammar = useGrammar ? buildToolGrammar(TOOLS) : void 0;
1608
+ const system = buildSystemPrompt(TOOLS, cwd, loadProjectContext(cwd));
1801
1609
  const ollamaTools = toOllamaTools(TOOLS);
1802
1610
  const toolNames = TOOLS.map((t) => t.name);
1803
1611
  const effort = EFFORT_OPTIONS[loadConfig().effort ?? "medium"];
@@ -1813,8 +1621,6 @@ async function* runAgent(opts) {
1813
1621
  for (let turn = 0; turn < MAX_TURNS; turn++) {
1814
1622
  let text = "";
1815
1623
  let tool_calls;
1816
- let respondEmitted = 0;
1817
- let streamedRespond = false;
1818
1624
  let emittedText = false;
1819
1625
  let lastTail = "";
1820
1626
  let tailRepeats = 0;
@@ -1824,24 +1630,12 @@ async function* runAgent(opts) {
1824
1630
  const composedSignal = signal ? AbortSignal.any ? AbortSignal.any([signal, ac.signal]) : ac.signal : ac.signal;
1825
1631
  if (signal) signal.addEventListener("abort", () => ac.abort(), { once: true });
1826
1632
  try {
1827
- for await (const chunk of chat3(model, toOllamaMessages(history, system), useGrammar ? void 0 : ollamaTools, { signal: composedSignal, num_ctx, num_predict: effort.num_predict, temperature: effort.temperature, format: grammar })) {
1633
+ for await (const chunk of chat3(model, toOllamaMessages(history, system), ollamaTools, { signal: composedSignal, num_ctx, num_predict: effort.num_predict, temperature: effort.temperature })) {
1828
1634
  if (signal?.aborted) break;
1829
1635
  if (chunk.content) {
1830
1636
  text += chunk.content;
1831
- if (!useGrammar) {
1832
- emittedText = true;
1833
- yield { type: "text-delta", text: chunk.content };
1834
- } else {
1835
- const r = streamRespondMessage(text);
1836
- if (r) {
1837
- streamedRespond = true;
1838
- if (r.message.length > respondEmitted) {
1839
- emittedText = true;
1840
- yield { type: "text-delta", text: r.message.slice(respondEmitted) };
1841
- respondEmitted = r.message.length;
1842
- }
1843
- }
1844
- }
1637
+ emittedText = true;
1638
+ yield { type: "text-delta", text: chunk.content };
1845
1639
  if (text.length >= REPEAT_TAIL) {
1846
1640
  const tail = text.slice(-REPEAT_TAIL);
1847
1641
  if (tail === lastTail) {
@@ -1890,19 +1684,7 @@ async function* runAgent(opts) {
1890
1684
  };
1891
1685
  return history;
1892
1686
  }
1893
- let blocks;
1894
- if (useGrammar) {
1895
- const action = parseGrammarAction(text, toolNames);
1896
- if (action?.kind === "tool") {
1897
- blocks = [{ type: "tool_use", id: mintToolUseId(), name: action.name, input: action.arguments }];
1898
- } else {
1899
- const message = action?.kind === "respond" ? action.message : text.trim();
1900
- if (message && !streamedRespond) yield { type: "text-delta", text: message };
1901
- blocks = message ? [{ type: "text", text: message }] : [];
1902
- }
1903
- } else {
1904
- blocks = blocksFromOllama(text, tool_calls, toolNames);
1905
- }
1687
+ const blocks = blocksFromOllama(text, tool_calls, toolNames);
1906
1688
  const tool_uses = blocks.filter((b) => b.type === "tool_use");
1907
1689
  history.push({ role: "assistant", content: blocks });
1908
1690
  if (truncated && tool_uses.length > 0) {
@@ -2026,12 +1808,11 @@ async function* runAgent(opts) {
2026
1808
  yield { type: "done", prompt_tokens: promptTokens, eval_tokens: evalTokens };
2027
1809
  return history;
2028
1810
  }
2029
- var MAX_TURNS, REPEAT_TAIL, REPEAT_KILL, GRAMMAR_MAX_PARAMS_B, BIG_WRITE_TOOLS;
1811
+ var MAX_TURNS, REPEAT_TAIL, REPEAT_KILL, BIG_WRITE_TOOLS;
2030
1812
  var init_loop = __esm({
2031
1813
  "src/agent/loop.ts"() {
2032
1814
  "use strict";
2033
1815
  init_client();
2034
- init_grammar();
2035
1816
  init_paths();
2036
1817
  init_registry();
2037
1818
  init_validate();
@@ -2043,7 +1824,6 @@ var init_loop = __esm({
2043
1824
  MAX_TURNS = 25;
2044
1825
  REPEAT_TAIL = 120;
2045
1826
  REPEAT_KILL = 4;
2046
- GRAMMAR_MAX_PARAMS_B = 14;
2047
1827
  BIG_WRITE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit_file"]);
2048
1828
  }
2049
1829
  });
@@ -2283,32 +2063,33 @@ import { Box, Text } from "ink";
2283
2063
  import { jsx, jsxs } from "react/jsx-runtime";
2284
2064
  function WelcomeBlock({ model, activeCtx, effort, cwd, updateAvailable }) {
2285
2065
  const ctxLabel = activeCtx != null ? `${Math.round(activeCtx / 1024)}k ctx` : "\u2014 ctx";
2286
- return /* @__PURE__ */ jsxs(
2287
- Box,
2288
- {
2289
- flexDirection: "column",
2290
- borderStyle: "round",
2291
- borderColor: "gray",
2292
- paddingX: 2,
2293
- marginBottom: 1,
2294
- children: [
2295
- /* @__PURE__ */ jsxs(Box, { gap: 2, children: [
2296
- /* @__PURE__ */ jsx(Text, { bold: true, color: "blue", children: "MIII CLI" }),
2297
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2298
- /* @__PURE__ */ jsx(Text, { children: model ?? "/models" }),
2299
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2300
- /* @__PURE__ */ jsx(Text, { children: ctxLabel }),
2301
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2302
- /* @__PURE__ */ jsxs(Text, { children: [
2303
- effort,
2304
- " effort"
2305
- ] })
2306
- ] }),
2307
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: cwd }),
2308
- updateAvailable && /* @__PURE__ */ jsx(Text, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: miii --update` })
2309
- ]
2310
- }
2311
- );
2066
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
2067
+ /* @__PURE__ */ jsxs(
2068
+ Box,
2069
+ {
2070
+ flexDirection: "column",
2071
+ borderStyle: "round",
2072
+ borderColor: "gray",
2073
+ paddingX: 2,
2074
+ children: [
2075
+ /* @__PURE__ */ jsxs(Box, { gap: 2, children: [
2076
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "blue", children: "MIII CLI" }),
2077
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2078
+ /* @__PURE__ */ jsx(Text, { children: model ?? "/models" }),
2079
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2080
+ /* @__PURE__ */ jsx(Text, { children: ctxLabel }),
2081
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2082
+ /* @__PURE__ */ jsxs(Text, { children: [
2083
+ effort,
2084
+ " effort"
2085
+ ] })
2086
+ ] }),
2087
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: cwd })
2088
+ ]
2089
+ }
2090
+ ),
2091
+ updateAvailable && /* @__PURE__ */ jsx(Text, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: miii --update` })
2092
+ ] });
2312
2093
  }
2313
2094
 
2314
2095
  // src/ui/InputBar.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miii-agent",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "Cursor / Claude Code, but local. An offline AI pair-programmer in your terminal, powered by Ollama. Private by default, free forever.",
5
5
  "type": "module",
6
6
  "bin": {