kimiflare 0.64.0 → 0.65.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
@@ -9098,6 +9098,8 @@ var init_pricing = __esm({
9098
9098
  import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir10 } from "fs/promises";
9099
9099
  import { homedir as homedir11 } from "os";
9100
9100
  import { join as join18 } from "path";
9101
+ import { EventEmitter as EventEmitter2 } from "events";
9102
+ import { randomUUID } from "crypto";
9101
9103
  function usageDir2() {
9102
9104
  const xdg = process.env.XDG_DATA_HOME || join18(homedir11(), ".local", "share");
9103
9105
  return join18(xdg, "kimiflare");
@@ -9128,6 +9130,11 @@ async function saveLog(log2) {
9128
9130
  await mkdir10(usageDir2(), { recursive: true });
9129
9131
  await writeFile10(usagePath2(), JSON.stringify(log2, null, 2), "utf8");
9130
9132
  }
9133
+ function withLock(fn) {
9134
+ const next = writeChain.then(fn, fn);
9135
+ writeChain = next.catch(() => void 0);
9136
+ return next;
9137
+ }
9131
9138
  async function loadHistory() {
9132
9139
  try {
9133
9140
  const raw = await readFile13(historyPath(), "utf8");
@@ -9229,34 +9236,100 @@ function pruneUsageLog(log2) {
9229
9236
  return { ...log2, days, sessions };
9230
9237
  }
9231
9238
  async function recordUsage(sessionId, usage, gateway) {
9232
- const log2 = pruneUsageLog(await loadLog2());
9233
- const date = today2();
9234
- const gatewaySnapshot = gateway ? await fetchGatewayUsageSnapshot(gateway).catch(() => gatewaySnapshotFromMeta(gateway.meta)) : void 0;
9235
- const cost = calculateCost(usage.prompt_tokens, usage.completion_tokens, usage.prompt_tokens_details?.cached_tokens ?? 0);
9236
- const totalCost = gatewaySnapshot?.cost ?? cost.total;
9237
- const day = getOrCreateDay(log2, date);
9238
- day.promptTokens += usage.prompt_tokens;
9239
- day.completionTokens += usage.completion_tokens;
9240
- day.cachedTokens += usage.prompt_tokens_details?.cached_tokens ?? 0;
9241
- day.cost += totalCost;
9242
- if (gatewaySnapshot) {
9243
- day.gatewayRequests = (day.gatewayRequests ?? 0) + 1;
9244
- day.gatewayCachedRequests = (day.gatewayCachedRequests ?? 0) + (gatewaySnapshot.cached ? 1 : 0);
9245
- day.gatewayCost = (day.gatewayCost ?? 0) + (gatewaySnapshot.cost ?? 0);
9246
- }
9247
- const session = getOrCreateSession(log2, sessionId, date);
9248
- session.promptTokens += usage.prompt_tokens;
9249
- session.completionTokens += usage.completion_tokens;
9250
- session.cachedTokens += usage.prompt_tokens_details?.cached_tokens ?? 0;
9251
- session.cost += totalCost;
9252
- if (gatewaySnapshot) {
9253
- session.gatewayRequests = (session.gatewayRequests ?? 0) + 1;
9254
- session.gatewayCachedRequests = (session.gatewayCachedRequests ?? 0) + (gatewaySnapshot.cached ? 1 : 0);
9255
- session.gatewayCost = (session.gatewayCost ?? 0) + (gatewaySnapshot.cost ?? 0);
9256
- session.gatewayLogs = [...session.gatewayLogs ?? [], gatewaySnapshot].slice(-100);
9257
- }
9258
- await saveLog(log2);
9259
- await upsertHistoryDay(day);
9239
+ const cost = calculateCost(
9240
+ usage.prompt_tokens,
9241
+ usage.completion_tokens,
9242
+ usage.prompt_tokens_details?.cached_tokens ?? 0
9243
+ );
9244
+ const estimatedCost = cost.total;
9245
+ const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
9246
+ const turnId = randomUUID();
9247
+ const logId = gateway?.meta.logId;
9248
+ await withLock(async () => {
9249
+ const log2 = pruneUsageLog(await loadLog2());
9250
+ const date = today2();
9251
+ const day = getOrCreateDay(log2, date);
9252
+ day.promptTokens += usage.prompt_tokens;
9253
+ day.completionTokens += usage.completion_tokens;
9254
+ day.cachedTokens += cachedTokens;
9255
+ day.cost += estimatedCost;
9256
+ const session = getOrCreateSession(log2, sessionId, date);
9257
+ session.promptTokens += usage.prompt_tokens;
9258
+ session.completionTokens += usage.completion_tokens;
9259
+ session.cachedTokens += cachedTokens;
9260
+ session.cost += estimatedCost;
9261
+ const turn = {
9262
+ turnId,
9263
+ logId,
9264
+ estimatedCost,
9265
+ cacheStatus: gateway?.meta.cacheStatus
9266
+ };
9267
+ session.turns = [...session.turns ?? [], turn].slice(-MAX_TURNS_PER_SESSION);
9268
+ if (gateway) {
9269
+ const stub = gatewaySnapshotFromMeta(gateway.meta);
9270
+ if (stub) {
9271
+ session.gatewayRequests = (session.gatewayRequests ?? 0) + 1;
9272
+ session.gatewayCachedRequests = (session.gatewayCachedRequests ?? 0) + (stub.cached ? 1 : 0);
9273
+ session.gatewayLogs = [...session.gatewayLogs ?? [], stub].slice(-100);
9274
+ day.gatewayRequests = (day.gatewayRequests ?? 0) + 1;
9275
+ day.gatewayCachedRequests = (day.gatewayCachedRequests ?? 0) + (stub.cached ? 1 : 0);
9276
+ }
9277
+ }
9278
+ await saveLog(log2);
9279
+ await upsertHistoryDay(day);
9280
+ });
9281
+ usageEvents.emit("update", sessionId);
9282
+ if (gateway && logId) {
9283
+ void reconcileTurnCost(sessionId, turnId, gateway).catch(() => void 0);
9284
+ }
9285
+ }
9286
+ async function reconcileTurnCost(sessionId, turnId, gateway) {
9287
+ for (const delay of RECONCILE_DELAYS_MS) {
9288
+ await new Promise((r) => setTimeout(r, delay));
9289
+ let snapshot;
9290
+ try {
9291
+ snapshot = await fetchGatewayUsageSnapshot(gateway);
9292
+ } catch {
9293
+ continue;
9294
+ }
9295
+ if (!snapshot || typeof snapshot.cost !== "number") continue;
9296
+ const patched = await withLock(async () => {
9297
+ const log2 = pruneUsageLog(await loadLog2());
9298
+ const session = log2.sessions.find((s) => s.id === sessionId);
9299
+ const turn = session?.turns?.find((t) => t.turnId === turnId);
9300
+ if (!session || !turn || turn.confirmedCost !== void 0) return false;
9301
+ const delta = snapshot.cost - turn.estimatedCost;
9302
+ turn.confirmedCost = snapshot.cost;
9303
+ turn.durationMs = snapshot.duration;
9304
+ turn.cacheStatus = snapshot.cacheStatus ?? turn.cacheStatus;
9305
+ turn.reconciledAt = Date.now();
9306
+ session.cost += delta;
9307
+ session.gatewayCost = (session.gatewayCost ?? 0) + snapshot.cost;
9308
+ const day = getOrCreateDay(log2, session.date);
9309
+ day.cost += delta;
9310
+ day.gatewayCost = (day.gatewayCost ?? 0) + snapshot.cost;
9311
+ const logs = session.gatewayLogs ?? [];
9312
+ const idx = snapshot.logId ? logs.findIndex((l) => l.logId === snapshot.logId) : -1;
9313
+ if (idx >= 0) logs[idx] = snapshot;
9314
+ else logs.push(snapshot);
9315
+ session.gatewayLogs = logs.slice(-100);
9316
+ await saveLog(log2);
9317
+ await upsertHistoryDay(day);
9318
+ return true;
9319
+ });
9320
+ if (patched) {
9321
+ usageEvents.emit("update", sessionId);
9322
+ }
9323
+ return;
9324
+ }
9325
+ await withLock(async () => {
9326
+ const log2 = await loadLog2();
9327
+ const turn = log2.sessions.find((s) => s.id === sessionId)?.turns?.find((t) => t.turnId === turnId);
9328
+ if (!turn || turn.confirmedCost !== void 0) return;
9329
+ turn.reconcileFailed = true;
9330
+ await saveLog(log2);
9331
+ });
9332
+ usageEvents.emit("update", sessionId);
9260
9333
  }
9261
9334
  function mergeDays(usageDays, historyDays) {
9262
9335
  const map = /* @__PURE__ */ new Map();
@@ -9270,7 +9343,18 @@ async function getCostReport(sessionId) {
9270
9343
  const allDays = mergeDays(log2.days, history);
9271
9344
  const date = today2();
9272
9345
  const currentMonth = date.slice(0, 7);
9273
- const session = sessionId ? log2.sessions.find((s) => s.id === sessionId) ?? { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 } : { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 };
9346
+ const rawSession = sessionId ? log2.sessions.find((s) => s.id === sessionId) : void 0;
9347
+ const session = rawSession ? {
9348
+ date: rawSession.date,
9349
+ promptTokens: rawSession.promptTokens,
9350
+ completionTokens: rawSession.completionTokens,
9351
+ cachedTokens: rawSession.cachedTokens,
9352
+ cost: rawSession.cost,
9353
+ gatewayRequests: rawSession.gatewayRequests,
9354
+ gatewayCachedRequests: rawSession.gatewayCachedRequests,
9355
+ gatewayCost: rawSession.gatewayCost,
9356
+ reconcilePending: hasPendingReconcile(rawSession)
9357
+ } : { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 };
9274
9358
  const todayUsage = log2.days.find((d) => d.date === date) ?? { date, promptTokens: 0, completionTokens: 0, cachedTokens: 0, cost: 0 };
9275
9359
  const monthUsage = {
9276
9360
  date: currentMonth,
@@ -9308,6 +9392,12 @@ async function getCostReport(sessionId) {
9308
9392
  }
9309
9393
  return { session, today: todayUsage, month: monthUsage, allTime };
9310
9394
  }
9395
+ function hasPendingReconcile(session) {
9396
+ if (!session.turns) return false;
9397
+ return session.turns.some(
9398
+ (t) => t.logId && t.confirmedCost === void 0 && !t.reconcileFailed
9399
+ );
9400
+ }
9311
9401
  async function getSessionGatewayLogs(sessionId) {
9312
9402
  const log2 = await loadLog2();
9313
9403
  const session = log2.sessions.find((s) => s.id === sessionId);
@@ -9367,7 +9457,7 @@ function formatCostReport(report) {
9367
9457
  add("All time", report.allTime);
9368
9458
  return lines.join("\n");
9369
9459
  }
9370
- var LOG_VERSION2;
9460
+ var LOG_VERSION2, usageEvents, MAX_TURNS_PER_SESSION, RECONCILE_DELAYS_MS, writeChain;
9371
9461
  var init_usage_tracker = __esm({
9372
9462
  "src/usage-tracker.ts"() {
9373
9463
  "use strict";
@@ -9375,6 +9465,10 @@ var init_usage_tracker = __esm({
9375
9465
  init_pricing();
9376
9466
  init_storage_limits();
9377
9467
  LOG_VERSION2 = 1;
9468
+ usageEvents = new EventEmitter2();
9469
+ MAX_TURNS_PER_SESSION = 50;
9470
+ RECONCILE_DELAYS_MS = [500, 1e3, 2e3, 4e3];
9471
+ writeChain = Promise.resolve();
9378
9472
  }
9379
9473
  });
9380
9474
 
@@ -11523,6 +11617,10 @@ function StatusBar({ usage, sessionUsage, thinking, turnStartedAt, mode, context
11523
11617
  ] }),
11524
11618
  usage && /* @__PURE__ */ jsxs8(Box8, { children: [
11525
11619
  /* @__PURE__ */ jsx9(Text8, { color: theme.info.color, children: buildRightParts(usage, contextLimit, sessionUsage, gatewayMeta, cloudMode, cloudBudget).join(" \xB7 ") }),
11620
+ sessionUsage?.reconcilePending ? /* @__PURE__ */ jsxs8(Text8, { color: theme.muted?.color ?? theme.info.color, dimColor: theme.muted?.dim ?? true, children: [
11621
+ " ",
11622
+ /* @__PURE__ */ jsx9(Spinner3, { type: "dots" })
11623
+ ] }) : null,
11526
11624
  warn ? /* @__PURE__ */ jsxs8(Text8, { color: theme.warn, bold: true, children: [
11527
11625
  " \xB7 ",
11528
11626
  "/compact recommended"
@@ -11542,10 +11640,11 @@ function buildRightParts(usage, contextLimit, sessionUsage, gatewayMeta, cloudMo
11542
11640
  const cached = sessionUsage.cachedTokens;
11543
11641
  parts.push(`in ${sessionUsage.promptTokens}${cached ? ` (${cached} cached)` : ""}`);
11544
11642
  parts.push(`ctx ${pct}%`);
11643
+ const prefix = sessionUsage.reconcilePending ? "\u2248$" : "$";
11545
11644
  if (cloudMode) {
11546
- parts.push(`\x1B[9m$${sessionUsage.cost.toFixed(2)}\x1B[29m`);
11645
+ parts.push(`\x1B[9m${prefix}${sessionUsage.cost.toFixed(2)}\x1B[29m`);
11547
11646
  } else {
11548
- parts.push(`$${sessionUsage.cost.toFixed(2)}`);
11647
+ parts.push(`${prefix}${sessionUsage.cost.toFixed(2)}`);
11549
11648
  }
11550
11649
  } else {
11551
11650
  const cached = usage.prompt_tokens_details?.cached_tokens ?? 0;
@@ -17804,6 +17903,210 @@ var init_slash_picker = __esm({
17804
17903
  }
17805
17904
  });
17806
17905
 
17906
+ // src/ui/use-picker-controller.ts
17907
+ import { useCallback as useCallback5, useEffect as useEffect7, useMemo as useMemo2, useRef as useRef4, useState as useState14 } from "react";
17908
+ function filterPickerItems(items, query) {
17909
+ return fuzzyFilter(items, query, (item) => item.name).slice(0, 50);
17910
+ }
17911
+ function shouldOpenMentionPicker(input, cursorOffset, pickerCancelOffset) {
17912
+ if (pickerCancelOffset === cursorOffset) return false;
17913
+ if (cursorOffset > 0 && input[cursorOffset - 1] === "@") {
17914
+ const beforeAt = cursorOffset - 2;
17915
+ return beforeAt < 0 || /\s/.test(input[beforeAt]);
17916
+ }
17917
+ return false;
17918
+ }
17919
+ function shouldOpenSlashPicker(input, cursorOffset, cancelOffset) {
17920
+ if (cancelOffset === cursorOffset) return false;
17921
+ if (cursorOffset === 0 || input[cursorOffset - 1] !== "/") return false;
17922
+ return /^\s*$/.test(input.slice(0, cursorOffset - 1));
17923
+ }
17924
+ function insertSlashCommand(input, anchor, name) {
17925
+ let tokenEnd = anchor + 1;
17926
+ while (tokenEnd < input.length && !/\s/.test(input[tokenEnd])) tokenEnd++;
17927
+ const head = input.slice(0, anchor + 1) + name;
17928
+ const tail = " " + input.slice(tokenEnd).replace(/^\s+/, "");
17929
+ return { value: head + tail, cursor: head.length + 1 };
17930
+ }
17931
+ function decidePickerTransition(active, input, cursorOffset, pickerCancelOffset, filePickerEnabled) {
17932
+ if (active !== null) {
17933
+ const trigger = active.kind === "file" ? "@" : "/";
17934
+ if (cursorOffset < active.anchor) return { kind: "close" };
17935
+ if (input[active.anchor] !== trigger) return { kind: "close" };
17936
+ const query = input.slice(active.anchor + 1, cursorOffset);
17937
+ if (/\s/.test(query)) return { kind: "close" };
17938
+ return { kind: "none" };
17939
+ }
17940
+ if (pickerCancelOffset === cursorOffset) {
17941
+ return { kind: "dropCancel" };
17942
+ }
17943
+ if (filePickerEnabled && shouldOpenMentionPicker(input, cursorOffset, pickerCancelOffset)) {
17944
+ return {
17945
+ kind: "open",
17946
+ picker: { kind: "file", anchor: cursorOffset - 1, selected: 0 },
17947
+ loadFiles: true
17948
+ };
17949
+ }
17950
+ if (shouldOpenSlashPicker(input, cursorOffset, pickerCancelOffset)) {
17951
+ return {
17952
+ kind: "open",
17953
+ picker: { kind: "slash", anchor: cursorOffset - 1, selected: 0 },
17954
+ loadFiles: false
17955
+ };
17956
+ }
17957
+ return { kind: "none" };
17958
+ }
17959
+ function usePickerController(opts2) {
17960
+ const {
17961
+ input,
17962
+ cursorOffset,
17963
+ setInput,
17964
+ setCursorOffset,
17965
+ filePickerEnabled,
17966
+ allSlashCommands,
17967
+ modalActive,
17968
+ loadFilePickerItems,
17969
+ onFileSelected,
17970
+ onSlashSelected,
17971
+ getRecentFiles
17972
+ } = opts2;
17973
+ const [active, setActive] = useState14(null);
17974
+ const [fileItemsRaw, setFileItemsRaw] = useState14([]);
17975
+ const filesLoadedRef = useRef4(false);
17976
+ const cancelOffsetRef = useRef4(null);
17977
+ const onFileSelectedRef = useRef4(onFileSelected);
17978
+ onFileSelectedRef.current = onFileSelected;
17979
+ const onSlashSelectedRef = useRef4(onSlashSelected);
17980
+ onSlashSelectedRef.current = onSlashSelected;
17981
+ const getRecentFilesRef = useRef4(getRecentFiles);
17982
+ getRecentFilesRef.current = getRecentFiles;
17983
+ const loadFilePickerItemsRef = useRef4(loadFilePickerItems);
17984
+ loadFilePickerItemsRef.current = loadFilePickerItems;
17985
+ const activeAnchor = active?.anchor ?? null;
17986
+ const activeKind = active?.kind ?? null;
17987
+ const query = useMemo2(() => {
17988
+ if (activeAnchor === null) return "";
17989
+ return input.slice(activeAnchor + 1, cursorOffset);
17990
+ }, [input, cursorOffset, activeAnchor]);
17991
+ const fileItems = useMemo2(() => {
17992
+ if (activeKind !== "file") return [];
17993
+ const items = filterPickerItems(fileItemsRaw, query).slice();
17994
+ const recents = getRecentFilesRef.current();
17995
+ return items.sort((a, b) => {
17996
+ const aRecent = recents.get(a.name) ?? 0;
17997
+ const bRecent = recents.get(b.name) ?? 0;
17998
+ if (aRecent && !bRecent) return -1;
17999
+ if (!aRecent && bRecent) return 1;
18000
+ if (aRecent && bRecent) return bRecent - aRecent;
18001
+ if (a.isDirectory && !b.isDirectory) return -1;
18002
+ if (!a.isDirectory && b.isDirectory) return 1;
18003
+ return a.name.localeCompare(b.name);
18004
+ });
18005
+ }, [activeKind, fileItemsRaw, query]);
18006
+ const slashItems = useMemo2(() => {
18007
+ if (activeKind !== "slash") return [];
18008
+ return fuzzyFilter(allSlashCommands, query, (c) => c.name).slice(0, 50);
18009
+ }, [activeKind, allSlashCommands, query]);
18010
+ useEffect7(() => {
18011
+ const t = decidePickerTransition(
18012
+ active,
18013
+ input,
18014
+ cursorOffset,
18015
+ cancelOffsetRef.current,
18016
+ filePickerEnabled
18017
+ );
18018
+ if (t.kind === "close") {
18019
+ setActive(null);
18020
+ return;
18021
+ }
18022
+ if (t.kind === "dropCancel") {
18023
+ cancelOffsetRef.current = null;
18024
+ return;
18025
+ }
18026
+ if (t.kind === "open") {
18027
+ setActive(t.picker);
18028
+ if (t.loadFiles && !filesLoadedRef.current) {
18029
+ filesLoadedRef.current = true;
18030
+ void loadFilePickerItemsRef.current().then((items) => setFileItemsRaw(items)).catch(() => setFileItemsRaw([]));
18031
+ }
18032
+ }
18033
+ }, [input, cursorOffset, active, filePickerEnabled]);
18034
+ useEffect7(() => {
18035
+ if (active?.kind !== "file") return;
18036
+ const max = Math.max(0, fileItems.length - 1);
18037
+ if (active.selected > max) {
18038
+ setActive({ ...active, selected: max });
18039
+ }
18040
+ }, [fileItems.length, active]);
18041
+ useEffect7(() => {
18042
+ if (active?.kind !== "slash") return;
18043
+ const max = Math.max(0, slashItems.length - 1);
18044
+ if (active.selected > max) {
18045
+ setActive({ ...active, selected: max });
18046
+ }
18047
+ }, [slashItems.length, active]);
18048
+ useEffect7(() => {
18049
+ if (modalActive && active !== null) {
18050
+ setActive(null);
18051
+ }
18052
+ }, [modalActive, active]);
18053
+ const onUp = useCallback5(() => {
18054
+ setActive((p) => {
18055
+ if (!p) return null;
18056
+ const next = Math.max(0, p.selected - 1);
18057
+ return next === p.selected ? p : { ...p, selected: next };
18058
+ });
18059
+ }, []);
18060
+ const onDown = useCallback5(() => {
18061
+ setActive((p) => {
18062
+ if (!p) return null;
18063
+ const max = p.kind === "file" ? Math.max(0, fileItems.length - 1) : Math.max(0, slashItems.length - 1);
18064
+ const next = Math.min(max, p.selected + 1);
18065
+ return next === p.selected ? p : { ...p, selected: next };
18066
+ });
18067
+ }, [fileItems.length, slashItems.length]);
18068
+ const onSelect = useCallback5(() => {
18069
+ if (!active) return;
18070
+ if (active.kind === "file") {
18071
+ const item2 = fileItems[active.selected];
18072
+ if (!item2) return;
18073
+ onFileSelectedRef.current?.(item2.name);
18074
+ const insert = item2.name + (item2.isDirectory ? "/" : " ");
18075
+ const newInput = input.slice(0, active.anchor) + insert + input.slice(cursorOffset);
18076
+ setInput(newInput);
18077
+ setCursorOffset(active.anchor + insert.length);
18078
+ setActive(null);
18079
+ return;
18080
+ }
18081
+ const item = slashItems[active.selected];
18082
+ if (!item) return;
18083
+ const { value } = insertSlashCommand(input, active.anchor, item.name);
18084
+ setActive(null);
18085
+ onSlashSelectedRef.current(value);
18086
+ }, [active, fileItems, slashItems, input, cursorOffset, setInput, setCursorOffset]);
18087
+ const onCancel = useCallback5(() => {
18088
+ cancelOffsetRef.current = cursorOffset;
18089
+ setActive(null);
18090
+ }, [cursorOffset]);
18091
+ return {
18092
+ active,
18093
+ isActive: active !== null,
18094
+ query,
18095
+ fileItems,
18096
+ slashItems,
18097
+ onUp,
18098
+ onDown,
18099
+ onSelect,
18100
+ onCancel
18101
+ };
18102
+ }
18103
+ var init_use_picker_controller = __esm({
18104
+ "src/ui/use-picker-controller.ts"() {
18105
+ "use strict";
18106
+ init_fuzzy();
18107
+ }
18108
+ });
18109
+
17807
18110
  // src/cost-attribution/tui-report.ts
17808
18111
  var tui_report_exports = {};
17809
18112
  __export(tui_report_exports, {
@@ -17889,13 +18192,9 @@ var init_tui_report = __esm({
17889
18192
  var app_exports = {};
17890
18193
  __export(app_exports, {
17891
18194
  buildFilePickerIgnoreList: () => buildFilePickerIgnoreList,
17892
- filterPickerItems: () => filterPickerItems,
17893
- insertSlashCommand: () => insertSlashCommand,
17894
- renderApp: () => renderApp,
17895
- shouldOpenMentionPicker: () => shouldOpenMentionPicker,
17896
- shouldOpenSlashPicker: () => shouldOpenSlashPicker
18195
+ renderApp: () => renderApp
17897
18196
  });
17898
- import React15, { useState as useState14, useRef as useRef4, useEffect as useEffect7, useCallback as useCallback5 } from "react";
18197
+ import React15, { useState as useState15, useRef as useRef5, useEffect as useEffect8, useCallback as useCallback6 } from "react";
17899
18198
  import { Box as Box25, Text as Text26, useApp, useInput as useInput10, render } from "ink";
17900
18199
  import SelectInput9 from "ink-select-input";
17901
18200
  import { existsSync as existsSync4, statSync as statSync4 } from "fs";
@@ -18004,29 +18303,6 @@ function buildFilePickerIgnoreList(cwd) {
18004
18303
  }
18005
18304
  return [...hardcoded, ...gitignorePatterns];
18006
18305
  }
18007
- function filterPickerItems(items, query) {
18008
- return fuzzyFilter(items, query, (item) => item.name).slice(0, 50);
18009
- }
18010
- function shouldOpenMentionPicker(input, cursorOffset, pickerCancelOffset) {
18011
- if (pickerCancelOffset === cursorOffset) return false;
18012
- if (cursorOffset > 0 && input[cursorOffset - 1] === "@") {
18013
- const beforeAt = cursorOffset - 2;
18014
- return beforeAt < 0 || /\s/.test(input[beforeAt]);
18015
- }
18016
- return false;
18017
- }
18018
- function shouldOpenSlashPicker(input, cursorOffset, cancelOffset) {
18019
- if (cancelOffset === cursorOffset) return false;
18020
- if (cursorOffset === 0 || input[cursorOffset - 1] !== "/") return false;
18021
- return /^\s*$/.test(input.slice(0, cursorOffset - 1));
18022
- }
18023
- function insertSlashCommand(input, anchor, name) {
18024
- let tokenEnd = anchor + 1;
18025
- while (tokenEnd < input.length && !/\s/.test(input[tokenEnd])) tokenEnd++;
18026
- const head = input.slice(0, anchor + 1) + name;
18027
- const tail = " " + input.slice(tokenEnd).replace(/^\s+/, "");
18028
- return { value: head + tail, cursor: head.length + 1 };
18029
- }
18030
18306
  function gatewayFromConfig(cfg) {
18031
18307
  if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
18032
18308
  if (!cfg.aiGatewayId) return void 0;
@@ -18158,13 +18434,13 @@ function App({
18158
18434
  initialCloudDeviceId
18159
18435
  }) {
18160
18436
  const { exit } = useApp();
18161
- const [cfg, setCfg] = useState14(initialCfg);
18162
- const [lspScope, setLspScope] = useState14(initialLspScope);
18163
- const [lspProjectPath, setLspProjectPath] = useState14(initialLspProjectPath);
18164
- const [cloudToken, setCloudToken] = useState14(initialCloudToken);
18165
- const [cloudDeviceId, setCloudDeviceId] = useState14(initialCloudDeviceId);
18166
- const [events, setRawEvents] = useState14([]);
18167
- const setEvents = useCallback5(
18437
+ const [cfg, setCfg] = useState15(initialCfg);
18438
+ const [lspScope, setLspScope] = useState15(initialLspScope);
18439
+ const [lspProjectPath, setLspProjectPath] = useState15(initialLspProjectPath);
18440
+ const [cloudToken, setCloudToken] = useState15(initialCloudToken);
18441
+ const [cloudDeviceId, setCloudDeviceId] = useState15(initialCloudDeviceId);
18442
+ const [events, setRawEvents] = useState15([]);
18443
+ const setEvents = useCallback6(
18168
18444
  (updater) => {
18169
18445
  setRawEvents((prev) => {
18170
18446
  const next = typeof updater === "function" ? updater(prev) : updater;
@@ -18173,13 +18449,24 @@ function App({
18173
18449
  },
18174
18450
  []
18175
18451
  );
18176
- const [input, setInput] = useState14("");
18177
- const [busy, setBusy] = useState14(false);
18178
- const [usage, setUsage] = useState14(null);
18179
- const [sessionUsage, setSessionUsage] = useState14(null);
18180
- const [gatewayMeta, setGatewayMeta] = useState14(null);
18181
- const [cloudBudget, setCloudBudget] = useState14(null);
18182
- const [showReasoning, setShowReasoning] = useState14(false);
18452
+ const [input, setInput] = useState15("");
18453
+ const [busy, setBusy] = useState15(false);
18454
+ const [usage, setUsage] = useState15(null);
18455
+ const [sessionUsage, setSessionUsage] = useState15(null);
18456
+ useEffect8(() => {
18457
+ const handler = (sid) => {
18458
+ if (sessionIdRef.current && sid === sessionIdRef.current) {
18459
+ void getCostReport(sid).then((report) => setSessionUsage(report.session));
18460
+ }
18461
+ };
18462
+ usageEvents.on("update", handler);
18463
+ return () => {
18464
+ usageEvents.off("update", handler);
18465
+ };
18466
+ }, []);
18467
+ const [gatewayMeta, setGatewayMeta] = useState15(null);
18468
+ const [cloudBudget, setCloudBudget] = useState15(null);
18469
+ const [showReasoning, setShowReasoning] = useState15(false);
18183
18470
  const {
18184
18471
  pending: perm,
18185
18472
  askPermission: askForPermission,
@@ -18200,52 +18487,52 @@ function App({
18200
18487
  ]);
18201
18488
  }
18202
18489
  );
18203
- const [limitModal, setLimitModal] = useState14(null);
18204
- const [loopModal, setLoopModal] = useState14(null);
18205
- const [queue, setQueue] = useState14([]);
18206
- const [history, setHistory] = useState14([]);
18207
- const [historyIndex, setHistoryIndex] = useState14(-1);
18208
- const [draftInput, setDraftInput] = useState14("");
18209
- const [mode, setMode] = useState14("edit");
18210
- const [codeMode, setCodeMode] = useState14(false);
18490
+ const [limitModal, setLimitModal] = useState15(null);
18491
+ const [loopModal, setLoopModal] = useState15(null);
18492
+ const [queue, setQueue] = useState15([]);
18493
+ const [history, setHistory] = useState15([]);
18494
+ const [historyIndex, setHistoryIndex] = useState15(-1);
18495
+ const [draftInput, setDraftInput] = useState15("");
18496
+ const [mode, setMode] = useState15("edit");
18497
+ const [codeMode, setCodeMode] = useState15(false);
18211
18498
  const filePickerEnabled = initialCfg?.filePicker ?? true;
18212
- const [effort, setEffort] = useState14(
18499
+ const [effort, setEffort] = useState15(
18213
18500
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
18214
18501
  );
18215
- const [resumeSessions, setResumeSessions] = useState14(null);
18216
- const [checkpointSession, setCheckpointSession] = useState14(null);
18217
- const [checkpointList, setCheckpointList] = useState14([]);
18218
- const [commandWizard, setCommandWizard] = useState14(null);
18219
- const [commandPicker, setCommandPicker] = useState14(null);
18220
- const [commandToDelete, setCommandToDelete] = useState14(null);
18221
- const [showCommandList, setShowCommandList] = useState14(false);
18222
- const [showLspWizard, setShowLspWizard] = useState14(false);
18223
- const [showRemoteDashboard, setShowRemoteDashboard] = useState14(false);
18224
- const [selectedRemoteSession, setSelectedRemoteSession] = useState14(null);
18225
- const [showInboxModal, setShowInboxModal] = useState14(false);
18226
- const [tasks, setTasks] = useState14([]);
18227
- const [tasksStartedAt, setTasksStartedAt] = useState14(null);
18228
- const [tasksStartTokens, setTasksStartTokens] = useState14(0);
18229
- const [turnStartedAt, setTurnStartedAt] = useState14(null);
18230
- const [turnPhase, setTurnPhase] = useState14("waiting");
18231
- const [currentToolName, setCurrentToolName] = useState14(null);
18232
- const [lastActivityAt, setLastActivityAt] = useState14(null);
18233
- const [verbose, setVerbose] = useState14(false);
18234
- const [hasUpdate, setHasUpdate] = useState14(initialUpdateResult?.hasUpdate ?? false);
18235
- const [latestVersion, setLatestVersion] = useState14(initialUpdateResult?.latestVersion ?? null);
18236
- const [theme, setTheme] = useState14(resolveTheme(initialCfg?.theme));
18237
- const [showThemePicker, setShowThemePicker] = useState14(false);
18238
- const [originalTheme, setOriginalTheme] = useState14(null);
18239
- const [skillsActive, setSkillsActive] = useState14(0);
18240
- const [memoryRecalled, setMemoryRecalled] = useState14(false);
18241
- const [intentTier, setIntentTier] = useState14(null);
18242
- const [kimiMdStale, setKimiMdStale] = useState14(false);
18243
- const [gitBranch, setGitBranch] = useState14(null);
18244
- const [lastSessionTopic, setLastSessionTopic] = useState14(null);
18245
- useEffect7(() => {
18502
+ const [resumeSessions, setResumeSessions] = useState15(null);
18503
+ const [checkpointSession, setCheckpointSession] = useState15(null);
18504
+ const [checkpointList, setCheckpointList] = useState15([]);
18505
+ const [commandWizard, setCommandWizard] = useState15(null);
18506
+ const [commandPicker, setCommandPicker] = useState15(null);
18507
+ const [commandToDelete, setCommandToDelete] = useState15(null);
18508
+ const [showCommandList, setShowCommandList] = useState15(false);
18509
+ const [showLspWizard, setShowLspWizard] = useState15(false);
18510
+ const [showRemoteDashboard, setShowRemoteDashboard] = useState15(false);
18511
+ const [selectedRemoteSession, setSelectedRemoteSession] = useState15(null);
18512
+ const [showInboxModal, setShowInboxModal] = useState15(false);
18513
+ const [tasks, setTasks] = useState15([]);
18514
+ const [tasksStartedAt, setTasksStartedAt] = useState15(null);
18515
+ const [tasksStartTokens, setTasksStartTokens] = useState15(0);
18516
+ const [turnStartedAt, setTurnStartedAt] = useState15(null);
18517
+ const [turnPhase, setTurnPhase] = useState15("waiting");
18518
+ const [currentToolName, setCurrentToolName] = useState15(null);
18519
+ const [lastActivityAt, setLastActivityAt] = useState15(null);
18520
+ const [verbose, setVerbose] = useState15(false);
18521
+ const [hasUpdate, setHasUpdate] = useState15(initialUpdateResult?.hasUpdate ?? false);
18522
+ const [latestVersion, setLatestVersion] = useState15(initialUpdateResult?.latestVersion ?? null);
18523
+ const [theme, setTheme] = useState15(resolveTheme(initialCfg?.theme));
18524
+ const [showThemePicker, setShowThemePicker] = useState15(false);
18525
+ const [originalTheme, setOriginalTheme] = useState15(null);
18526
+ const [skillsActive, setSkillsActive] = useState15(0);
18527
+ const [memoryRecalled, setMemoryRecalled] = useState15(false);
18528
+ const [intentTier, setIntentTier] = useState15(null);
18529
+ const [kimiMdStale, setKimiMdStale] = useState15(false);
18530
+ const [gitBranch, setGitBranch] = useState15(null);
18531
+ const [lastSessionTopic, setLastSessionTopic] = useState15(null);
18532
+ useEffect8(() => {
18246
18533
  setGitBranch(detectGitBranch());
18247
18534
  }, []);
18248
- useEffect7(() => {
18535
+ useEffect8(() => {
18249
18536
  void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
18250
18537
  ({ listSessions: listSessions2 }) => listSessions2(1).then((sessions) => {
18251
18538
  const last = sessions[0];
@@ -18255,7 +18542,7 @@ function App({
18255
18542
  })
18256
18543
  );
18257
18544
  }, []);
18258
- useEffect7(() => {
18545
+ useEffect8(() => {
18259
18546
  const onSigint = () => {
18260
18547
  logger.info("sigint:fired", {
18261
18548
  hasHandler: sigintHandlerRef.current !== null
@@ -18267,7 +18554,7 @@ function App({
18267
18554
  process.off("SIGINT", onSigint);
18268
18555
  };
18269
18556
  }, []);
18270
- useEffect7(() => {
18557
+ useEffect8(() => {
18271
18558
  let cancelled = false;
18272
18559
  loadAndMergeThemes().then(({ errors, wcagWarnings }) => {
18273
18560
  if (cancelled) return;
@@ -18291,7 +18578,7 @@ ${wcagWarnings.join("\n")}` }
18291
18578
  cancelled = true;
18292
18579
  };
18293
18580
  }, []);
18294
- useEffect7(() => {
18581
+ useEffect8(() => {
18295
18582
  if (!cfg?.cloudMode || !initialCloudToken) return;
18296
18583
  let cancelled = false;
18297
18584
  const fetchBudget = async () => {
@@ -18317,81 +18604,56 @@ ${wcagWarnings.join("\n")}` }
18317
18604
  cancelled = true;
18318
18605
  };
18319
18606
  }, [cfg?.cloudMode, initialCloudToken]);
18320
- const [cursorOffset, setCursorOffset] = useState14(0);
18321
- const [activePicker, setActivePicker] = useState14(null);
18322
- const [filePickerItems, setFilePickerItems] = useState14([]);
18323
- const filePickerLoadedRef = useRef4(false);
18324
- const [customCommandsVersion, setCustomCommandsVersion] = useState14(0);
18325
- const cacheStableRef = useRef4(initialCfg?.cacheStablePrompts !== false);
18326
- const messagesRef = useRef4(
18607
+ const [cursorOffset, setCursorOffset] = useState15(0);
18608
+ const [customCommandsVersion, setCustomCommandsVersion] = useState15(0);
18609
+ const cacheStableRef = useRef5(initialCfg?.cacheStablePrompts !== false);
18610
+ const messagesRef = useRef5(
18327
18611
  makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
18328
18612
  );
18329
- const executorRef = useRef4(new ToolExecutor(ALL_TOOLS));
18330
- const activeAsstIdRef = useRef4(null);
18331
- const sessionScopeRef = useRef4(new AbortScope());
18332
- const activeScopeRef = useRef4(null);
18333
- const supervisorRef = useRef4(new TurnSupervisor());
18334
- const isAbortingRef = useRef4(false);
18335
- const lastEscapeAtRef = useRef4(0);
18336
- const sigintHandlerRef = useRef4(null);
18337
- const limitResolveRef = useRef4(null);
18338
- const loopResolveRef = useRef4(null);
18339
- const pendingToolCallsRef = useRef4(/* @__PURE__ */ new Map());
18340
- const sessionIdRef = useRef4(null);
18341
- const sessionCreatedAtRef = useRef4(null);
18342
- const sessionTitleRef = useRef4(null);
18343
- const modeRef = useRef4(mode);
18344
- const effortRef = useRef4(effort);
18345
- const tasksRef = useRef4([]);
18346
- const usageRef = useRef4(null);
18347
- const gatewayMetaRef = useRef4(null);
18348
- const lastApiErrorRef = useRef4(null);
18349
- const updateCheckedRef = useRef4(false);
18350
- const sessionStateRef = useRef4(emptySessionState());
18351
- const artifactStoreRef = useRef4(new ArtifactStore());
18352
- const compiledContextRef = useRef4(initialCfg?.compiledContext === true);
18353
- const updateNudgedRef = useRef4(false);
18354
- const compactSuggestedRef = useRef4(false);
18355
- const mcpManagerRef = useRef4(new McpManager());
18356
- const mcpToolsRef = useRef4([]);
18357
- const mcpInitRef = useRef4(false);
18358
- const submitRef = useRef4(() => {
18613
+ const executorRef = useRef5(new ToolExecutor(ALL_TOOLS));
18614
+ const activeAsstIdRef = useRef5(null);
18615
+ const sessionScopeRef = useRef5(new AbortScope());
18616
+ const activeScopeRef = useRef5(null);
18617
+ const supervisorRef = useRef5(new TurnSupervisor());
18618
+ const isAbortingRef = useRef5(false);
18619
+ const lastEscapeAtRef = useRef5(0);
18620
+ const sigintHandlerRef = useRef5(null);
18621
+ const limitResolveRef = useRef5(null);
18622
+ const loopResolveRef = useRef5(null);
18623
+ const pendingToolCallsRef = useRef5(/* @__PURE__ */ new Map());
18624
+ const sessionIdRef = useRef5(null);
18625
+ const sessionCreatedAtRef = useRef5(null);
18626
+ const sessionTitleRef = useRef5(null);
18627
+ const modeRef = useRef5(mode);
18628
+ const effortRef = useRef5(effort);
18629
+ const tasksRef = useRef5([]);
18630
+ const usageRef = useRef5(null);
18631
+ const gatewayMetaRef = useRef5(null);
18632
+ const lastApiErrorRef = useRef5(null);
18633
+ const updateCheckedRef = useRef5(false);
18634
+ const sessionStateRef = useRef5(emptySessionState());
18635
+ const artifactStoreRef = useRef5(new ArtifactStore());
18636
+ const compiledContextRef = useRef5(initialCfg?.compiledContext === true);
18637
+ const updateNudgedRef = useRef5(false);
18638
+ const compactSuggestedRef = useRef5(false);
18639
+ const mcpManagerRef = useRef5(new McpManager());
18640
+ const mcpToolsRef = useRef5([]);
18641
+ const mcpInitRef = useRef5(false);
18642
+ const submitRef = useRef5(() => {
18359
18643
  });
18360
- const lspManagerRef = useRef4(new LspManager());
18361
- const lspToolsRef = useRef4([]);
18362
- const lspInitRef = useRef4(false);
18363
- const busyRef = useRef4(busy);
18364
- const memoryManagerRef = useRef4(null);
18365
- const sessionStartRecallRef = useRef4(null);
18366
- const kimiMdStaleNudgedRef = useRef4(false);
18367
- const turnCounterRef = useRef4(0);
18368
- const pendingTextRef = useRef4(/* @__PURE__ */ new Map());
18369
- const flushTimeoutRef = useRef4(null);
18370
- const customCommandsRef = useRef4([]);
18371
- const pickerCancelRef = useRef4(null);
18372
- const recentFilesRef = useRef4(/* @__PURE__ */ new Map());
18644
+ const lspManagerRef = useRef5(new LspManager());
18645
+ const lspToolsRef = useRef5([]);
18646
+ const lspInitRef = useRef5(false);
18647
+ const busyRef = useRef5(busy);
18648
+ const memoryManagerRef = useRef5(null);
18649
+ const sessionStartRecallRef = useRef5(null);
18650
+ const kimiMdStaleNudgedRef = useRef5(false);
18651
+ const turnCounterRef = useRef5(0);
18652
+ const pendingTextRef = useRef5(/* @__PURE__ */ new Map());
18653
+ const flushTimeoutRef = useRef5(null);
18654
+ const customCommandsRef = useRef5([]);
18655
+ const recentFilesRef = useRef5(/* @__PURE__ */ new Map());
18373
18656
  const MAX_RECENT_FILES = 10;
18374
- const pickerAnchor = activePicker?.anchor ?? null;
18375
- const pickerKind = activePicker?.kind ?? null;
18376
- const pickerQuery = React15.useMemo(() => {
18377
- if (pickerAnchor === null) return null;
18378
- return input.slice(pickerAnchor + 1, cursorOffset);
18379
- }, [input, cursorOffset, pickerAnchor]);
18380
- const filteredFileItems = React15.useMemo(() => {
18381
- if (pickerKind !== "file" || pickerQuery === null) return [];
18382
- const items = filterPickerItems(filePickerItems, pickerQuery).slice();
18383
- const now2 = Date.now();
18384
- return items.sort((a, b) => {
18385
- const aRecent = recentFilesRef.current.get(a.name) ?? 0;
18386
- const bRecent = recentFilesRef.current.get(b.name) ?? 0;
18387
- if (aRecent && !bRecent) return -1;
18388
- if (!aRecent && bRecent) return 1;
18389
- if (aRecent && bRecent) return bRecent - aRecent;
18390
- if (a.isDirectory && !b.isDirectory) return -1;
18391
- if (!a.isDirectory && b.isDirectory) return 1;
18392
- return a.name.localeCompare(b.name);
18393
- });
18394
- }, [pickerKind, filePickerItems, pickerQuery]);
18395
18657
  const allSlashCommands = React15.useMemo(() => {
18396
18658
  const customs = customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({
18397
18659
  name: c.name,
@@ -18400,138 +18662,43 @@ ${wcagWarnings.join("\n")}` }
18400
18662
  }));
18401
18663
  return [...BUILTIN_COMMANDS, ...customs];
18402
18664
  }, [customCommandsVersion]);
18403
- const filteredSlashItems = React15.useMemo(() => {
18404
- if (pickerKind !== "slash" || pickerQuery === null) return [];
18405
- return fuzzyFilter(allSlashCommands, pickerQuery, (c) => c.name).slice(0, 50);
18406
- }, [pickerKind, allSlashCommands, pickerQuery]);
18407
- useEffect7(() => {
18408
- if (activePicker !== null) {
18409
- const trigger = activePicker.kind === "file" ? "@" : "/";
18410
- if (cursorOffset < activePicker.anchor) {
18411
- setActivePicker(null);
18412
- return;
18413
- }
18414
- if (input[activePicker.anchor] !== trigger) {
18415
- setActivePicker(null);
18416
- return;
18417
- }
18418
- const query = input.slice(activePicker.anchor + 1, cursorOffset);
18419
- if (/\s/.test(query)) {
18420
- setActivePicker(null);
18421
- return;
18422
- }
18423
- return;
18424
- }
18425
- if (pickerCancelRef.current === cursorOffset) {
18426
- pickerCancelRef.current = null;
18427
- return;
18428
- }
18429
- if (filePickerEnabled && shouldOpenMentionPicker(input, cursorOffset, pickerCancelRef.current)) {
18430
- setActivePicker({ kind: "file", anchor: cursorOffset - 1, selected: 0 });
18431
- if (!filePickerLoadedRef.current) {
18432
- filePickerLoadedRef.current = true;
18433
- const cwd = process.cwd();
18434
- void fg4("**/*", {
18435
- cwd,
18436
- ignore: buildFilePickerIgnoreList(cwd),
18437
- dot: false,
18438
- absolute: false,
18439
- onlyFiles: false,
18440
- markDirectories: true
18441
- }).then((entries) => {
18442
- const strings = entries.slice(0, 300);
18443
- const items = strings.map((e) => ({
18444
- name: e.endsWith("/") ? e.slice(0, -1) : e,
18445
- isDirectory: e.endsWith("/")
18446
- }));
18447
- items.sort((a, b) => {
18448
- if (a.isDirectory && !b.isDirectory) return -1;
18449
- if (!a.isDirectory && b.isDirectory) return 1;
18450
- return a.name.localeCompare(b.name);
18451
- });
18452
- setFilePickerItems(items);
18453
- }).catch(() => {
18454
- setFilePickerItems([]);
18455
- });
18456
- }
18457
- return;
18458
- }
18459
- if (shouldOpenSlashPicker(input, cursorOffset, pickerCancelRef.current)) {
18460
- setActivePicker({ kind: "slash", anchor: cursorOffset - 1, selected: 0 });
18461
- return;
18462
- }
18463
- }, [input, cursorOffset, activePicker, filePickerEnabled]);
18464
- useEffect7(() => {
18465
- if (activePicker?.kind !== "file") return;
18466
- const max = Math.max(0, filteredFileItems.length - 1);
18467
- if (activePicker.selected > max) {
18468
- setActivePicker({ ...activePicker, selected: max });
18469
- }
18470
- }, [filteredFileItems.length, activePicker]);
18471
- useEffect7(() => {
18472
- if (activePicker?.kind !== "slash") return;
18473
- const max = Math.max(0, filteredSlashItems.length - 1);
18474
- if (activePicker.selected > max) {
18475
- setActivePicker({ ...activePicker, selected: max });
18476
- }
18477
- }, [filteredSlashItems.length, activePicker]);
18478
- const handlePickerUp = useCallback5(() => {
18479
- setActivePicker((p) => {
18480
- if (!p) return null;
18481
- const next = Math.max(0, p.selected - 1);
18482
- return next === p.selected ? p : { ...p, selected: next };
18665
+ const modalActive = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || resumeSessions !== null || checkpointSession !== null || perm !== null || limitModal !== null || loopModal !== null || showInboxModal;
18666
+ const loadFilePickerItems = useCallback6(async () => {
18667
+ const cwd = process.cwd();
18668
+ const entries = await fg4("**/*", {
18669
+ cwd,
18670
+ ignore: buildFilePickerIgnoreList(cwd),
18671
+ dot: false,
18672
+ absolute: false,
18673
+ onlyFiles: false,
18674
+ markDirectories: true
18483
18675
  });
18484
- }, []);
18485
- const handlePickerDown = useCallback5(() => {
18486
- setActivePicker((p) => {
18487
- if (!p) return null;
18488
- const max = p.kind === "file" ? Math.max(0, filteredFileItems.length - 1) : Math.max(0, filteredSlashItems.length - 1);
18489
- const next = Math.min(max, p.selected + 1);
18490
- return next === p.selected ? p : { ...p, selected: next };
18676
+ const strings = entries.slice(0, 300);
18677
+ const items = strings.map((e) => ({
18678
+ name: e.endsWith("/") ? e.slice(0, -1) : e,
18679
+ isDirectory: e.endsWith("/")
18680
+ }));
18681
+ items.sort((a, b) => {
18682
+ if (a.isDirectory && !b.isDirectory) return -1;
18683
+ if (!a.isDirectory && b.isDirectory) return 1;
18684
+ return a.name.localeCompare(b.name);
18491
18685
  });
18492
- }, [filteredFileItems.length, filteredSlashItems.length]);
18493
- const handlePickerSelect = useCallback5(() => {
18494
- if (!activePicker) return;
18495
- if (activePicker.kind === "file") {
18496
- const item2 = filteredFileItems[activePicker.selected];
18497
- if (!item2) return;
18498
- trackRecentFile(recentFilesRef, item2.name, MAX_RECENT_FILES);
18499
- const insert = item2.name + (item2.isDirectory ? "/" : " ");
18500
- const newInput = input.slice(0, activePicker.anchor) + insert + input.slice(cursorOffset);
18501
- setInput(newInput);
18502
- setCursorOffset(activePicker.anchor + insert.length);
18503
- setActivePicker(null);
18504
- return;
18505
- }
18506
- const item = filteredSlashItems[activePicker.selected];
18507
- if (!item) return;
18508
- const { value } = insertSlashCommand(input, activePicker.anchor, item.name);
18509
- setActivePicker(null);
18510
- submitRef.current(value);
18511
- }, [activePicker, filteredFileItems, filteredSlashItems, input, cursorOffset]);
18512
- const handlePickerCancel = useCallback5(() => {
18513
- pickerCancelRef.current = cursorOffset;
18514
- setActivePicker(null);
18515
- }, [cursorOffset]);
18516
- useEffect7(() => {
18517
- const modalActive = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || resumeSessions !== null || checkpointSession !== null || perm !== null || limitModal !== null || loopModal !== null || showInboxModal;
18518
- if (modalActive && activePicker !== null) {
18519
- setActivePicker(null);
18520
- }
18521
- }, [
18522
- commandWizard,
18523
- commandPicker,
18524
- commandToDelete,
18525
- showCommandList,
18526
- showLspWizard,
18527
- resumeSessions,
18528
- perm,
18529
- limitModal,
18530
- loopModal,
18531
- showInboxModal,
18532
- activePicker
18533
- ]);
18534
- useEffect7(() => {
18686
+ return items;
18687
+ }, []);
18688
+ const picker = usePickerController({
18689
+ input,
18690
+ cursorOffset,
18691
+ setInput,
18692
+ setCursorOffset,
18693
+ filePickerEnabled,
18694
+ allSlashCommands,
18695
+ modalActive,
18696
+ loadFilePickerItems,
18697
+ onFileSelected: (name) => trackRecentFile(recentFilesRef, name, MAX_RECENT_FILES),
18698
+ onSlashSelected: (value) => submitRef.current(value),
18699
+ getRecentFiles: () => recentFilesRef.current
18700
+ });
18701
+ useEffect8(() => {
18535
18702
  if (!cfg) return;
18536
18703
  void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
18537
18704
  ({ pruneSessions: pruneSessions2 }) => pruneSessions2().then((removed) => {
@@ -18643,7 +18810,7 @@ ${wcagWarnings.join("\n")}` }
18643
18810
  }
18644
18811
  });
18645
18812
  }, [cfg, setEvents]);
18646
- useEffect7(() => {
18813
+ useEffect8(() => {
18647
18814
  const id = setInterval(() => {
18648
18815
  try {
18649
18816
  performance.clearMarks();
@@ -18653,7 +18820,7 @@ ${wcagWarnings.join("\n")}` }
18653
18820
  }, 3e5);
18654
18821
  return () => clearInterval(id);
18655
18822
  }, []);
18656
- const reloadCustomCommands = useCallback5(async () => {
18823
+ const reloadCustomCommands = useCallback6(async () => {
18657
18824
  const { commands, warnings } = await loadCustomCommands(process.cwd());
18658
18825
  customCommandsRef.current = commands;
18659
18826
  setCustomCommandsVersion((v) => v + 1);
@@ -18668,7 +18835,7 @@ ${wcagWarnings.join("\n")}` }
18668
18835
  ]);
18669
18836
  }
18670
18837
  }, [setEvents]);
18671
- useEffect7(() => {
18838
+ useEffect8(() => {
18672
18839
  if (!cfg || updateCheckedRef.current) return;
18673
18840
  updateCheckedRef.current = true;
18674
18841
  if (initialUpdateResult) {
@@ -18719,7 +18886,7 @@ ${wcagWarnings.join("\n")}` }
18719
18886
  }
18720
18887
  });
18721
18888
  }, [cfg, initialUpdateResult]);
18722
- useEffect7(() => {
18889
+ useEffect8(() => {
18723
18890
  modeRef.current = mode;
18724
18891
  if (cacheStableRef.current) {
18725
18892
  messagesRef.current[1] = {
@@ -18746,10 +18913,10 @@ ${wcagWarnings.join("\n")}` }
18746
18913
  executorRef.current.clearSessionPermissions();
18747
18914
  }
18748
18915
  }, [mode, cfg?.model]);
18749
- useEffect7(() => {
18916
+ useEffect8(() => {
18750
18917
  effortRef.current = effort;
18751
18918
  }, [effort]);
18752
- useEffect7(() => {
18919
+ useEffect8(() => {
18753
18920
  if (!cfg) return;
18754
18921
  const id = setInterval(() => {
18755
18922
  void checkForUpdate().then((result) => {
@@ -18780,7 +18947,7 @@ ${wcagWarnings.join("\n")}` }
18780
18947
  }, 30 * 60 * 1e3);
18781
18948
  return () => clearInterval(id);
18782
18949
  }, [cfg]);
18783
- const initMcp = useCallback5(async () => {
18950
+ const initMcp = useCallback6(async () => {
18784
18951
  if (!cfg?.mcpServers || mcpInitRef.current) return;
18785
18952
  mcpInitRef.current = true;
18786
18953
  const manager = mcpManagerRef.current;
@@ -18845,7 +19012,7 @@ ${wcagWarnings.join("\n")}` }
18845
19012
  ]);
18846
19013
  }
18847
19014
  }, [cfg]);
18848
- const initLsp = useCallback5(async () => {
19015
+ const initLsp = useCallback6(async () => {
18849
19016
  if (!cfg?.lspEnabled || !cfg?.lspServers || lspInitRef.current) {
18850
19017
  if (lspInitRef.current) return;
18851
19018
  if (!cfg?.lspEnabled) {
@@ -18908,7 +19075,7 @@ ${wcagWarnings.join("\n")}` }
18908
19075
  ]);
18909
19076
  }
18910
19077
  }, [cfg]);
18911
- useEffect7(() => {
19078
+ useEffect8(() => {
18912
19079
  if (cfg && !mcpInitRef.current) {
18913
19080
  void initMcp();
18914
19081
  }
@@ -18916,7 +19083,7 @@ ${wcagWarnings.join("\n")}` }
18916
19083
  void initLsp();
18917
19084
  }
18918
19085
  }, [cfg, initMcp, initLsp]);
18919
- const ensureSessionId = useCallback5(() => {
19086
+ const ensureSessionId = useCallback6(() => {
18920
19087
  if (sessionIdRef.current) return sessionIdRef.current;
18921
19088
  const firstUser = messagesRef.current.find((m) => m.role === "user");
18922
19089
  let firstText = "session";
@@ -18929,7 +19096,7 @@ ${wcagWarnings.join("\n")}` }
18929
19096
  sessionIdRef.current = makeSessionId(firstText);
18930
19097
  return sessionIdRef.current;
18931
19098
  }, []);
18932
- const saveSessionSafe = useCallback5(async () => {
19099
+ const saveSessionSafe = useCallback6(async () => {
18933
19100
  if (!cfg) return;
18934
19101
  ensureSessionId();
18935
19102
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
@@ -18955,7 +19122,7 @@ ${wcagWarnings.join("\n")}` }
18955
19122
  ]);
18956
19123
  }
18957
19124
  }, [cfg, ensureSessionId]);
18958
- const onIterationEnd = useCallback5(
19125
+ const onIterationEnd = useCallback6(
18959
19126
  async (messages, signal) => {
18960
19127
  if (signal.aborted) return messages;
18961
19128
  if (!shouldCompact({ messages })) return messages;
@@ -19156,7 +19323,7 @@ ${wcagWarnings.join("\n")}` }
19156
19323
  void lspManagerRef.current.stopAll().finally(() => exit());
19157
19324
  }
19158
19325
  };
19159
- const flushAssistantUpdates = useCallback5(() => {
19326
+ const flushAssistantUpdates = useCallback6(() => {
19160
19327
  flushTimeoutRef.current = null;
19161
19328
  const pending = pendingTextRef.current;
19162
19329
  if (pending.size === 0) return;
@@ -19174,7 +19341,7 @@ ${wcagWarnings.join("\n")}` }
19174
19341
  })
19175
19342
  );
19176
19343
  }, []);
19177
- const updateAssistant = useCallback5(
19344
+ const updateAssistant = useCallback6(
19178
19345
  (id, patch) => {
19179
19346
  const result = patch({ text: "", reasoning: "" });
19180
19347
  const assistantResult = result;
@@ -19203,7 +19370,7 @@ ${wcagWarnings.join("\n")}` }
19203
19370
  },
19204
19371
  [flushAssistantUpdates]
19205
19372
  );
19206
- const updateTool = useCallback5(
19373
+ const updateTool = useCallback6(
19207
19374
  (id, patch) => {
19208
19375
  setEvents(
19209
19376
  (evts) => evts.map(
@@ -19213,11 +19380,11 @@ ${wcagWarnings.join("\n")}` }
19213
19380
  },
19214
19381
  []
19215
19382
  );
19216
- const updateGatewayMeta = useCallback5((meta) => {
19383
+ const updateGatewayMeta = useCallback6((meta) => {
19217
19384
  gatewayMetaRef.current = meta;
19218
19385
  setGatewayMeta(meta);
19219
19386
  }, []);
19220
- const runCompact = useCallback5(async () => {
19387
+ const runCompact = useCallback6(async () => {
19221
19388
  if (!cfg) return;
19222
19389
  if (busy) {
19223
19390
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "can't compact while model is running" }]);
@@ -19313,11 +19480,11 @@ ${wcagWarnings.join("\n")}` }
19313
19480
  pendingToolCallsRef.current.clear();
19314
19481
  }
19315
19482
  }, [cfg, busy, saveSessionSafe]);
19316
- const openResumePicker = useCallback5(async () => {
19483
+ const openResumePicker = useCallback6(async () => {
19317
19484
  const sessions = await listSessions(200, process.cwd());
19318
19485
  setResumeSessions(sessions);
19319
19486
  }, []);
19320
- const runInit = useCallback5(async () => {
19487
+ const runInit = useCallback6(async () => {
19321
19488
  if (!cfg) return;
19322
19489
  if (busy) {
19323
19490
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "can't /init while model is running" }]);
@@ -19612,7 +19779,7 @@ ${wcagWarnings.join("\n")}` }
19612
19779
  pendingToolCallsRef.current.clear();
19613
19780
  }
19614
19781
  }, [cfg, busy, updateAssistant, updateTool, updateGatewayMeta]);
19615
- const handleThemePick = useCallback5(
19782
+ const handleThemePick = useCallback6(
19616
19783
  (picked) => {
19617
19784
  setShowThemePicker(false);
19618
19785
  if (!picked) return;
@@ -19630,7 +19797,7 @@ ${wcagWarnings.join("\n")}` }
19630
19797
  },
19631
19798
  []
19632
19799
  );
19633
- const doResumeSession = useCallback5(
19800
+ const doResumeSession = useCallback6(
19634
19801
  async (filePath, checkpointId) => {
19635
19802
  try {
19636
19803
  const file = checkpointId ? (await loadSessionFromCheckpoint(filePath, checkpointId)).file : await loadSession(filePath);
@@ -19682,7 +19849,7 @@ ${wcagWarnings.join("\n")}` }
19682
19849
  },
19683
19850
  []
19684
19851
  );
19685
- const handleResumePick = useCallback5(
19852
+ const handleResumePick = useCallback6(
19686
19853
  async (picked) => {
19687
19854
  setResumeSessions(null);
19688
19855
  if (!picked) return;
@@ -19704,7 +19871,7 @@ ${wcagWarnings.join("\n")}` }
19704
19871
  },
19705
19872
  [doResumeSession]
19706
19873
  );
19707
- const handleCheckpointPick = useCallback5(
19874
+ const handleCheckpointPick = useCallback6(
19708
19875
  async (checkpointId) => {
19709
19876
  const session = checkpointSession;
19710
19877
  setCheckpointSession(null);
@@ -19723,7 +19890,7 @@ ${wcagWarnings.join("\n")}` }
19723
19890
  },
19724
19891
  [checkpointSession, doResumeSession]
19725
19892
  );
19726
- const handleSlash = useCallback5(
19893
+ const handleSlash = useCallback6(
19727
19894
  (cmd) => {
19728
19895
  const raw = cmd.trim();
19729
19896
  const [head, ...rest] = raw.split(/\s+/);
@@ -20696,7 +20863,7 @@ ${lines.join("\n")}` }]);
20696
20863
  },
20697
20864
  [cfg, exit, usage, theme, mode, openResumePicker, runCompact, runInit, initMcp, setCfg, setShowRemoteDashboard, setSelectedRemoteSession]
20698
20865
  );
20699
- const handleCommandSave = useCallback5(
20866
+ const handleCommandSave = useCallback6(
20700
20867
  async (opts2) => {
20701
20868
  setCommandWizard(null);
20702
20869
  try {
@@ -20718,7 +20885,7 @@ ${lines.join("\n")}` }]);
20718
20885
  },
20719
20886
  [commandWizard, reloadCustomCommands, setEvents]
20720
20887
  );
20721
- const handleCommandDelete = useCallback5(
20888
+ const handleCommandDelete = useCallback6(
20722
20889
  async (cmd) => {
20723
20890
  setCommandToDelete(null);
20724
20891
  try {
@@ -20737,7 +20904,7 @@ ${lines.join("\n")}` }]);
20737
20904
  },
20738
20905
  [reloadCustomCommands, setEvents]
20739
20906
  );
20740
- const processMessage = useCallback5(
20907
+ const processMessage = useCallback6(
20741
20908
  async (text, displayText, opts2) => {
20742
20909
  if (!cfg) return;
20743
20910
  let trimmed = text.trim();
@@ -21254,14 +21421,14 @@ ${lines.join("\n")}` }]);
21254
21421
  },
21255
21422
  [cfg, handleSlash, updateAssistant, updateTool, saveSessionSafe, updateGatewayMeta]
21256
21423
  );
21257
- useEffect7(() => {
21424
+ useEffect8(() => {
21258
21425
  if (!busy && queue.length > 0 && supervisorRef.current.phase === "idle") {
21259
21426
  const next = queue[0];
21260
21427
  setQueue((q) => q.slice(1));
21261
21428
  processMessage(next.full, next.display, { queuedKey: next.key });
21262
21429
  }
21263
21430
  }, [busy, queue, processMessage]);
21264
- const submit = useCallback5(
21431
+ const submit = useCallback6(
21265
21432
  (full, display) => {
21266
21433
  const trimmedFull = full.trim();
21267
21434
  if (!trimmedFull) return;
@@ -21284,7 +21451,7 @@ ${lines.join("\n")}` }]);
21284
21451
  [processMessage]
21285
21452
  );
21286
21453
  submitRef.current = submit;
21287
- useEffect7(() => {
21454
+ useEffect8(() => {
21288
21455
  if (compactSuggestedRef.current) return;
21289
21456
  if (usage && usage.prompt_tokens / CONTEXT_LIMIT >= AUTO_COMPACT_SUGGEST_PCT) {
21290
21457
  compactSuggestedRef.current = true;
@@ -21548,21 +21715,21 @@ ${lines.join("\n")}` }]);
21548
21715
  intentTier: intentTier ?? void 0
21549
21716
  }
21550
21717
  ),
21551
- activePicker?.kind === "file" && /* @__PURE__ */ jsx27(
21718
+ picker.active?.kind === "file" && /* @__PURE__ */ jsx27(
21552
21719
  FilePicker,
21553
21720
  {
21554
- items: filteredFileItems,
21555
- selectedIndex: activePicker.selected,
21556
- query: pickerQuery ?? "",
21721
+ items: picker.fileItems,
21722
+ selectedIndex: picker.active.selected,
21723
+ query: picker.query,
21557
21724
  recentFiles: new Set(recentFilesRef.current.keys())
21558
21725
  }
21559
21726
  ),
21560
- activePicker?.kind === "slash" && /* @__PURE__ */ jsx27(
21727
+ picker.active?.kind === "slash" && /* @__PURE__ */ jsx27(
21561
21728
  SlashPicker,
21562
21729
  {
21563
- items: filteredSlashItems,
21564
- selectedIndex: activePicker.selected,
21565
- query: pickerQuery ?? ""
21730
+ items: picker.slashItems,
21731
+ selectedIndex: picker.active.selected,
21732
+ query: picker.query
21566
21733
  }
21567
21734
  ),
21568
21735
  /* @__PURE__ */ jsxs25(Box25, { marginTop: 1, children: [
@@ -21576,11 +21743,11 @@ ${lines.join("\n")}` }]);
21576
21743
  enablePaste: true,
21577
21744
  cursorOffset,
21578
21745
  onCursorChange: setCursorOffset,
21579
- pickerActive: activePicker !== null,
21580
- onPickerUp: handlePickerUp,
21581
- onPickerDown: handlePickerDown,
21582
- onPickerSelect: handlePickerSelect,
21583
- onPickerCancel: handlePickerCancel,
21746
+ pickerActive: picker.isActive,
21747
+ onPickerUp: picker.onUp,
21748
+ onPickerDown: picker.onDown,
21749
+ onPickerSelect: picker.onSelect,
21750
+ onPickerCancel: picker.onCancel,
21584
21751
  onHistoryUp: () => {
21585
21752
  if (history.length === 0) return;
21586
21753
  if (historyIndex === -1) {
@@ -21718,7 +21885,7 @@ var init_app = __esm({
21718
21885
  init_lsp_nudge();
21719
21886
  init_file_picker();
21720
21887
  init_slash_picker();
21721
- init_fuzzy();
21888
+ init_use_picker_controller();
21722
21889
  MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
21723
21890
  FEEDBACK_WORKER_URL2 = "https://hello.kimiflare.com";
21724
21891
  CONTEXT_LIMIT = 262e3;