miii-agent 0.1.21 → 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 +35 -210
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -190,8 +190,7 @@ async function* chat(entry, model, messages, tools, opts) {
190
190
  keep_alive: opts?.keep_alive ?? "10m",
191
191
  options
192
192
  };
193
- if (opts?.format) req.format = opts.format;
194
- else if (tools) req.tools = tools;
193
+ if (tools) req.tools = tools;
195
194
  try {
196
195
  stream = await client.chat(
197
196
  req
@@ -517,62 +516,6 @@ var init_client = __esm({
517
516
  }
518
517
  });
519
518
 
520
- // src/llm/grammar.ts
521
- function argProperties(props) {
522
- const out = {};
523
- for (const [key, spec] of Object.entries(props)) {
524
- const node = { type: spec.type };
525
- if (spec.enum && spec.enum.length) node.enum = spec.enum;
526
- out[key] = node;
527
- }
528
- return out;
529
- }
530
- function toolBranch(tool) {
531
- const args2 = {
532
- type: "object",
533
- additionalProperties: false,
534
- properties: argProperties(tool.input_schema.properties)
535
- };
536
- if (tool.input_schema.required && tool.input_schema.required.length) {
537
- args2.required = tool.input_schema.required;
538
- }
539
- return {
540
- type: "object",
541
- additionalProperties: false,
542
- required: ["name", "arguments"],
543
- properties: {
544
- name: { const: tool.name },
545
- arguments: args2
546
- }
547
- };
548
- }
549
- function respondBranch() {
550
- return {
551
- type: "object",
552
- additionalProperties: false,
553
- required: ["name", "arguments"],
554
- properties: {
555
- name: { const: RESPOND_ACTION },
556
- arguments: {
557
- type: "object",
558
- additionalProperties: false,
559
- required: ["message"],
560
- properties: { message: { type: "string" } }
561
- }
562
- }
563
- };
564
- }
565
- function buildToolGrammar(tools) {
566
- return { oneOf: [...tools.map(toolBranch), respondBranch()] };
567
- }
568
- var RESPOND_ACTION;
569
- var init_grammar = __esm({
570
- "src/llm/grammar.ts"() {
571
- "use strict";
572
- RESPOND_ACTION = "respond";
573
- }
574
- });
575
-
576
519
  // src/tools/paths.ts
577
520
  import { resolve, relative as relative2, isAbsolute, sep, join as join4 } from "path";
578
521
  import { homedir as homedir3 } from "os";
@@ -1265,15 +1208,8 @@ var init_context = __esm({
1265
1208
  });
1266
1209
 
1267
1210
  // src/prompt/system.ts
1268
- function buildSystemPrompt(tools, cwd, project, grammarMode = false) {
1211
+ function buildSystemPrompt(tools, cwd, project) {
1269
1212
  const toolLines = tools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
1270
- const actionProtocol = grammarMode ? `
1271
- # Action protocol (strict)
1272
- 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.
1273
- To use a tool: {"name": "<tool_name>", "arguments": { ...that tool's args }}
1274
- To give your final answer to the user: {"name": "respond", "arguments": {"message": "<your full answer here>"}}
1275
- 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.
1276
- ` : "";
1277
1213
  const projectSection = project && project.content.trim() ? `
1278
1214
  # ${CONTEXT_FILENAME} \u2014 project instructions (authoritative, read first)
1279
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 ? `
@@ -1358,7 +1294,7 @@ Ask in a numbered list. One round of questions per turn. Then wait.
1358
1294
  # Tools
1359
1295
  You have access to the following tools. Call them via the function-calling interface.
1360
1296
  ${toolLines}
1361
- ${actionProtocol}
1297
+
1362
1298
  # Loop semantics
1363
1299
  - When you need to act on the filesystem or run a command, emit a tool call.
1364
1300
  - After each tool result, decide: more tool calls, or a final plain-text answer.
@@ -1588,89 +1524,6 @@ function extractFirstJsonObject(s) {
1588
1524
  }
1589
1525
  return null;
1590
1526
  }
1591
- function parseGrammarAction(content, knownToolNames) {
1592
- if (!content) return null;
1593
- let raw = content.trim();
1594
- if (!raw.startsWith("{")) {
1595
- const found = extractFirstJsonObject(raw);
1596
- if (!found) return null;
1597
- raw = found.json;
1598
- }
1599
- let obj;
1600
- try {
1601
- obj = JSON.parse(raw);
1602
- } catch {
1603
- const found = extractFirstJsonObject(raw);
1604
- if (!found) return null;
1605
- try {
1606
- obj = JSON.parse(found.json);
1607
- } catch {
1608
- return null;
1609
- }
1610
- }
1611
- const name = typeof obj.name === "string" ? obj.name : void 0;
1612
- if (!name) return null;
1613
- let args2;
1614
- const wrapped = obj.arguments ?? obj.parameters ?? obj.input ?? obj.args;
1615
- if (typeof wrapped === "string") {
1616
- try {
1617
- const parsed = JSON.parse(wrapped);
1618
- args2 = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1619
- } catch {
1620
- args2 = {};
1621
- }
1622
- } else if (wrapped && typeof wrapped === "object" && !Array.isArray(wrapped)) {
1623
- args2 = wrapped;
1624
- } else {
1625
- const { name: _n, ...rest } = obj;
1626
- args2 = rest;
1627
- }
1628
- if (name === "respond") {
1629
- const message = typeof args2.message === "string" ? args2.message : "";
1630
- return { kind: "respond", message };
1631
- }
1632
- if (!knownToolNames.includes(name)) return null;
1633
- return { kind: "tool", name, arguments: args2 };
1634
- }
1635
- function streamRespondMessage(text) {
1636
- if (!/"name"\s*:\s*"respond"/.test(text)) return null;
1637
- const m = text.match(/"message"\s*:\s*"/);
1638
- if (!m || m.index == null) return null;
1639
- const start = m.index + m[0].length;
1640
- const escapes = {
1641
- n: "\n",
1642
- t: " ",
1643
- r: "\r",
1644
- b: "\b",
1645
- f: "\f",
1646
- '"': '"',
1647
- "\\": "\\",
1648
- "/": "/"
1649
- };
1650
- let out = "";
1651
- let i = start;
1652
- while (i < text.length) {
1653
- const ch = text[i];
1654
- if (ch === '"') return { message: out, complete: true };
1655
- if (ch === "\\") {
1656
- const nx = text[i + 1];
1657
- if (nx === void 0) break;
1658
- if (nx === "u") {
1659
- const hex = text.slice(i + 2, i + 6);
1660
- if (hex.length < 4) break;
1661
- out += String.fromCharCode(parseInt(hex, 16));
1662
- i += 6;
1663
- continue;
1664
- }
1665
- out += escapes[nx] ?? nx;
1666
- i += 2;
1667
- continue;
1668
- }
1669
- out += ch;
1670
- i++;
1671
- }
1672
- return { message: out, complete: false };
1673
- }
1674
1527
  function blocksFromOllama(text, tool_calls, knownToolNames = []) {
1675
1528
  const blocks = [];
1676
1529
  let finalText = text;
@@ -1752,9 +1605,7 @@ function markSeen(name, input, seen) {
1752
1605
  async function* runAgent(opts) {
1753
1606
  const { model, cwd, permissions, hooks, signal, num_ctx } = opts;
1754
1607
  const startTime = Date.now();
1755
- const useGrammar = false;
1756
- const system = buildSystemPrompt(TOOLS, cwd, loadProjectContext(cwd), useGrammar);
1757
- const grammar = useGrammar ? buildToolGrammar(TOOLS) : void 0;
1608
+ const system = buildSystemPrompt(TOOLS, cwd, loadProjectContext(cwd));
1758
1609
  const ollamaTools = toOllamaTools(TOOLS);
1759
1610
  const toolNames = TOOLS.map((t) => t.name);
1760
1611
  const effort = EFFORT_OPTIONS[loadConfig().effort ?? "medium"];
@@ -1770,8 +1621,6 @@ async function* runAgent(opts) {
1770
1621
  for (let turn = 0; turn < MAX_TURNS; turn++) {
1771
1622
  let text = "";
1772
1623
  let tool_calls;
1773
- let respondEmitted = 0;
1774
- let streamedRespond = false;
1775
1624
  let emittedText = false;
1776
1625
  let lastTail = "";
1777
1626
  let tailRepeats = 0;
@@ -1781,24 +1630,12 @@ async function* runAgent(opts) {
1781
1630
  const composedSignal = signal ? AbortSignal.any ? AbortSignal.any([signal, ac.signal]) : ac.signal : ac.signal;
1782
1631
  if (signal) signal.addEventListener("abort", () => ac.abort(), { once: true });
1783
1632
  try {
1784
- 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 })) {
1785
1634
  if (signal?.aborted) break;
1786
1635
  if (chunk.content) {
1787
1636
  text += chunk.content;
1788
- if (!useGrammar) {
1789
- emittedText = true;
1790
- yield { type: "text-delta", text: chunk.content };
1791
- } else {
1792
- const r = streamRespondMessage(text);
1793
- if (r) {
1794
- streamedRespond = true;
1795
- if (r.message.length > respondEmitted) {
1796
- emittedText = true;
1797
- yield { type: "text-delta", text: r.message.slice(respondEmitted) };
1798
- respondEmitted = r.message.length;
1799
- }
1800
- }
1801
- }
1637
+ emittedText = true;
1638
+ yield { type: "text-delta", text: chunk.content };
1802
1639
  if (text.length >= REPEAT_TAIL) {
1803
1640
  const tail = text.slice(-REPEAT_TAIL);
1804
1641
  if (tail === lastTail) {
@@ -1847,19 +1684,7 @@ async function* runAgent(opts) {
1847
1684
  };
1848
1685
  return history;
1849
1686
  }
1850
- let blocks;
1851
- if (useGrammar) {
1852
- const action = parseGrammarAction(text, toolNames);
1853
- if (action?.kind === "tool") {
1854
- blocks = [{ type: "tool_use", id: mintToolUseId(), name: action.name, input: action.arguments }];
1855
- } else {
1856
- const message = action?.kind === "respond" ? action.message : text.trim();
1857
- if (message && !streamedRespond) yield { type: "text-delta", text: message };
1858
- blocks = message ? [{ type: "text", text: message }] : [];
1859
- }
1860
- } else {
1861
- blocks = blocksFromOllama(text, tool_calls, toolNames);
1862
- }
1687
+ const blocks = blocksFromOllama(text, tool_calls, toolNames);
1863
1688
  const tool_uses = blocks.filter((b) => b.type === "tool_use");
1864
1689
  history.push({ role: "assistant", content: blocks });
1865
1690
  if (truncated && tool_uses.length > 0) {
@@ -1988,7 +1813,6 @@ var init_loop = __esm({
1988
1813
  "src/agent/loop.ts"() {
1989
1814
  "use strict";
1990
1815
  init_client();
1991
- init_grammar();
1992
1816
  init_paths();
1993
1817
  init_registry();
1994
1818
  init_validate();
@@ -2239,32 +2063,33 @@ import { Box, Text } from "ink";
2239
2063
  import { jsx, jsxs } from "react/jsx-runtime";
2240
2064
  function WelcomeBlock({ model, activeCtx, effort, cwd, updateAvailable }) {
2241
2065
  const ctxLabel = activeCtx != null ? `${Math.round(activeCtx / 1024)}k ctx` : "\u2014 ctx";
2242
- return /* @__PURE__ */ jsxs(
2243
- Box,
2244
- {
2245
- flexDirection: "column",
2246
- borderStyle: "round",
2247
- borderColor: "gray",
2248
- paddingX: 2,
2249
- marginBottom: 1,
2250
- children: [
2251
- /* @__PURE__ */ jsxs(Box, { gap: 2, children: [
2252
- /* @__PURE__ */ jsx(Text, { bold: true, color: "blue", children: "MIII CLI" }),
2253
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2254
- /* @__PURE__ */ jsx(Text, { children: model ?? "/models" }),
2255
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2256
- /* @__PURE__ */ jsx(Text, { children: ctxLabel }),
2257
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2258
- /* @__PURE__ */ jsxs(Text, { children: [
2259
- effort,
2260
- " effort"
2261
- ] })
2262
- ] }),
2263
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: cwd }),
2264
- updateAvailable && /* @__PURE__ */ jsx(Text, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: miii --update` })
2265
- ]
2266
- }
2267
- );
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
+ ] });
2268
2093
  }
2269
2094
 
2270
2095
  // src/ui/InputBar.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miii-agent",
3
- "version": "0.1.21",
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": {