miii-agent 0.1.20 → 0.1.22
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 +104 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -89,8 +89,8 @@ var init_config = __esm({
|
|
|
89
89
|
"src/config.ts"() {
|
|
90
90
|
"use strict";
|
|
91
91
|
EFFORT_OPTIONS = {
|
|
92
|
-
low: { temperature: 0.2, num_predict:
|
|
93
|
-
medium: { temperature: 0.7, num_predict:
|
|
92
|
+
low: { temperature: 0.2, num_predict: 8192 },
|
|
93
|
+
medium: { temperature: 0.7, num_predict: 16384 },
|
|
94
94
|
high: { temperature: 1, num_predict: -1 }
|
|
95
95
|
};
|
|
96
96
|
CONFIG_DIR = join(homedir(), ".miii");
|
|
@@ -249,6 +249,7 @@ async function* chat(entry, model, messages, tools, opts) {
|
|
|
249
249
|
content: stripHarmony(chunk.message.content),
|
|
250
250
|
thinking: stripHarmony(chunk.message.thinking),
|
|
251
251
|
done: chunk.done,
|
|
252
|
+
done_reason: chunk.done_reason,
|
|
252
253
|
tool_calls: chunk.message.tool_calls,
|
|
253
254
|
prompt_eval_count: chunk.prompt_eval_count,
|
|
254
255
|
eval_count: chunk.eval_count
|
|
@@ -384,6 +385,7 @@ async function* chat2(entry, model, messages, tools, opts) {
|
|
|
384
385
|
if (oaTools) body.tools = oaTools;
|
|
385
386
|
if (opts?.num_predict && opts.num_predict > 0) body.max_tokens = opts.num_predict;
|
|
386
387
|
const toolCallAccum = /* @__PURE__ */ new Map();
|
|
388
|
+
let lastFinishReason;
|
|
387
389
|
const TIMEOUT_MS = 18e4;
|
|
388
390
|
const timeoutSignal = AbortSignal.timeout(TIMEOUT_MS);
|
|
389
391
|
const combinedSignal = opts?.signal && typeof AbortSignal.any === "function" ? AbortSignal.any([opts.signal, timeoutSignal]) : opts?.signal ?? timeoutSignal;
|
|
@@ -423,6 +425,7 @@ async function* chat2(entry, model, messages, tools, opts) {
|
|
|
423
425
|
if (!choices || choices.length === 0) continue;
|
|
424
426
|
const delta = choices[0].delta ?? {};
|
|
425
427
|
const finishReason = choices[0].finish_reason;
|
|
428
|
+
if (finishReason) lastFinishReason = finishReason;
|
|
426
429
|
if (delta.content) {
|
|
427
430
|
yield { content: delta.content, done: false };
|
|
428
431
|
}
|
|
@@ -487,6 +490,9 @@ async function* chat2(entry, model, messages, tools, opts) {
|
|
|
487
490
|
yield {
|
|
488
491
|
content: "",
|
|
489
492
|
done: true,
|
|
493
|
+
// OpenAI signals a hit token cap as finish_reason 'length'; normalize to the
|
|
494
|
+
// Ollama spelling so the agent loop can detect truncation uniformly.
|
|
495
|
+
done_reason: lastFinishReason === "length" ? "length" : lastFinishReason ?? void 0,
|
|
490
496
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
491
497
|
};
|
|
492
498
|
}
|
|
@@ -749,7 +755,10 @@ var init_edit_file = __esm({
|
|
|
749
755
|
handler: ({ path, old_str, new_str, replace_all }) => {
|
|
750
756
|
try {
|
|
751
757
|
if (old_str === new_str) {
|
|
752
|
-
return {
|
|
758
|
+
return {
|
|
759
|
+
content: `old_str and new_str are identical \u2014 nothing to change in ${path}. If the file is already correct, do NOT edit again: finish with the respond action and tell the user it is done.`,
|
|
760
|
+
is_error: true
|
|
761
|
+
};
|
|
753
762
|
}
|
|
754
763
|
const abs = confinePath(path);
|
|
755
764
|
const src = readFileSync3(abs, "utf-8");
|
|
@@ -1218,6 +1227,31 @@ function toZod(schema) {
|
|
|
1218
1227
|
}
|
|
1219
1228
|
return z.object(shape).passthrough();
|
|
1220
1229
|
}
|
|
1230
|
+
function exampleValue(spec) {
|
|
1231
|
+
if (spec.enum && spec.enum.length) return spec.enum[0];
|
|
1232
|
+
switch (spec.type) {
|
|
1233
|
+
case "number":
|
|
1234
|
+
case "integer":
|
|
1235
|
+
return 0;
|
|
1236
|
+
case "boolean":
|
|
1237
|
+
return false;
|
|
1238
|
+
case "array":
|
|
1239
|
+
return [];
|
|
1240
|
+
case "object":
|
|
1241
|
+
return {};
|
|
1242
|
+
default:
|
|
1243
|
+
return "...";
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function exampleInput(schema) {
|
|
1247
|
+
const required = schema.required ?? [];
|
|
1248
|
+
const obj = {};
|
|
1249
|
+
for (const key of required) {
|
|
1250
|
+
const spec = schema.properties[key];
|
|
1251
|
+
if (spec) obj[key] = exampleValue(spec);
|
|
1252
|
+
}
|
|
1253
|
+
return JSON.stringify(obj);
|
|
1254
|
+
}
|
|
1221
1255
|
function validateInput(schema, input) {
|
|
1222
1256
|
const result = toZod(schema).safeParse(input ?? {});
|
|
1223
1257
|
if (result.success) return null;
|
|
@@ -1614,8 +1648,22 @@ function parseGrammarAction(content, knownToolNames) {
|
|
|
1614
1648
|
}
|
|
1615
1649
|
}
|
|
1616
1650
|
const name = typeof obj.name === "string" ? obj.name : void 0;
|
|
1617
|
-
const args2 = obj.arguments ?? {};
|
|
1618
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
|
+
}
|
|
1619
1667
|
if (name === "respond") {
|
|
1620
1668
|
const message = typeof args2.message === "string" ? args2.message : "";
|
|
1621
1669
|
return { kind: "respond", message };
|
|
@@ -1707,6 +1755,30 @@ function readGuard(name, input, seen) {
|
|
|
1707
1755
|
const verb = name === "edit_file" ? "edit" : "overwrite";
|
|
1708
1756
|
return `Refusing to ${verb} ${p}: you have not read it this turn. Call read_file on ${p} first, then retry the ${name}.`;
|
|
1709
1757
|
}
|
|
1758
|
+
function unwrapEnvelope(name, input) {
|
|
1759
|
+
if (!("arguments" in input)) return input;
|
|
1760
|
+
if ("name" in input && input.name !== name) return input;
|
|
1761
|
+
let args2 = input.arguments;
|
|
1762
|
+
if (typeof args2 === "string") {
|
|
1763
|
+
try {
|
|
1764
|
+
args2 = JSON.parse(args2);
|
|
1765
|
+
} catch {
|
|
1766
|
+
return input;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
if (args2 && typeof args2 === "object" && !Array.isArray(args2)) return args2;
|
|
1770
|
+
return input;
|
|
1771
|
+
}
|
|
1772
|
+
function splitWriteHint(name, cause) {
|
|
1773
|
+
const lead = cause === "truncated" ? `Your response was cut off at the output token limit, so this ${name} call is incomplete and was NOT run.` : `Your ${name} call arrived with missing or garbled arguments \u2014 usually the response was cut off or mangled while writing a large value. It was NOT run.`;
|
|
1774
|
+
return `${lead} Do not resend the whole file in one call. Instead create the file with write_file containing only the first portion, then append the rest with successive edit_file calls. Keep each call small.`;
|
|
1775
|
+
}
|
|
1776
|
+
function looksTruncatedWrite(name, input) {
|
|
1777
|
+
if (!BIG_WRITE_TOOLS.has(name)) return false;
|
|
1778
|
+
if (typeof input.path !== "string" || !input.path) return true;
|
|
1779
|
+
if (name === "write_file" && typeof input.content !== "string") return true;
|
|
1780
|
+
return false;
|
|
1781
|
+
}
|
|
1710
1782
|
function markSeen(name, input, seen) {
|
|
1711
1783
|
if (name !== "read_file" && name !== "edit_file" && name !== "write_file") return;
|
|
1712
1784
|
const p = input.path;
|
|
@@ -1743,9 +1815,11 @@ async function* runAgent(opts) {
|
|
|
1743
1815
|
let tool_calls;
|
|
1744
1816
|
let respondEmitted = 0;
|
|
1745
1817
|
let streamedRespond = false;
|
|
1818
|
+
let emittedText = false;
|
|
1746
1819
|
let lastTail = "";
|
|
1747
1820
|
let tailRepeats = 0;
|
|
1748
1821
|
let streamLooped = false;
|
|
1822
|
+
let truncated = false;
|
|
1749
1823
|
const ac = new AbortController();
|
|
1750
1824
|
const composedSignal = signal ? AbortSignal.any ? AbortSignal.any([signal, ac.signal]) : ac.signal : ac.signal;
|
|
1751
1825
|
if (signal) signal.addEventListener("abort", () => ac.abort(), { once: true });
|
|
@@ -1755,12 +1829,14 @@ async function* runAgent(opts) {
|
|
|
1755
1829
|
if (chunk.content) {
|
|
1756
1830
|
text += chunk.content;
|
|
1757
1831
|
if (!useGrammar) {
|
|
1832
|
+
emittedText = true;
|
|
1758
1833
|
yield { type: "text-delta", text: chunk.content };
|
|
1759
1834
|
} else {
|
|
1760
1835
|
const r = streamRespondMessage(text);
|
|
1761
1836
|
if (r) {
|
|
1762
1837
|
streamedRespond = true;
|
|
1763
1838
|
if (r.message.length > respondEmitted) {
|
|
1839
|
+
emittedText = true;
|
|
1764
1840
|
yield { type: "text-delta", text: r.message.slice(respondEmitted) };
|
|
1765
1841
|
respondEmitted = r.message.length;
|
|
1766
1842
|
}
|
|
@@ -1781,7 +1857,7 @@ async function* runAgent(opts) {
|
|
|
1781
1857
|
}
|
|
1782
1858
|
}
|
|
1783
1859
|
}
|
|
1784
|
-
if (chunk.thinking) {
|
|
1860
|
+
if (chunk.thinking && !emittedText) {
|
|
1785
1861
|
yield { type: "thinking-delta", text: chunk.thinking };
|
|
1786
1862
|
}
|
|
1787
1863
|
if (chunk.tool_calls && chunk.tool_calls.length > 0) {
|
|
@@ -1790,6 +1866,7 @@ async function* runAgent(opts) {
|
|
|
1790
1866
|
if (chunk.done) {
|
|
1791
1867
|
promptTokens += chunk.prompt_eval_count ?? 0;
|
|
1792
1868
|
evalTokens += chunk.eval_count ?? 0;
|
|
1869
|
+
if (chunk.done_reason === "length") truncated = true;
|
|
1793
1870
|
}
|
|
1794
1871
|
}
|
|
1795
1872
|
} catch (err) {
|
|
@@ -1828,6 +1905,19 @@ async function* runAgent(opts) {
|
|
|
1828
1905
|
}
|
|
1829
1906
|
const tool_uses = blocks.filter((b) => b.type === "tool_use");
|
|
1830
1907
|
history.push({ role: "assistant", content: blocks });
|
|
1908
|
+
if (truncated && tool_uses.length > 0) {
|
|
1909
|
+
const results2 = tool_uses.map((use) => ({
|
|
1910
|
+
type: "tool_result",
|
|
1911
|
+
tool_use_id: use.id,
|
|
1912
|
+
content: splitWriteHint(use.name, "truncated"),
|
|
1913
|
+
is_error: true
|
|
1914
|
+
}));
|
|
1915
|
+
for (const u of tool_uses) yield { type: "tool-use", block: u };
|
|
1916
|
+
for (const r of results2) yield { type: "tool-result", block: r };
|
|
1917
|
+
history.push({ role: "user", content: results2 });
|
|
1918
|
+
yield { type: "turn-end", stop_reason: "tool_use" };
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1831
1921
|
if (tool_uses.length === 0) {
|
|
1832
1922
|
yield { type: "turn-end", stop_reason: "end_turn" };
|
|
1833
1923
|
break;
|
|
@@ -1862,12 +1952,14 @@ async function* runAgent(opts) {
|
|
|
1862
1952
|
yield { type: "tool-result", block: r2 };
|
|
1863
1953
|
continue;
|
|
1864
1954
|
}
|
|
1955
|
+
use.input = unwrapEnvelope(use.name, use.input);
|
|
1865
1956
|
const invalid = validateInput(tool.input_schema, use.input);
|
|
1866
1957
|
if (invalid) {
|
|
1958
|
+
const content = looksTruncatedWrite(use.name, use.input) ? splitWriteHint(use.name, "garbled") : `${invalid} for ${use.name}. Pass the arguments directly as the tool input \u2014 do NOT wrap them in {"name":...,"arguments":...}. Correct shape: ${exampleInput(tool.input_schema)}. Retry with all required fields.`;
|
|
1867
1959
|
const r2 = {
|
|
1868
1960
|
type: "tool_result",
|
|
1869
1961
|
tool_use_id: use.id,
|
|
1870
|
-
content
|
|
1962
|
+
content,
|
|
1871
1963
|
is_error: true
|
|
1872
1964
|
};
|
|
1873
1965
|
results.push(r2);
|
|
@@ -1934,7 +2026,7 @@ async function* runAgent(opts) {
|
|
|
1934
2026
|
yield { type: "done", prompt_tokens: promptTokens, eval_tokens: evalTokens };
|
|
1935
2027
|
return history;
|
|
1936
2028
|
}
|
|
1937
|
-
var MAX_TURNS, REPEAT_TAIL, REPEAT_KILL, GRAMMAR_MAX_PARAMS_B;
|
|
2029
|
+
var MAX_TURNS, REPEAT_TAIL, REPEAT_KILL, GRAMMAR_MAX_PARAMS_B, BIG_WRITE_TOOLS;
|
|
1938
2030
|
var init_loop = __esm({
|
|
1939
2031
|
"src/agent/loop.ts"() {
|
|
1940
2032
|
"use strict";
|
|
@@ -1952,6 +2044,7 @@ var init_loop = __esm({
|
|
|
1952
2044
|
REPEAT_TAIL = 120;
|
|
1953
2045
|
REPEAT_KILL = 4;
|
|
1954
2046
|
GRAMMAR_MAX_PARAMS_B = 14;
|
|
2047
|
+
BIG_WRITE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit_file"]);
|
|
1955
2048
|
}
|
|
1956
2049
|
});
|
|
1957
2050
|
|
|
@@ -2670,7 +2763,7 @@ import { Box as Box12, Text as Text12, Static } from "ink";
|
|
|
2670
2763
|
// src/ui/markdown.ts
|
|
2671
2764
|
import { Marked } from "marked";
|
|
2672
2765
|
import { markedTerminal } from "marked-terminal";
|
|
2673
|
-
import { highlight } from "cli-highlight";
|
|
2766
|
+
import { highlight, supportsLanguage } from "cli-highlight";
|
|
2674
2767
|
|
|
2675
2768
|
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
2676
2769
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
@@ -3194,7 +3287,7 @@ var theme = {
|
|
|
3194
3287
|
// faint rule
|
|
3195
3288
|
};
|
|
3196
3289
|
function highlightCode(code, lang) {
|
|
3197
|
-
if (!lang) return code;
|
|
3290
|
+
if (!lang || !supportsLanguage(lang)) return code;
|
|
3198
3291
|
try {
|
|
3199
3292
|
return highlight(code, { language: lang, ignoreIllegals: true });
|
|
3200
3293
|
} catch {
|
|
@@ -3292,7 +3385,7 @@ import { Box as Box10, Text as Text10 } from "ink";
|
|
|
3292
3385
|
|
|
3293
3386
|
// src/ui/ToolBlock.tsx
|
|
3294
3387
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
3295
|
-
import { highlight as highlight2 } from "cli-highlight";
|
|
3388
|
+
import { highlight as highlight2, supportsLanguage as supportsLanguage2 } from "cli-highlight";
|
|
3296
3389
|
|
|
3297
3390
|
// src/ui/toolExpand.ts
|
|
3298
3391
|
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
@@ -3427,7 +3520,7 @@ function langFromPath(path) {
|
|
|
3427
3520
|
return ext ? EXT_LANG[ext] : void 0;
|
|
3428
3521
|
}
|
|
3429
3522
|
function highlightLine(text, lang) {
|
|
3430
|
-
if (!lang) return text;
|
|
3523
|
+
if (!lang || !supportsLanguage2(lang)) return text;
|
|
3431
3524
|
try {
|
|
3432
3525
|
return highlight2(text, { language: lang, ignoreIllegals: true });
|
|
3433
3526
|
} catch {
|
|
@@ -4628,7 +4721,6 @@ function App() {
|
|
|
4628
4721
|
header: /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd })
|
|
4629
4722
|
}
|
|
4630
4723
|
),
|
|
4631
|
-
updateAvailable && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: `\u2191 update available: v${updateAvailable} \u2014 run: miii --update` }) }),
|
|
4632
4724
|
input.startsWith("/") && /* @__PURE__ */ jsx13(CommandPalette, { filter: input, cursor: paletteCursor }),
|
|
4633
4725
|
contextWarning !== null && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: `\u26A0 context ${contextWarning}% full \u2014 run /clear and start fresh` }) }),
|
|
4634
4726
|
!input.startsWith("/") && (() => {
|
package/package.json
CHANGED