kimiflare 0.23.0 → 0.24.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
@@ -1528,7 +1528,7 @@ var init_mode = __esm({
1528
1528
  "use strict";
1529
1529
  MODES = ["edit", "plan", "auto"];
1530
1530
  MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "bash"]);
1531
- DANGEROUS_PATTERNS = /[<>;&`$]|\$\(|\$\{|\|\||\b&\s*$/;
1531
+ DANGEROUS_PATTERNS = /[<>;`$]|\$\(|\$\{|\|\||\b&\s*$/;
1532
1532
  GIT_READONLY_SUBCOMMANDS = {
1533
1533
  log: true,
1534
1534
  diff: true,
@@ -1888,8 +1888,10 @@ function injectCoauthor(command, coauthor) {
1888
1888
  if (command.includes(trailer)) return command;
1889
1889
  const createsCommit = /\bgit\s+(commit|merge|revert|cherry-pick)\b/.test(trimmed);
1890
1890
  const isRebaseContinue = /\bgit\s+rebase\b/.test(trimmed) && !/\b--abort\b|\b--skip\b/.test(trimmed);
1891
+ const movesHeadOnly = /\bgit\s+(reset|checkout|switch)\b/.test(trimmed);
1891
1892
  const mentionsGit = /\bgit\b/.test(trimmed);
1892
1893
  if (!createsCommit && !isRebaseContinue && !mentionsGit) return command;
1894
+ if (movesHeadOnly) return command;
1893
1895
  const tmpFile = join5(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
1894
1896
  const amendBlock = `
1895
1897
  if ! git log -1 --pretty=%B 2>/dev/null | grep -qF "${trailer}"; then
@@ -1902,7 +1904,7 @@ function injectCoauthor(command, coauthor) {
1902
1904
  const beforeHead = `git rev-parse HEAD 2>/dev/null || echo "NO_HEAD"`;
1903
1905
  const afterCheck = `
1904
1906
  _KF_AFTER_HEAD=$(git rev-parse HEAD 2>/dev/null || echo "NO_HEAD")
1905
- if [ "$_KF_BEFORE_HEAD" != "$_KF_AFTER_HEAD" ] && [ "$_KF_AFTER_HEAD" != "NO_HEAD" ]; then
1907
+ if [ "$_KF_BEFORE_HEAD" != "$_KF_AFTER_HEAD" ] && [ "$_KF_AFTER_HEAD" != "NO_HEAD" ] && git merge-base --is-ancestor "$_KF_BEFORE_HEAD" "$_KF_AFTER_HEAD" 2>/dev/null; then
1906
1908
  ${amendBlock}
1907
1909
  fi
1908
1910
  `.trim();
@@ -3079,6 +3081,29 @@ var init_update_check = __esm({
3079
3081
  }
3080
3082
  });
3081
3083
 
3084
+ // src/util/version.ts
3085
+ import { readFileSync as readFileSync2 } from "fs";
3086
+ import { fileURLToPath as fileURLToPath2 } from "url";
3087
+ import { dirname as dirname3, join as join7 } from "path";
3088
+ function getAppVersion() {
3089
+ if (cachedVersion !== null) return cachedVersion;
3090
+ try {
3091
+ const here = dirname3(fileURLToPath2(import.meta.url));
3092
+ const pkg = JSON.parse(readFileSync2(join7(here, "..", "..", "package.json"), "utf8"));
3093
+ cachedVersion = pkg.version ?? "0.0.0";
3094
+ } catch {
3095
+ cachedVersion = "0.0.0";
3096
+ }
3097
+ return cachedVersion;
3098
+ }
3099
+ var cachedVersion;
3100
+ var init_version = __esm({
3101
+ "src/util/version.ts"() {
3102
+ "use strict";
3103
+ cachedVersion = null;
3104
+ }
3105
+ });
3106
+
3082
3107
  // src/agent/compact.ts
3083
3108
  function indexOfNthUserFromEnd(messages, n) {
3084
3109
  let seen = 0;
@@ -3968,13 +3993,12 @@ import React3 from "react";
3968
3993
  import { Box as Box4, Text as Text4, Static } from "ink";
3969
3994
  import Spinner2 from "ink-spinner";
3970
3995
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3971
- var MAX_FINALIZED_EVENTS, ChatView, EventView;
3996
+ var ChatView, EventView;
3972
3997
  var init_chat = __esm({
3973
3998
  "src/ui/chat.tsx"() {
3974
3999
  "use strict";
3975
4000
  init_tool_view();
3976
4001
  init_markdown();
3977
- MAX_FINALIZED_EVENTS = 100;
3978
4002
  ChatView = React3.memo(function ChatView2({ events, showReasoning, theme, verbose }) {
3979
4003
  const finalized = [];
3980
4004
  const active = [];
@@ -3989,15 +4013,13 @@ var init_chat = __esm({
3989
4013
  finalized.push({ id: e.key, evt: e, showSeparator });
3990
4014
  }
3991
4015
  }
3992
- const droppedCount = Math.max(0, finalized.length - MAX_FINALIZED_EVENTS);
3993
- const visibleFinalized = droppedCount > 0 ? finalized.slice(droppedCount) : finalized;
3994
4016
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3995
- /* @__PURE__ */ jsx4(Static, { items: visibleFinalized, children: (item) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
4017
+ /* @__PURE__ */ jsx4(Static, { items: finalized, children: (item) => /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3996
4018
  item.showSeparator && /* @__PURE__ */ jsx4(Box4, { marginY: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: "\u2500".repeat(40) }) }),
3997
4019
  /* @__PURE__ */ jsx4(EventView, { evt: item.evt, showReasoning, theme, verbose })
3998
- ] }) }),
4020
+ ] }, item.id) }),
3999
4021
  active.map((e, i) => {
4000
- const prevEvt = i > 0 ? active[i - 1] : visibleFinalized[visibleFinalized.length - 1]?.evt;
4022
+ const prevEvt = i > 0 ? active[i - 1] : finalized[finalized.length - 1]?.evt;
4001
4023
  const showSeparator = e.kind === "user" && prevEvt && (prevEvt.kind === "assistant" || prevEvt.kind === "tool");
4002
4024
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
4003
4025
  showSeparator && /* @__PURE__ */ jsx4(Box4, { marginY: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: "\u2500".repeat(40) }) }),
@@ -5328,7 +5350,9 @@ function Welcome({ theme, accountId }) {
5328
5350
  ] }),
5329
5351
  /* @__PURE__ */ jsx12(Text12, { color: theme.user, children: s })
5330
5352
  ] }, i)) }),
5331
- /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: "Type a message or /help for commands \xB7 ctrl-c to exit \xB7 shift+tab to cycle modes" }) })
5353
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: "Type a message or /help for commands \xB7 ctrl-c to exit \xB7 shift+tab to cycle modes" }) }),
5354
+ /* @__PURE__ */ jsx12(Box11, { children: /* @__PURE__ */ jsx12(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: "Tip: type /hello to send feedback to the creator" }) }),
5355
+ /* @__PURE__ */ jsx12(Box11, { children: /* @__PURE__ */ jsx12(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: "Join our community: https://discord.gg/aEuAUHNTK5 (type /community to open)" }) })
5332
5356
  ] });
5333
5357
  }
5334
5358
  var SUGGESTIONS;
@@ -5579,10 +5603,10 @@ __export(sessions_exports, {
5579
5603
  });
5580
5604
  import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5, readdir as readdir2, stat as stat3 } from "fs/promises";
5581
5605
  import { homedir as homedir6 } from "os";
5582
- import { join as join7 } from "path";
5606
+ import { join as join8 } from "path";
5583
5607
  function sessionsDir() {
5584
- const xdg = process.env.XDG_DATA_HOME || join7(homedir6(), ".local", "share");
5585
- return join7(xdg, "kimiflare", "sessions");
5608
+ const xdg = process.env.XDG_DATA_HOME || join8(homedir6(), ".local", "share");
5609
+ return join8(xdg, "kimiflare", "sessions");
5586
5610
  }
5587
5611
  function sanitize(text) {
5588
5612
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -5595,7 +5619,7 @@ function makeSessionId(firstPrompt) {
5595
5619
  async function saveSession(file) {
5596
5620
  const dir = sessionsDir();
5597
5621
  await mkdir5(dir, { recursive: true });
5598
- const path = join7(dir, `${file.id}.json`);
5622
+ const path = join8(dir, `${file.id}.json`);
5599
5623
  await writeFile5(path, JSON.stringify(file, null, 2), "utf8");
5600
5624
  return path;
5601
5625
  }
@@ -5615,7 +5639,7 @@ async function listSessions(limit = 30) {
5615
5639
  const summaries = [];
5616
5640
  for (const name of entries) {
5617
5641
  if (!name.endsWith(".json")) continue;
5618
- const path = join7(dir, name);
5642
+ const path = join8(dir, name);
5619
5643
  try {
5620
5644
  const [s, raw] = await Promise.all([stat3(path), readFile7(path, "utf8")]);
5621
5645
  const parsed = JSON.parse(raw);
@@ -5688,13 +5712,13 @@ var init_image = __esm({
5688
5712
  // src/usage-tracker.ts
5689
5713
  import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
5690
5714
  import { homedir as homedir7 } from "os";
5691
- import { join as join8 } from "path";
5715
+ import { join as join9 } from "path";
5692
5716
  function usageDir() {
5693
- const xdg = process.env.XDG_DATA_HOME || join8(homedir7(), ".local", "share");
5694
- return join8(xdg, "kimiflare");
5717
+ const xdg = process.env.XDG_DATA_HOME || join9(homedir7(), ".local", "share");
5718
+ return join9(xdg, "kimiflare");
5695
5719
  }
5696
5720
  function usagePath() {
5697
- return join8(usageDir(), "usage.json");
5721
+ return join9(usageDir(), "usage.json");
5698
5722
  }
5699
5723
  function today() {
5700
5724
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -5820,7 +5844,7 @@ async function getCostReport(sessionId) {
5820
5844
  const log = pruneUsageLog(await loadLog());
5821
5845
  const date = today();
5822
5846
  const currentMonth = date.slice(0, 7);
5823
- const session = log.sessions.find((s) => s.id === sessionId) ?? { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 };
5847
+ const session = sessionId ? log.sessions.find((s) => s.id === sessionId) ?? { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 } : { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 };
5824
5848
  const todayUsage = log.days.find((d) => d.date === date) ?? { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 };
5825
5849
  const monthUsage = {
5826
5850
  date: currentMonth,
@@ -5919,7 +5943,7 @@ __export(db_exports, {
5919
5943
  updateMemoryEmbedding: () => updateMemoryEmbedding
5920
5944
  });
5921
5945
  import Database from "better-sqlite3";
5922
- import { dirname as dirname3 } from "path";
5946
+ import { dirname as dirname4 } from "path";
5923
5947
  import { mkdirSync, statSync as statSync2 } from "fs";
5924
5948
  function initSchema(db) {
5925
5949
  db.exec(`
@@ -6000,7 +6024,7 @@ function openMemoryDb(dbPath) {
6000
6024
  if (dbInstance) {
6001
6025
  dbInstance.close();
6002
6026
  }
6003
- mkdirSync(dirname3(dbPath), { recursive: true });
6027
+ mkdirSync(dirname4(dbPath), { recursive: true });
6004
6028
  dbInstance = new Database(dbPath);
6005
6029
  dbInstance.pragma("journal_mode = WAL");
6006
6030
  dbInstance.pragma("foreign_keys = ON");
@@ -6872,6 +6896,42 @@ Return only the topic key string.`
6872
6896
  }
6873
6897
  });
6874
6898
 
6899
+ // src/util/state.ts
6900
+ import { readFile as readFile10, writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
6901
+ import { homedir as homedir8 } from "os";
6902
+ import { join as join11 } from "path";
6903
+ function statePath() {
6904
+ const xdg = process.env.XDG_CONFIG_HOME || join11(homedir8(), ".config");
6905
+ return join11(xdg, "kimiflare", "state.json");
6906
+ }
6907
+ async function readState() {
6908
+ try {
6909
+ const raw = await readFile10(statePath(), "utf8");
6910
+ return JSON.parse(raw);
6911
+ } catch {
6912
+ return {};
6913
+ }
6914
+ }
6915
+ async function writeState(state) {
6916
+ const path = statePath();
6917
+ await mkdir7(join11(path, ".."), { recursive: true });
6918
+ await writeFile7(path, JSON.stringify(state, null, 2) + "\n", "utf8");
6919
+ }
6920
+ async function markCreatorMessageSeen(version) {
6921
+ const state = await readState();
6922
+ state.creatorMessageSeenVersion = version;
6923
+ await writeState(state);
6924
+ }
6925
+ async function shouldShowCreatorMessage(version) {
6926
+ const state = await readState();
6927
+ return state.creatorMessageSeenVersion !== version;
6928
+ }
6929
+ var init_state = __esm({
6930
+ "src/util/state.ts"() {
6931
+ "use strict";
6932
+ }
6933
+ });
6934
+
6875
6935
  // src/app.tsx
6876
6936
  var app_exports = {};
6877
6937
  __export(app_exports, {
@@ -6880,8 +6940,10 @@ __export(app_exports, {
6880
6940
  import { useState as useState6, useRef as useRef3, useEffect as useEffect4, useCallback } from "react";
6881
6941
  import { Box as Box12, Text as Text13, useApp, useInput as useInput2, render } from "ink";
6882
6942
  import { existsSync } from "fs";
6883
- import { join as join10 } from "path";
6943
+ import { join as join12 } from "path";
6884
6944
  import { unlink as unlink2 } from "fs/promises";
6945
+ import { spawn as spawn2 } from "child_process";
6946
+ import { platform as platform2 } from "os";
6885
6947
  import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
6886
6948
  function gatewayFromConfig(cfg) {
6887
6949
  if (!cfg.aiGatewayId) return void 0;
@@ -6902,10 +6964,34 @@ function gatewayUsageLookupFromConfig(cfg, meta) {
6902
6964
  meta
6903
6965
  };
6904
6966
  }
6967
+ function openBrowser(url) {
6968
+ const cmd = platform2() === "darwin" ? "open" : platform2() === "win32" ? "start" : "xdg-open";
6969
+ const child = spawn2(cmd, [url], { detached: true, stdio: "ignore" });
6970
+ child.unref();
6971
+ }
6905
6972
  function capEvents(prev) {
6906
6973
  if (prev.length <= MAX_EVENTS) return prev;
6907
6974
  return prev.slice(prev.length - MAX_EVENTS);
6908
6975
  }
6976
+ function compactEventsVisual(prev, keepLastTurns) {
6977
+ let seen = 0;
6978
+ let cutoff = -1;
6979
+ for (let i = prev.length - 1; i >= 0; i--) {
6980
+ if (prev[i].kind === "user") {
6981
+ seen++;
6982
+ if (seen === keepLastTurns + 1) {
6983
+ cutoff = i;
6984
+ break;
6985
+ }
6986
+ }
6987
+ }
6988
+ if (cutoff <= 0) return prev;
6989
+ const kept = prev.slice(cutoff);
6990
+ return [
6991
+ { kind: "info", key: mkKey(), text: `\xB7\xB7\xB7 ${cutoff} earlier messages compacted \xB7\xB7\xB7` },
6992
+ ...kept
6993
+ ];
6994
+ }
6909
6995
  function makePrefixMessages(cacheStable, model, mode, tools) {
6910
6996
  if (cacheStable) {
6911
6997
  return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
@@ -7004,8 +7090,21 @@ function App({ initialCfg, initialUpdateResult }) {
7004
7090
  }
7005
7091
  })
7006
7092
  );
7093
+ void shouldShowCreatorMessage(getAppVersion()).then((shouldShow) => {
7094
+ if (shouldShow) {
7095
+ setEvents((e) => [
7096
+ ...e,
7097
+ {
7098
+ kind: "info",
7099
+ key: mkKey(),
7100
+ text: "Hey, how do you like this version? I'd love to hear from you \u2014 type /hello to send me a voice note. Only I see it, and I may DM you back."
7101
+ }
7102
+ ]);
7103
+ void markCreatorMessageSeen(getAppVersion());
7104
+ }
7105
+ });
7007
7106
  if (cfg.memoryEnabled) {
7008
- const dbPath = cfg.memoryDbPath ?? join10(process.cwd(), ".kimiflare", "memory.db");
7107
+ const dbPath = cfg.memoryDbPath ?? join12(process.cwd(), ".kimiflare", "memory.db");
7009
7108
  const manager = new MemoryManager({
7010
7109
  dbPath,
7011
7110
  accountId: cfg.accountId,
@@ -7366,14 +7465,19 @@ function App({ initialCfg, initialUpdateResult }) {
7366
7465
  } else {
7367
7466
  messagesRef.current = result.newMessages;
7368
7467
  sessionStateRef.current = result.newState;
7369
- setEvents((e) => [
7370
- ...e,
7371
- {
7372
- kind: "info",
7373
- key: mkKey(),
7374
- text: `compacted ${result.metrics.rawTurnsRemoved} turns \u2192 ${result.metrics.estimatedTokensBefore} \u2192 ${result.metrics.estimatedTokensAfter} tokens, ${result.metrics.archivedArtifacts} artifacts`
7375
- }
7376
- ]);
7468
+ setEvents(
7469
+ (e) => compactEventsVisual(
7470
+ [
7471
+ ...e,
7472
+ {
7473
+ kind: "info",
7474
+ key: mkKey(),
7475
+ text: `compacted ${result.metrics.rawTurnsRemoved} turns \u2192 ${result.metrics.estimatedTokensBefore} \u2192 ${result.metrics.estimatedTokensAfter} tokens, ${result.metrics.archivedArtifacts} artifacts`
7476
+ }
7477
+ ],
7478
+ 4
7479
+ )
7480
+ );
7377
7481
  await saveSessionSafe();
7378
7482
  }
7379
7483
  } else {
@@ -7392,14 +7496,19 @@ function App({ initialCfg, initialUpdateResult }) {
7392
7496
  ]);
7393
7497
  } else {
7394
7498
  messagesRef.current = result.newMessages;
7395
- setEvents((e) => [
7396
- ...e,
7397
- {
7398
- kind: "info",
7399
- key: mkKey(),
7400
- text: `compacted ${result.replacedCount} messages into a summary`
7401
- }
7402
- ]);
7499
+ setEvents(
7500
+ (e) => compactEventsVisual(
7501
+ [
7502
+ ...e,
7503
+ {
7504
+ kind: "info",
7505
+ key: mkKey(),
7506
+ text: `compacted ${result.replacedCount} messages into a summary`
7507
+ }
7508
+ ],
7509
+ 4
7510
+ )
7511
+ );
7403
7512
  await saveSessionSafe();
7404
7513
  }
7405
7514
  }
@@ -7428,13 +7537,13 @@ function App({ initialCfg, initialUpdateResult }) {
7428
7537
  }
7429
7538
  const cwd = process.cwd();
7430
7539
  for (const name of ["KIMI.md", "KIMIFLARE.md", "AGENT.md"]) {
7431
- if (existsSync(join10(cwd, name))) {
7540
+ if (existsSync(join12(cwd, name))) {
7432
7541
  setEvents((e) => [
7433
7542
  ...e,
7434
7543
  {
7435
7544
  kind: "info",
7436
7545
  key: mkKey(),
7437
- text: `${name} already exists at ${join10(cwd, name)} \u2014 delete it first if you want to regenerate`
7546
+ text: `${name} already exists at ${join12(cwd, name)} \u2014 delete it first if you want to regenerate`
7438
7547
  }
7439
7548
  ]);
7440
7549
  return;
@@ -7543,6 +7652,10 @@ function App({ initialCfg, initialUpdateResult }) {
7543
7652
  resolve2("allow");
7544
7653
  return;
7545
7654
  }
7655
+ if (req.tool.name === "bash") {
7656
+ setPerm({ tool: req.tool, args: req.args, resolve: resolve2 });
7657
+ return;
7658
+ }
7546
7659
  setEvents((e) => [
7547
7660
  ...e,
7548
7661
  {
@@ -7558,7 +7671,7 @@ function App({ initialCfg, initialUpdateResult }) {
7558
7671
  })
7559
7672
  }
7560
7673
  });
7561
- if (existsSync(join10(cwd, "KIMI.md"))) {
7674
+ if (existsSync(join12(cwd, "KIMI.md"))) {
7562
7675
  if (cacheStableRef.current) {
7563
7676
  messagesRef.current[1] = {
7564
7677
  role: "system",
@@ -7704,15 +7817,16 @@ function App({ initialCfg, initialUpdateResult }) {
7704
7817
  return true;
7705
7818
  }
7706
7819
  if (c === "/cost") {
7707
- if (!sessionIdRef.current) {
7708
- setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "no usage recorded yet" }]);
7709
- return true;
7710
- }
7711
- void getCostReport(sessionIdRef.current).then((report) => {
7820
+ void getCostReport(sessionIdRef.current ?? void 0).then((report) => {
7712
7821
  setEvents((e) => [
7713
7822
  ...e,
7714
7823
  { kind: "info", key: mkKey(), text: formatCostReport(report) }
7715
7824
  ]);
7825
+ }).catch((err) => {
7826
+ setEvents((e) => [
7827
+ ...e,
7828
+ { kind: "error", key: mkKey(), text: `cost report failed: ${err.message}` }
7829
+ ]);
7716
7830
  });
7717
7831
  return true;
7718
7832
  }
@@ -7962,19 +8076,6 @@ ${lines.join("\n")}` }]);
7962
8076
  }
7963
8077
  return true;
7964
8078
  }
7965
- if (c === "/cost") {
7966
- if (!sessionIdRef.current) {
7967
- setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "no usage recorded yet" }]);
7968
- return true;
7969
- }
7970
- void getCostReport(sessionIdRef.current).then((report) => {
7971
- setEvents((e) => [
7972
- ...e,
7973
- { kind: "info", key: mkKey(), text: formatCostReport(report) }
7974
- ]);
7975
- });
7976
- return true;
7977
- }
7978
8079
  if (c === "/resume") {
7979
8080
  void openResumePicker();
7980
8081
  return true;
@@ -8056,6 +8157,24 @@ ${lines.join("\n")}` }]);
8056
8157
  ]);
8057
8158
  return true;
8058
8159
  }
8160
+ if (c === "/hello") {
8161
+ const session = crypto.randomUUID();
8162
+ const url = `${FEEDBACK_WORKER_URL}/?s=${session}&v=${getAppVersion()}`;
8163
+ openBrowser(url);
8164
+ setEvents((e) => [
8165
+ ...e,
8166
+ { kind: "info", key: mkKey(), text: "Opened voice note page in your browser. Record your message there and hit Send when you're done." }
8167
+ ]);
8168
+ return true;
8169
+ }
8170
+ if (c === "/community") {
8171
+ openBrowser("https://discord.gg/aEuAUHNTK5");
8172
+ setEvents((e) => [
8173
+ ...e,
8174
+ { kind: "info", key: mkKey(), text: "Opened Discord invite in your browser." }
8175
+ ]);
8176
+ return true;
8177
+ }
8059
8178
  if (c === "/logout") {
8060
8179
  unlink2(configPath()).catch(() => {
8061
8180
  });
@@ -8072,7 +8191,7 @@ ${lines.join("\n")}` }]);
8072
8191
  {
8073
8192
  kind: "info",
8074
8193
  key: mkKey(),
8075
- text: "commands:\n /mode edit|plan|auto switch mode (or shift+tab to cycle)\n /plan /auto /edit shortcuts for /mode\n /thinking low|med|high set reasoning effort (quality vs speed)\n /theme interactive theme picker (or ctrl+t)\n /theme NAME set theme by name\n /resume pick a past conversation\n /compact summarize old turns to free context\n /init scan this repo and write a KIMI.md for future agents\n /memory show memory stats\n /memory search <query> search stored memories\n /memory clear wipe memories for this repo\n /mcp list list connected MCP servers and tools\n /mcp reload reconnect all configured MCP servers\n /reasoning toggle show/hide model reasoning\n /clear clear current conversation\n /gateway show gateway status\n /gateway ID enable AI Gateway\n /gateway off disable AI Gateway (direct Workers AI)\n /gateway cache-ttl N set gateway cache TTL in seconds\n /gateway skip-cache T|F set gateway skip-cache flag\n /gateway collect-logs T|F include payload in gateway logs\n /gateway metadata K=V add metadata key-value pair\n /gateway metadata clear remove all metadata\n /cost /model /update /logout /help /exit\nkeys: ctrl-c interrupt/exit \xB7 ctrl-r toggle reasoning \xB7 ctrl-o verbose \xB7 ctrl+t theme \xB7 shift+tab cycle mode \xB7 \u2191/\u2193 history"
8194
+ text: "commands:\n /mode edit|plan|auto switch mode (or shift+tab to cycle)\n /plan /auto /edit shortcuts for /mode\n /thinking low|med|high set reasoning effort (quality vs speed)\n /theme interactive theme picker (or ctrl+t)\n /theme NAME set theme by name\n /resume pick a past conversation\n /compact summarize old turns to free context\n /init scan this repo and write a KIMI.md for future agents\n /memory show memory stats\n /memory search <query> search stored memories\n /memory clear wipe memories for this repo\n /mcp list list connected MCP servers and tools\n /mcp reload reconnect all configured MCP servers\n /reasoning toggle show/hide model reasoning\n /clear clear current conversation\n /hello send a voice note to the creator\n /community join our Discord server\n /gateway show gateway status\n /gateway ID enable AI Gateway\n /gateway off disable AI Gateway (direct Workers AI)\n /gateway cache-ttl N set gateway cache TTL in seconds\n /gateway skip-cache T|F set gateway skip-cache flag\n /gateway collect-logs T|F include payload in gateway logs\n /gateway metadata K=V add metadata key-value pair\n /gateway metadata clear remove all metadata\n /cost /model /update /logout /help /exit\nkeys: ctrl-c interrupt/exit \xB7 ctrl-r toggle reasoning \xB7 ctrl-o verbose \xB7 ctrl+t theme \xB7 shift+tab cycle mode \xB7 \u2191/\u2193 history"
8076
8195
  }
8077
8196
  ]);
8078
8197
  return true;
@@ -8208,6 +8327,7 @@ ${lines.join("\n")}` }]);
8208
8327
  onUsageFinal: (u, meta) => {
8209
8328
  const sid = ensureSessionId();
8210
8329
  void recordUsage(sid, u, gatewayUsageLookupFromConfig(cfg, meta ?? gatewayMetaRef.current));
8330
+ void getCostReport(sid).then((report) => setSessionUsage(report.session));
8211
8331
  },
8212
8332
  onGatewayMeta: updateGatewayMeta,
8213
8333
  onTasks: (nextTasks) => {
@@ -8464,7 +8584,7 @@ async function renderApp(cfg, updateResult) {
8464
8584
  });
8465
8585
  await instance.waitUntilExit();
8466
8586
  }
8467
- var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS;
8587
+ var FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS;
8468
8588
  var init_app = __esm({
8469
8589
  "src/app.tsx"() {
8470
8590
  "use strict";
@@ -8495,9 +8615,12 @@ var init_app = __esm({
8495
8615
  init_usage_tracker();
8496
8616
  init_manager2();
8497
8617
  init_storage_limits();
8618
+ init_state();
8619
+ init_version();
8620
+ FEEDBACK_WORKER_URL = "https://kimiflare-feedback.sina-b35.workers.dev";
8498
8621
  CONTEXT_LIMIT = 262e3;
8499
8622
  AUTO_COMPACT_SUGGEST_PCT = 0.8;
8500
- MAX_EVENTS = 80;
8623
+ MAX_EVENTS = 500;
8501
8624
  nextAssistantId = 1;
8502
8625
  nextKey = 1;
8503
8626
  mkKey = () => `evt_${nextKey++}`;
@@ -8516,21 +8639,10 @@ init_loop();
8516
8639
  init_system_prompt();
8517
8640
  init_executor();
8518
8641
  init_update_check();
8642
+ init_version();
8519
8643
  import { Command } from "commander";
8520
- import { readFileSync as readFileSync2 } from "fs";
8521
- import { fileURLToPath as fileURLToPath2 } from "url";
8522
- import { dirname as dirname4, join as join11 } from "path";
8523
- function readPackageVersion() {
8524
- try {
8525
- const here = dirname4(fileURLToPath2(import.meta.url));
8526
- const pkg = JSON.parse(readFileSync2(join11(here, "..", "package.json"), "utf8"));
8527
- return pkg.version ?? "0.0.0";
8528
- } catch {
8529
- return "0.0.0";
8530
- }
8531
- }
8532
8644
  var program = new Command();
8533
- program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(readPackageVersion()).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)").parse();
8645
+ 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)").parse();
8534
8646
  var opts = program.opts();
8535
8647
  async function main() {
8536
8648
  const cfg = await loadConfig();