kimiflare 0.55.1 → 0.56.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/README.md CHANGED
@@ -33,7 +33,12 @@
33
33
 
34
34
  - **262k context window** — Read entire modules, large configs, and full stack traces without the model losing track.
35
35
  - **Image understanding** — Drop image paths (PNG, JPG, WebP, GIF, BMP up to 5 MB) into any prompt. Great for UI reviews, diagrams, and screenshots.
36
- - **Plan / Edit / Auto modes** — `plan` blocks mutating tools for safe research. `edit` (default) prompts per mutating call. `auto` approves everything for trusted tasks.
36
+ - **Plan / Edit / Auto modes** — `plan` is a whitelist-only research mode: only read-only tools (read, glob, grep, web search, GitHub read-only, browser fetch) are allowed. Writes, edits, mutating bash, MCP tools, and LSP renames are all blocked. `edit` (default) prompts per mutating call. `auto` approves everything for trusted tasks.
37
+ - **Windows support** — OS-aware shell auto-detects `cmd.exe` / PowerShell on Windows, `bash` on Unix. The `bash` tool works out of the box on all platforms.
38
+ - **Message queuing** — Submit multiple messages while the agent is busy; they queue and auto-drain. Escape interrupts the current turn but preserves the queue.
39
+ - **Smart permission modal** — Denying a tool opens inline feedback so you can tell the agent what to do instead. Keyboard-native navigation (`↑/↓`, `j/k`, `Alt+1/2/3`).
40
+ - **Loop guardrails** — Agent hard-stops when all tools in a turn are blocked, preventing infinite token-burning cycles.
41
+ - **Persistent all-time cost history** — Append-only `history.jsonl` tracks daily usage forever, so `/cost` shows true all-time and monthly totals that survive across sessions and version updates.
37
42
  - **Live cost tracking** — Status bar shows real-time spend based on Cloudflare pricing. Know exactly what each turn costs.
38
43
  - **LSP + MCP** — Semantic code intelligence (hover, go-to-definition, references, diagnostics) via Language Server Protocol. Extend with external tools via Model Context Protocol.
39
44
  - **Local structured memory** — SQLite + embeddings cross-session memory. The agent recalls facts, instructions, and preferences across sessions via `remember`, `recall`, and `forget` tools.
@@ -41,14 +46,16 @@
41
46
 
42
47
  ## Recently shipped
43
48
 
44
- - **Turn supervisor architecture** — graceful preemption, visual cleanup, and better multi-step task management.
45
- - **Web search, GitHub read-only, and headless browser tools** — research without leaving the terminal.
46
- - **Tiered skill routing** — the agent picks the right skill depth for the task, with visible TUI indicators.
47
- - **Extensible JSON themes** — WCAG contrast-validated, fully customizable color palettes.
48
- - **KIMI.md drift detection** — memory-based staleness indicators warn when your project context file is out of date.
49
- - **Fuzzy @ file picker** — type `@` to mention files with fuzzy matching and inline filtering.
50
- - **Kimiflare Cloud mode** — device auth, no API key needed, with real-time token budget tracking.
51
- - **Context-window guardrails** — prevents runs that would exceed the model's limit before they start.
49
+ - **OS-aware shell with Windows support** — Auto-detects `cmd.exe`, PowerShell, or bash based on platform. Override with `KIMIFLARE_SHELL` or `/shell`.
50
+ - **Smart permission modal with inline feedback** — Deny a tool and immediately tell the agent what to do instead. Keyboard-native navigation with `↑/↓`, `j/k`, `Alt+1/2/3`.
51
+ - **True message queuing** — Enter queues messages while the agent is busy; Escape interrupts and auto-drains the queue.
52
+ - **Hard-stop loop guardrail** — Stops token-burning cycles when all tools in a turn are blocked.
53
+ - **Persistent all-time usage history** — `history.jsonl` tracks daily usage forever; `/cost` shows true all-time and monthly totals.
54
+ - **Humanized Cloudflare API errors** — Actionable error codes and structured error display instead of raw JSON dumps.
55
+ - **429 rate limit retry** — Automatic backoff and retry when Cloudflare rate-limits requests.
56
+ - **Tool state visualization** — Queued, rejected, and cancelled tools are clearly labeled in the TUI.
57
+ - **Paste preview placeholders** — Pasted content shows a snippet preview with sequential IDs instead of random hashes.
58
+ - **Headless SDK** — Programmatic `createAgentSession` API and JSONL-over-stdio RPC mode for building on top of KimiFlare.
52
59
 
53
60
  See the full changelog at [github.com/sinameraji/kimiflare/releases](https://github.com/sinameraji/kimiflare/releases).
54
61
 
@@ -112,7 +119,7 @@ session.dispose();
112
119
  ```
113
120
 
114
121
  **Key features:**
115
- - `subscribe()` — receive typed events (`text_delta`, `tool_call`, `tool_result`, `task_update`, `usage`, `error`, `done`, etc.)
122
+ - `subscribe()` — receive typed events (`text_delta`, `tool_call`, `tool_result`, `task_update`, `usage`, `warning`, `error`, `done`, etc.)
116
123
  - `prompt()` / `steer()` / `followUp()` — full conversation lifecycle
117
124
  - `pause()` / `resume()` — graceful preemption
118
125
  - `getStatus()` / `getUsage()` — inspect session state
@@ -191,6 +198,7 @@ kimiflare
191
198
  | Command | Effect |
192
199
  |---------|--------|
193
200
  | `/mode edit\|plan\|auto` | Switch permission mode |
201
+ | `/shell auto\|bash\|cmd\|powershell` | Show or set the shell for the bash tool |
194
202
  | `/thinking low\|medium\|high` | Reasoning effort (persists) |
195
203
  | `/theme` | Interactive theme picker (`Ctrl+T`) |
196
204
  | `/resume` | Pick a past conversation to restore |
package/dist/index.js CHANGED
@@ -353,16 +353,21 @@ var init_lsp_config = __esm({
353
353
 
354
354
  // src/util/logger.ts
355
355
  function log(level, event, data) {
356
- if (LEVEL_ORDER[level] < LEVEL_ORDER[globalMinLevel]) return;
357
356
  const entry = {
358
357
  ts: (/* @__PURE__ */ new Date()).toISOString(),
359
358
  level,
360
359
  event,
361
360
  data
362
361
  };
363
- console.error(JSON.stringify(entry));
362
+ recentLogs.push(entry);
363
+ if (recentLogs.length > RECENT_LOGS_MAX) {
364
+ recentLogs.shift();
365
+ }
366
+ if (LEVEL_ORDER[level] >= LEVEL_ORDER[globalMinLevel]) {
367
+ console.error(JSON.stringify(entry));
368
+ }
364
369
  }
365
- var globalMinLevel, LEVEL_ORDER, logger;
370
+ var globalMinLevel, LEVEL_ORDER, RECENT_LOGS_MAX, recentLogs, logger;
366
371
  var init_logger = __esm({
367
372
  "src/util/logger.ts"() {
368
373
  "use strict";
@@ -374,6 +379,8 @@ var init_logger = __esm({
374
379
  error: 3,
375
380
  off: 4
376
381
  };
382
+ RECENT_LOGS_MAX = 100;
383
+ recentLogs = [];
377
384
  logger = {
378
385
  debug: (event, data) => log("debug", event, data),
379
386
  info: (event, data) => log("info", event, data),
@@ -9821,6 +9828,59 @@ var init_abort_scope = __esm({
9821
9828
  }
9822
9829
  });
9823
9830
 
9831
+ // src/cloud/report.ts
9832
+ function buildReport2(opts2) {
9833
+ return {
9834
+ error: {
9835
+ message: opts2.errorMessage,
9836
+ code: opts2.errorCode,
9837
+ http_status: opts2.httpStatus
9838
+ },
9839
+ context: {
9840
+ command: "/report",
9841
+ model: opts2.model,
9842
+ session_id: opts2.sessionId,
9843
+ request_id: opts2.requestId
9844
+ },
9845
+ user_message: opts2.userNote,
9846
+ metadata: {
9847
+ version: getAppVersion(),
9848
+ platform: `${process.platform} ${process.arch}`,
9849
+ node_version: process.version,
9850
+ cloud_mode: opts2.cloudMode ?? false
9851
+ }
9852
+ };
9853
+ }
9854
+ async function sendReport(payload, token) {
9855
+ try {
9856
+ const headers = { "Content-Type": "application/json" };
9857
+ if (token) {
9858
+ headers["Authorization"] = `Bearer ${token}`;
9859
+ }
9860
+ const res = await fetch(REPORT_URL, {
9861
+ method: "POST",
9862
+ headers,
9863
+ body: JSON.stringify(payload)
9864
+ });
9865
+ if (res.ok) {
9866
+ return { ok: true, message: "Report sent. Thanks for helping improve KimiFlare!" };
9867
+ }
9868
+ const body = await res.text().catch(() => "unknown error");
9869
+ return { ok: false, message: `Failed to send report (${res.status}): ${body}` };
9870
+ } catch (e) {
9871
+ const msg = e instanceof Error ? e.message : String(e);
9872
+ return { ok: false, message: `Failed to send report: ${msg}` };
9873
+ }
9874
+ }
9875
+ var REPORT_URL;
9876
+ var init_report2 = __esm({
9877
+ "src/cloud/report.ts"() {
9878
+ "use strict";
9879
+ init_version();
9880
+ REPORT_URL = "https://api.kimiflare.com/v1/report";
9881
+ }
9882
+ });
9883
+
9824
9884
  // src/ui/theme-context.tsx
9825
9885
  import { createContext, useContext } from "react";
9826
9886
  import { jsx } from "react/jsx-runtime";
@@ -10629,7 +10689,8 @@ function ApiErrorMessage({ httpStatus, code, message: message2 }) {
10629
10689
  "\u26A0 ",
10630
10690
  message2
10631
10691
  ] }),
10632
- meta && /* @__PURE__ */ jsx6(Text5, { color: theme.muted?.color ?? theme.info.color, dimColor: theme.muted?.dim ?? true, children: meta })
10692
+ meta && /* @__PURE__ */ jsx6(Text5, { color: theme.muted?.color ?? theme.info.color, dimColor: theme.muted?.dim ?? true, children: meta }),
10693
+ /* @__PURE__ */ jsx6(Text5, { color: theme.muted?.color ?? theme.info.color, dimColor: theme.muted?.dim ?? true, children: "Type /report to send diagnostic info" })
10633
10694
  ] });
10634
10695
  }
10635
10696
  var init_api_error_message = __esm({
@@ -13766,6 +13827,7 @@ var init_builtins = __esm({
13766
13827
  { name: "remote", argHint: "<prompt>", description: "Run a remote session on Cloudflare", source: "builtin" },
13767
13828
  { name: "update", description: "Check for updates", source: "builtin" },
13768
13829
  { name: "hello", description: "Send a voice note to the creator", source: "builtin" },
13830
+ { name: "report", argHint: "[send] [note]", description: "Report the last API error with diagnostic logs", source: "builtin" },
13769
13831
  { name: "shell", argHint: "[auto|bash|cmd|powershell|<path>]", description: "Show or set shell for bash tool", source: "builtin" },
13770
13832
  { name: "logout", description: "Clear stored credentials", source: "builtin" },
13771
13833
  { name: "exit", description: "Exit kimiflare", source: "builtin" },
@@ -17147,6 +17209,7 @@ ${wcagWarnings.join("\n")}` }
17147
17209
  const tasksRef = useRef3([]);
17148
17210
  const usageRef = useRef3(null);
17149
17211
  const gatewayMetaRef = useRef3(null);
17212
+ const lastApiErrorRef = useRef3(null);
17150
17213
  const updateCheckedRef = useRef3(false);
17151
17214
  const sessionStateRef = useRef3(emptySessionState());
17152
17215
  const artifactStoreRef = useRef3(new ArtifactStore());
@@ -18368,15 +18431,11 @@ ${wcagWarnings.join("\n")}` }
18368
18431
  { kind: "error", key: mkKey(), text: "The agent got stuck repeating the same actions. Here's what we know so far." }
18369
18432
  ]);
18370
18433
  } else if (e instanceof KimiApiError && (e.httpStatus === 429 || e.code === 3040 || e.httpStatus !== void 0 && e.httpStatus >= 500)) {
18434
+ const err = { httpStatus: e.httpStatus, code: e.code, message: humanizeCloudflareError(e) };
18435
+ lastApiErrorRef.current = err;
18371
18436
  setEvents((es) => [
18372
18437
  ...es,
18373
- {
18374
- kind: "api_error",
18375
- key: mkKey(),
18376
- httpStatus: e.httpStatus,
18377
- code: e.code,
18378
- message: humanizeCloudflareError(e)
18379
- }
18438
+ { kind: "api_error", key: mkKey(), ...err }
18380
18439
  ]);
18381
18440
  } else {
18382
18441
  const displayText = e instanceof KimiApiError ? humanizeCloudflareError(e) : `init failed: ${e.message}`;
@@ -19191,6 +19250,55 @@ ${lines.join("\n")}` }]);
19191
19250
  ]);
19192
19251
  return true;
19193
19252
  }
19253
+ if (c === "/report") {
19254
+ const err = lastApiErrorRef.current;
19255
+ if (!err) {
19256
+ setEvents((e) => [
19257
+ ...e,
19258
+ { kind: "info", key: mkKey(), text: "No recent API error to report." }
19259
+ ]);
19260
+ return true;
19261
+ }
19262
+ const note = rest.join(" ").trim();
19263
+ const isSend = note.toLowerCase() === "send" || note.toLowerCase().startsWith("send ");
19264
+ if (!isSend) {
19265
+ const preview = [
19266
+ "Report preview:",
19267
+ ` Error: ${err.message}`,
19268
+ err.httpStatus !== void 0 ? ` HTTP ${err.httpStatus}` : "",
19269
+ err.code !== void 0 ? ` Code: ${err.code}` : "",
19270
+ note ? ` Note: ${note}` : "",
19271
+ "",
19272
+ "Type `/report send` to submit or `/report send <note>` to add context."
19273
+ ].filter(Boolean).join("\n");
19274
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: preview }]);
19275
+ return true;
19276
+ }
19277
+ const userNote = note.slice(4).trim() || void 0;
19278
+ const payload = buildReport2({
19279
+ errorMessage: err.message,
19280
+ httpStatus: err.httpStatus,
19281
+ errorCode: err.code,
19282
+ sessionId: sessionIdRef.current ?? void 0,
19283
+ userNote,
19284
+ model: cfg?.model,
19285
+ cloudMode: cfg?.cloudMode
19286
+ });
19287
+ void sendReport(payload, cfg?.cloudToken).then((result) => {
19288
+ setEvents((e) => [
19289
+ ...e,
19290
+ { kind: result.ok ? "info" : "error", key: mkKey(), text: result.message }
19291
+ ]);
19292
+ if (result.ok) {
19293
+ lastApiErrorRef.current = null;
19294
+ }
19295
+ });
19296
+ setEvents((e) => [
19297
+ ...e,
19298
+ { kind: "info", key: mkKey(), text: "Sending report\u2026" }
19299
+ ]);
19300
+ return true;
19301
+ }
19194
19302
  if (c === "/logout") {
19195
19303
  unlink4(configPath()).catch(() => {
19196
19304
  });
@@ -19967,15 +20075,11 @@ ${lines.join("\n")}` }]);
19967
20075
  { kind: "cloud_quota_exhausted", key: mkKey(), used, limit, expiresAt }
19968
20076
  ]);
19969
20077
  } else if (e instanceof KimiApiError && (e.httpStatus === 429 || e.code === 3040 || e.httpStatus !== void 0 && e.httpStatus >= 500)) {
20078
+ const err = { httpStatus: e.httpStatus, code: e.code, message: humanizeCloudflareError(e) };
20079
+ lastApiErrorRef.current = err;
19970
20080
  setEvents((es) => [
19971
20081
  ...es,
19972
- {
19973
- kind: "api_error",
19974
- key: mkKey(),
19975
- httpStatus: e.httpStatus,
19976
- code: e.code,
19977
- message: humanizeCloudflareError(e)
19978
- }
20082
+ { kind: "api_error", key: mkKey(), ...err }
19979
20083
  ]);
19980
20084
  } else {
19981
20085
  const displayText2 = e instanceof KimiApiError ? humanizeCloudflareError(e) : e.message ?? String(e);
@@ -20393,6 +20497,7 @@ var init_app = __esm({
20393
20497
  init_errors();
20394
20498
  init_abort_scope();
20395
20499
  init_logger();
20500
+ init_report2();
20396
20501
  init_chat();
20397
20502
  init_status();
20398
20503
  init_permission();