kimiflare 0.32.0 → 0.33.0

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/index.js CHANGED
@@ -945,6 +945,87 @@ var init_cost_debug = __esm({
945
945
  }
946
946
  });
947
947
 
948
+ // src/memory/extractors.ts
949
+ function safeJsonParse(text) {
950
+ try {
951
+ return JSON.parse(text);
952
+ } catch {
953
+ return null;
954
+ }
955
+ }
956
+ var EXTRACTORS;
957
+ var init_extractors = __esm({
958
+ "src/memory/extractors.ts"() {
959
+ "use strict";
960
+ EXTRACTORS = [
961
+ {
962
+ id: "package_json",
963
+ match: (tool, file) => tool === "read" && /package\.json$/.test(file || ""),
964
+ extract: (content, file) => {
965
+ const pkg = safeJsonParse(content);
966
+ if (!pkg) return null;
967
+ const deps = Object.keys(pkg.dependencies || {}).slice(0, 10);
968
+ const devDeps = Object.keys(pkg.devDependencies || {}).slice(0, 5);
969
+ const scripts = Object.keys(pkg.scripts || {}).slice(0, 5);
970
+ return {
971
+ content: `Project dependencies: ${deps.join(", ") || "none"}. Dev dependencies: ${devDeps.join(", ") || "none"}. Scripts: ${scripts.join(", ") || "none"}. Type: ${pkg.type || "commonjs"}.`,
972
+ category: "fact",
973
+ importance: 4,
974
+ topicKey: "project_dependencies",
975
+ relatedFiles: file ? [file] : void 0
976
+ };
977
+ }
978
+ },
979
+ {
980
+ id: "tsconfig",
981
+ match: (tool, file) => tool === "read" && /tsconfig.*\.json$/.test(file || ""),
982
+ extract: (content, file) => {
983
+ const ts = safeJsonParse(content);
984
+ if (!ts) return null;
985
+ const opts2 = ts.compilerOptions || {};
986
+ return {
987
+ content: `TypeScript config: target=${opts2.target || "default"}, module=${opts2.module || "default"}, strict=${opts2.strict || false}, jsx=${opts2.jsx || "none"}.`,
988
+ category: "fact",
989
+ importance: 4,
990
+ topicKey: "project_tsconfig",
991
+ relatedFiles: file ? [file] : void 0
992
+ };
993
+ }
994
+ },
995
+ {
996
+ id: "entry_point",
997
+ match: (tool, file) => tool === "read" && /src\/(index|main)\.(ts|tsx|js|jsx)$/.test(file || ""),
998
+ extract: (content, file) => {
999
+ const exports = content.match(/export\s+(?:default\s+)?(?:function|class|const|interface|type)\s+(\w+)/g);
1000
+ const exportNames = exports ? exports.map((e) => e.split(/\s+/).pop()).filter((n) => !!n).slice(0, 5) : [];
1001
+ return {
1002
+ content: `Entry point ${file} exports: ${exportNames.join(", ") || "default export or side effects"}.`,
1003
+ category: "fact",
1004
+ importance: 3,
1005
+ topicKey: "project_entry_point",
1006
+ relatedFiles: file ? [file] : void 0
1007
+ };
1008
+ }
1009
+ },
1010
+ {
1011
+ id: "edit_event",
1012
+ match: (tool, file) => (tool === "edit" || tool === "write") && !!file,
1013
+ extract: (_content, file) => {
1014
+ if (!file) return null;
1015
+ const safeKey = file.replace(/[^a-zA-Z0-9]/g, "_");
1016
+ return {
1017
+ content: `File modified: ${file}.`,
1018
+ category: "event",
1019
+ importance: 2,
1020
+ topicKey: `event_edit_${safeKey}`,
1021
+ relatedFiles: [file]
1022
+ };
1023
+ }
1024
+ }
1025
+ ];
1026
+ }
1027
+ });
1028
+
948
1029
  // src/agent/strip-reasoning.ts
949
1030
  function stripHistoricalReasoning(messages, opts2 = {}) {
950
1031
  const keepLast = opts2.keepLast ?? DEFAULT_KEEP_LAST;
@@ -1328,7 +1409,28 @@ Use console.log() to return results. Only console.log output will be sent back t
1328
1409
  const webFetchHistory = [];
1329
1410
  const MAX_WEB_FETCH_PER_TURN = 5;
1330
1411
  const WEB_FETCH_DOMAIN_THRESHOLD = 2;
1331
- for (let iter = 0; iter < max; iter++) {
1412
+ let cumulativePromptTokens = 0;
1413
+ let iter = 0;
1414
+ let budgetExhausted = false;
1415
+ while (true) {
1416
+ if (budgetExhausted) {
1417
+ opts2.messages.push({
1418
+ role: "system",
1419
+ content: "You have reached the cumulative input token budget for this session. Please synthesize your findings and provide a final summary of what was accomplished."
1420
+ });
1421
+ }
1422
+ if (iter >= max) {
1423
+ if (opts2.continueOnLimit) {
1424
+ opts2.messages.push({
1425
+ role: "system",
1426
+ content: "You have reached the tool-call limit for this session. The counter has been reset so you can continue working. Please proceed with your task."
1427
+ });
1428
+ iter = 0;
1429
+ } else {
1430
+ throw new Error(`kimiflare: tool iteration limit reached (${max})`);
1431
+ }
1432
+ }
1433
+ iter++;
1332
1434
  turn++;
1333
1435
  const previousMessages = opts2.messages.slice();
1334
1436
  const toolCalls = [];
@@ -1426,7 +1528,13 @@ Use console.log() to return results. Only console.log output will be sent back t
1426
1528
  }
1427
1529
  }
1428
1530
  if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
1429
- if (lastUsage) opts2.callbacks.onUsageFinal?.(lastUsage, gatewayMeta);
1531
+ if (lastUsage) {
1532
+ opts2.callbacks.onUsageFinal?.(lastUsage, gatewayMeta);
1533
+ cumulativePromptTokens += lastUsage.prompt_tokens;
1534
+ if (!budgetExhausted && opts2.maxInputTokens !== void 0 && opts2.maxInputTokens > 0 && cumulativePromptTokens >= opts2.maxInputTokens && toolCalls.length > 0) {
1535
+ budgetExhausted = true;
1536
+ }
1537
+ }
1430
1538
  const assistantMsg = {
1431
1539
  role: "assistant",
1432
1540
  content: content ? sanitizeString(content) : null,
@@ -1455,6 +1563,9 @@ Use console.log() to return results. Only console.log output will be sent back t
1455
1563
  shadowStrip: shadowStripMetrics
1456
1564
  });
1457
1565
  }
1566
+ if (budgetExhausted) {
1567
+ throw new BudgetExhaustedError();
1568
+ }
1458
1569
  return;
1459
1570
  }
1460
1571
  for (const tc of toolCalls) {
@@ -1589,6 +1700,30 @@ ${sandboxResult.output}` : sandboxResult.output;
1589
1700
  name: result.name
1590
1701
  });
1591
1702
  opts2.callbacks.onToolResult?.(result);
1703
+ if (opts2.memoryManager) {
1704
+ let filePath;
1705
+ try {
1706
+ const args = JSON.parse(tc.function.arguments || "{}");
1707
+ filePath = args.path;
1708
+ } catch {
1709
+ }
1710
+ for (const extractor of EXTRACTORS) {
1711
+ if (extractor.match(tc.function.name, filePath)) {
1712
+ const memory = extractor.extract(result.content, filePath);
1713
+ if (memory) {
1714
+ void opts2.memoryManager.remember(
1715
+ memory.content,
1716
+ memory.category,
1717
+ memory.importance,
1718
+ opts2.cwd,
1719
+ opts2.sessionId ?? "unknown",
1720
+ opts2.signal
1721
+ ).catch(() => {
1722
+ });
1723
+ }
1724
+ }
1725
+ }
1726
+ }
1592
1727
  recentToolCalls.push(loopSignature);
1593
1728
  if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1594
1729
  }
@@ -1604,8 +1739,10 @@ ${sandboxResult.output}` : sandboxResult.output;
1604
1739
  shadowStrip: shadowStripMetrics
1605
1740
  });
1606
1741
  }
1742
+ if (budgetExhausted) {
1743
+ throw new BudgetExhaustedError();
1744
+ }
1607
1745
  }
1608
- throw new Error(`kimiflare: tool iteration limit reached (${opts2.maxToolIterations ?? 50})`);
1609
1746
  }
1610
1747
  function validateToolArguments(raw) {
1611
1748
  if (!raw || !raw.trim()) return "{}";
@@ -1616,7 +1753,7 @@ function validateToolArguments(raw) {
1616
1753
  return "{}";
1617
1754
  }
1618
1755
  }
1619
- var codeModeApiCache;
1756
+ var BudgetExhaustedError, codeModeApiCache;
1620
1757
  var init_loop = __esm({
1621
1758
  "src/agent/loop.ts"() {
1622
1759
  "use strict";
@@ -1624,8 +1761,15 @@ var init_loop = __esm({
1624
1761
  init_registry();
1625
1762
  init_messages();
1626
1763
  init_cost_debug();
1764
+ init_extractors();
1627
1765
  init_strip_reasoning();
1628
1766
  init_code_mode();
1767
+ BudgetExhaustedError = class extends Error {
1768
+ constructor(message2 = "Cumulative input token budget exhausted") {
1769
+ super(message2);
1770
+ this.name = "BudgetExhaustedError";
1771
+ }
1772
+ };
1629
1773
  codeModeApiCache = /* @__PURE__ */ new Map();
1630
1774
  }
1631
1775
  });
@@ -7413,7 +7557,7 @@ var init_help_menu = __esm({
7413
7557
  commands: [
7414
7558
  { command: "/init", description: "scan this repo and write a KIMI.md" },
7415
7559
  { command: "/logout", description: "clear credentials" },
7416
- { command: "filePicker", description: "enable with KIMIFLARE_FILE_PICKER=1 or filePicker: true in config", selectable: false }
7560
+ { command: "filePicker", description: "enabled by default; disable with KIMIFLARE_FILE_PICKER=0 or filePicker: false in config", selectable: false }
7417
7561
  ]
7418
7562
  }
7419
7563
  ];
@@ -11026,7 +11170,7 @@ function App({
11026
11170
  const [draftInput, setDraftInput] = useState9("");
11027
11171
  const [mode, setMode] = useState9("edit");
11028
11172
  const [codeMode, setCodeMode] = useState9(initialCfg?.codeMode ?? false);
11029
- const filePickerEnabled = initialCfg?.filePicker ?? false;
11173
+ const filePickerEnabled = initialCfg?.filePicker ?? true;
11030
11174
  const [effort, setEffort] = useState9(
11031
11175
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
11032
11176
  );
@@ -11833,23 +11977,13 @@ function App({
11833
11977
  return;
11834
11978
  }
11835
11979
  const cwd = process.cwd();
11836
- for (const name of ["KIMI.md", "KIMIFLARE.md", "AGENT.md"]) {
11837
- if (existsSync2(join17(cwd, name))) {
11838
- setEvents((e) => [
11839
- ...e,
11840
- {
11841
- kind: "info",
11842
- key: mkKey(),
11843
- text: `${name} already exists at ${join17(cwd, name)} \u2014 delete it first if you want to regenerate`
11844
- }
11845
- ]);
11846
- return;
11847
- }
11848
- }
11849
- const prompt = [
11850
- "Generate a KIMI.md at the repository root so future agents have project context.",
11980
+ const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find((n) => existsSync2(join17(cwd, n)));
11981
+ const isRefresh = existingName !== void 0;
11982
+ const promptParts = [
11983
+ isRefresh ? `Regenerate ${existingName} at the repository root to refresh project context. If the file already exists, read it first and preserve anything still accurate, updating only what has changed.` : "Generate a KIMI.md at the repository root so future agents have project context.",
11851
11984
  "",
11852
11985
  "First, use the `glob`, `read`, and `grep` tools to understand the project: read `package.json`, the top-level `README.md` if present, the tsconfig / build config, and skim the top-level source directory structure.",
11986
+ isRefresh ? `Also read the existing ${existingName} so you know what to keep vs. update.` : null,
11853
11987
  "",
11854
11988
  "Then call the `write` tool to create `KIMI.md` at the repo root with these sections, terse (aim \u2264 100 lines total):",
11855
11989
  "",
@@ -11860,8 +11994,9 @@ function App({
11860
11994
  "- **Do / Don't** \u2014 quirks or rules future agents should know.",
11861
11995
  "",
11862
11996
  "Do not call `tasks_set` for this. Just read what you need, then write the file."
11863
- ].join("\n");
11864
- setEvents((e) => [...e, { kind: "user", key: mkKey(), text: "/init" }]);
11997
+ ];
11998
+ const prompt = promptParts.filter((p) => p !== null).join("\n");
11999
+ setEvents((e) => [...e, { kind: "user", key: mkKey(), text: isRefresh ? `/init (refreshing ${existingName})` : "/init" }]);
11865
12000
  messagesRef.current.push({ role: "user", content: sanitizeString(prompt) });
11866
12001
  setBusy(true);
11867
12002
  setTurnStartedAt(Date.now());
@@ -13433,7 +13568,7 @@ init_update_check();
13433
13568
  init_version();
13434
13569
  import { Command } from "commander";
13435
13570
  var program = new Command();
13436
- program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)");
13571
+ program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--continue-on-limit", "reset tool-call counter and continue when the 50-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10));
13437
13572
  program.command("cost").description("Show cost attribution by task type (requires costAttribution enabled)").option("-w, --week", "last 7 days (default)").option("-m, --month", "last 30 days").option("-d, --day", "today only").option("-s, --session <id>", "single session detail").option("-c, --category <name>", "filter by category").option("--json", "machine-readable output").option("--reclassify", "re-run classification on all sessions").option("--local-only", "skip Cloudflare reconciliation").action(async (cmdOpts) => {
13438
13573
  const cfg = await loadConfig();
13439
13574
  const enabled = cfg?.costAttribution ?? false;
@@ -13482,6 +13617,8 @@ async function main() {
13482
13617
  allowAll: !!opts.dangerouslyAllowAll,
13483
13618
  showReasoning: !!opts.reasoning,
13484
13619
  codeMode: cfg.codeMode,
13620
+ continueOnLimit: !!opts.continueOnLimit,
13621
+ maxInputTokens: opts.maxInputTokens,
13485
13622
  updateResult
13486
13623
  });
13487
13624
  return;
@@ -13529,52 +13666,63 @@ async function runPrintMode(opts2) {
13529
13666
  process.on("SIGINT", () => controller.abort());
13530
13667
  let printedReasoningHeader = false;
13531
13668
  let printedAnswerHeader = false;
13532
- await runAgentTurn({
13533
- accountId: opts2.accountId,
13534
- apiToken: opts2.apiToken,
13535
- model: opts2.model,
13536
- gateway: gatewayFromPrintOpts(opts2),
13537
- messages,
13538
- tools: ALL_TOOLS,
13539
- executor,
13540
- cwd,
13541
- signal: controller.signal,
13542
- codeMode: opts2.codeMode,
13543
- coauthor: opts2.coauthor !== false ? { name: opts2.coauthorName || "kimiflare", email: opts2.coauthorEmail || "kimiflare@proton.me" } : void 0,
13544
- callbacks: {
13545
- onReasoningDelta: opts2.showReasoning ? (delta) => {
13546
- if (!printedReasoningHeader) {
13547
- process.stderr.write("\x1B[2m--- reasoning ---\n");
13548
- printedReasoningHeader = true;
13549
- }
13550
- process.stderr.write(delta);
13551
- } : void 0,
13552
- onTextDelta: (delta) => {
13553
- if (opts2.showReasoning && printedReasoningHeader && !printedAnswerHeader) {
13554
- process.stderr.write("\n--- answer ---\x1B[0m\n");
13555
- printedAnswerHeader = true;
13556
- }
13557
- process.stdout.write(delta);
13558
- },
13559
- onToolCallFinalized: (call) => {
13560
- process.stderr.write(`\x1B[2m[tool ${call.function.name}(${call.function.arguments})]\x1B[0m
13669
+ try {
13670
+ await runAgentTurn({
13671
+ accountId: opts2.accountId,
13672
+ apiToken: opts2.apiToken,
13673
+ model: opts2.model,
13674
+ gateway: gatewayFromPrintOpts(opts2),
13675
+ messages,
13676
+ tools: ALL_TOOLS,
13677
+ executor,
13678
+ cwd,
13679
+ signal: controller.signal,
13680
+ codeMode: opts2.codeMode,
13681
+ continueOnLimit: opts2.continueOnLimit,
13682
+ maxInputTokens: opts2.maxInputTokens,
13683
+ coauthor: opts2.coauthor !== false ? { name: opts2.coauthorName || "kimiflare", email: opts2.coauthorEmail || "kimiflare@proton.me" } : void 0,
13684
+ callbacks: {
13685
+ onReasoningDelta: opts2.showReasoning ? (delta) => {
13686
+ if (!printedReasoningHeader) {
13687
+ process.stderr.write("\x1B[2m--- reasoning ---\n");
13688
+ printedReasoningHeader = true;
13689
+ }
13690
+ process.stderr.write(delta);
13691
+ } : void 0,
13692
+ onTextDelta: (delta) => {
13693
+ if (opts2.showReasoning && printedReasoningHeader && !printedAnswerHeader) {
13694
+ process.stderr.write("\n--- answer ---\x1B[0m\n");
13695
+ printedAnswerHeader = true;
13696
+ }
13697
+ process.stdout.write(delta);
13698
+ },
13699
+ onToolCallFinalized: (call) => {
13700
+ process.stderr.write(`\x1B[2m[tool ${call.function.name}(${call.function.arguments})]\x1B[0m
13561
13701
  `);
13562
- },
13563
- onToolResult: (result) => {
13564
- const snippet = result.content.length > 400 ? result.content.slice(0, 400) + "..." : result.content;
13565
- process.stderr.write(`\x1B[2m[result: ${snippet.replace(/\n/g, " \u23CE ")}]\x1B[0m
13702
+ },
13703
+ onToolResult: (result) => {
13704
+ const snippet = result.content.length > 400 ? result.content.slice(0, 400) + "..." : result.content;
13705
+ process.stderr.write(`\x1B[2m[result: ${snippet.replace(/\n/g, " \u23CE ")}]\x1B[0m
13566
13706
  `);
13567
- },
13568
- askPermission: async ({ tool, args }) => {
13569
- if (opts2.allowAll) return "allow";
13570
- process.stderr.write(
13571
- `\x1B[31m[permission denied: ${tool.name}(${JSON.stringify(args)}) \u2014 pass --dangerously-allow-all to approve in print mode]\x1B[0m
13707
+ },
13708
+ askPermission: async ({ tool, args }) => {
13709
+ if (opts2.allowAll) return "allow";
13710
+ process.stderr.write(
13711
+ `\x1B[31m[permission denied: ${tool.name}(${JSON.stringify(args)}) \u2014 pass --dangerously-allow-all to approve in print mode]\x1B[0m
13572
13712
  `
13573
- );
13574
- return "deny";
13713
+ );
13714
+ return "deny";
13715
+ }
13575
13716
  }
13717
+ });
13718
+ } catch (err) {
13719
+ if (err instanceof BudgetExhaustedError) {
13720
+ process.stderr.write("\n\x1B[33m[Budget exhausted \u2014 exiting with code 42]\x1B[0m\n");
13721
+ process.exitCode = 42;
13722
+ return;
13576
13723
  }
13577
- });
13724
+ throw err;
13725
+ }
13578
13726
  process.stdout.write("\n");
13579
13727
  }
13580
13728
  //# sourceMappingURL=index.js.map