kimiflare 0.40.0 → 0.41.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
@@ -1861,6 +1861,9 @@ var init_code_mode = __esm({
1861
1861
  });
1862
1862
 
1863
1863
  // src/agent/loop.ts
1864
+ function isHighSignalMemory(memory) {
1865
+ return memory.topicKey === "project_dependencies" || memory.topicKey === "project_tsconfig" || memory.topicKey === "project_entry_point" || memory.category === "instruction" || memory.category === "preference" || memory.category === "event" && memory.importance >= 3;
1866
+ }
1864
1867
  async function runAgentTurn(opts2) {
1865
1868
  const turnStart = performance.now();
1866
1869
  const max = opts2.maxToolIterations ?? 50;
@@ -2260,6 +2263,15 @@ ${sandboxResult.output}` : `${warningPrefix}${sandboxResult.output}`;
2260
2263
  void 0,
2261
2264
  memory.topicKey
2262
2265
  );
2266
+ if (isHighSignalMemory(memory)) {
2267
+ const sid = opts2.sessionId ?? "default";
2268
+ const current = (driftAccumulator.get(sid) ?? 0) + 1;
2269
+ driftAccumulator.set(sid, current);
2270
+ if (current >= DRIFT_THRESHOLD) {
2271
+ opts2.callbacks.onKimiMdStale?.();
2272
+ driftAccumulator.set(sid, 0);
2273
+ }
2274
+ }
2263
2275
  }
2264
2276
  } catch {
2265
2277
  }
@@ -2271,6 +2283,12 @@ ${sandboxResult.output}` : `${warningPrefix}${sandboxResult.output}`;
2271
2283
  if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
2272
2284
  }
2273
2285
  }
2286
+ if (opts2.sessionId) {
2287
+ const current = driftAccumulator.get(opts2.sessionId) ?? 0;
2288
+ if (current > 0) {
2289
+ driftAccumulator.set(opts2.sessionId, Math.max(0, current - 1));
2290
+ }
2291
+ }
2274
2292
  if (opts2.onIterationEnd) {
2275
2293
  opts2.messages = await opts2.onIterationEnd(opts2.messages, opts2.signal);
2276
2294
  if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
@@ -2303,7 +2321,7 @@ function validateToolArguments(raw) {
2303
2321
  return "{}";
2304
2322
  }
2305
2323
  }
2306
- var BudgetExhaustedError, codeModeApiCache;
2324
+ var BudgetExhaustedError, codeModeApiCache, driftAccumulator, DRIFT_THRESHOLD;
2307
2325
  var init_loop = __esm({
2308
2326
  "src/agent/loop.ts"() {
2309
2327
  "use strict";
@@ -2321,6 +2339,8 @@ var init_loop = __esm({
2321
2339
  }
2322
2340
  };
2323
2341
  codeModeApiCache = /* @__PURE__ */ new Map();
2342
+ driftAccumulator = /* @__PURE__ */ new Map();
2343
+ DRIFT_THRESHOLD = 5;
2324
2344
  }
2325
2345
  });
2326
2346
 
@@ -7253,7 +7273,7 @@ import { useEffect, useState } from "react";
7253
7273
  import { Box as Box5, Text as Text5 } from "ink";
7254
7274
  import Spinner3 from "ink-spinner";
7255
7275
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
7256
- function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode, effort, contextLimit, hasUpdate, latestVersion, gatewayMeta, codeMode, cloudMode, cloudBudget }) {
7276
+ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode, effort, contextLimit, hasUpdate, latestVersion, gatewayMeta, codeMode, cloudMode, cloudBudget, kimiMdStale }) {
7257
7277
  const theme = useTheme();
7258
7278
  const [now2, setNow] = useState(Date.now());
7259
7279
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
@@ -7296,6 +7316,10 @@ function StatusBar({ model, usage, sessionUsage, thinking, turnStartedAt, mode,
7296
7316
  "update available",
7297
7317
  latestVersion ? ` \u2192 ${latestVersion}` : "",
7298
7318
  " \xB7 run /update"
7319
+ ] }) : null,
7320
+ kimiMdStale ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
7321
+ " \xB7 ",
7322
+ "\u26A0 KIMI.md stale \xB7 run /init"
7299
7323
  ] }) : null
7300
7324
  ] })
7301
7325
  ] });
@@ -9712,11 +9736,21 @@ var init_usage_tracker = __esm({
9712
9736
  }
9713
9737
  });
9714
9738
 
9739
+ // src/memory/schema.ts
9740
+ var DEFAULT_EMBEDDING_DIM;
9741
+ var init_schema = __esm({
9742
+ "src/memory/schema.ts"() {
9743
+ "use strict";
9744
+ DEFAULT_EMBEDDING_DIM = 768;
9745
+ }
9746
+ });
9747
+
9715
9748
  // src/memory/db.ts
9716
9749
  var db_exports = {};
9717
9750
  __export(db_exports, {
9718
9751
  clearMemoriesForRepo: () => clearMemoriesForRepo,
9719
9752
  closeMemoryDb: () => closeMemoryDb,
9753
+ countHighSignalMemoriesSince: () => countHighSignalMemoriesSince,
9720
9754
  deleteExcessMemories: () => deleteExcessMemories,
9721
9755
  deleteMemoriesByIds: () => deleteMemoriesByIds,
9722
9756
  deleteOldMemories: () => deleteOldMemories,
@@ -10103,6 +10137,19 @@ function getMemoryById(db, id) {
10103
10137
  const row = db.prepare("SELECT * FROM memories WHERE id = ?").get(id);
10104
10138
  return row ? rowToMemory(row) : null;
10105
10139
  }
10140
+ function countHighSignalMemoriesSince(db, repoPath, since) {
10141
+ const row = db.prepare(
10142
+ `SELECT COUNT(*) as count FROM memories
10143
+ WHERE repo_path = ? AND created_at > ?
10144
+ AND forgotten = 0 AND superseded_by IS NULL
10145
+ AND (
10146
+ topic_key IN ('project_dependencies', 'project_tsconfig', 'project_entry_point')
10147
+ OR category IN ('instruction', 'preference')
10148
+ OR (category = 'event' AND importance >= 3)
10149
+ )`
10150
+ ).get(repoPath, since);
10151
+ return row?.count ?? 0;
10152
+ }
10106
10153
  var dbInstance, dbPathInstance;
10107
10154
  var init_db = __esm({
10108
10155
  "src/memory/db.ts"() {
@@ -10464,6 +10511,7 @@ var init_manager3 = __esm({
10464
10511
  "src/memory/manager.ts"() {
10465
10512
  "use strict";
10466
10513
  init_client();
10514
+ init_schema();
10467
10515
  init_db();
10468
10516
  init_embeddings();
10469
10517
  init_retrieval();
@@ -10593,6 +10641,48 @@ Return a JSON array of strings. Example:
10593
10641
  }
10594
10642
  return { id: memory.id, superseded: supersededIds.length > 0 ? supersededIds : void 0 };
10595
10643
  }
10644
+ /**
10645
+ * Count high-signal memories created since the given timestamp.
10646
+ * Used for KIMI.md drift detection (Trigger A: session-start check).
10647
+ */
10648
+ countHighSignalMemoriesSince(repoPath, since) {
10649
+ if (!this.db) return 0;
10650
+ return countHighSignalMemoriesSince(this.db, repoPath, since);
10651
+ }
10652
+ /**
10653
+ * Get the timestamp of the most recent KIMI.md refresh memory.
10654
+ * Returns 0 if none exists.
10655
+ */
10656
+ getLastKimiMdRefreshTime(repoPath) {
10657
+ if (!this.db) return 0;
10658
+ const rows = this.db.prepare(
10659
+ `SELECT created_at FROM memories
10660
+ WHERE repo_path = ? AND topic_key = 'kimi_md_refresh'
10661
+ AND forgotten = 0 AND superseded_by IS NULL
10662
+ ORDER BY created_at DESC LIMIT 1`
10663
+ ).all(repoPath);
10664
+ return rows[0]?.created_at ?? 0;
10665
+ }
10666
+ /**
10667
+ * Record that KIMI.md was refreshed. Creates a lightweight memory
10668
+ * so drift detection knows when the snapshot was last updated.
10669
+ */
10670
+ async recordKimiMdRefresh(repoPath, sessionId) {
10671
+ if (!this.db) return;
10672
+ const embedding = new Float32Array(DEFAULT_EMBEDDING_DIM);
10673
+ insertMemory(
10674
+ this.db,
10675
+ {
10676
+ content: `KIMI.md refreshed for ${repoPath}`,
10677
+ category: "event",
10678
+ sourceSessionId: sessionId,
10679
+ repoPath,
10680
+ importance: 2,
10681
+ topicKey: "kimi_md_refresh"
10682
+ },
10683
+ embedding
10684
+ );
10685
+ }
10596
10686
  /**
10597
10687
  * Recall memories using the full hybrid retrieval pipeline.
10598
10688
  */
@@ -13476,6 +13566,7 @@ function App({
13476
13566
  const [latestVersion, setLatestVersion] = useState10(initialUpdateResult?.latestVersion ?? null);
13477
13567
  const [theme, setTheme] = useState10(resolveTheme(initialCfg?.theme));
13478
13568
  const [showThemePicker, setShowThemePicker] = useState10(false);
13569
+ const [kimiMdStale, setKimiMdStale] = useState10(false);
13479
13570
  useEffect6(() => {
13480
13571
  if (!cfg?.cloudMode || !initialCloudToken) return;
13481
13572
  let cancelled = false;
@@ -13531,6 +13622,8 @@ function App({
13531
13622
  const busyRef = useRef3(busy);
13532
13623
  const memoryManagerRef = useRef3(null);
13533
13624
  const sessionStartRecallRef = useRef3(null);
13625
+ const kimiMdStaleNudgedRef = useRef3(false);
13626
+ const turnCounterRef = useRef3(0);
13534
13627
  const pendingTextRef = useRef3(/* @__PURE__ */ new Map());
13535
13628
  const flushTimeoutRef = useRef3(null);
13536
13629
  const customCommandsRef = useRef3([]);
@@ -13760,6 +13853,13 @@ function App({
13760
13853
  } catch {
13761
13854
  }
13762
13855
  })();
13856
+ if (existsSync3(join22(cwd, "KIMI.md"))) {
13857
+ const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
13858
+ const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
13859
+ if (driftCount >= 5) {
13860
+ setKimiMdStale(true);
13861
+ }
13862
+ }
13763
13863
  } else {
13764
13864
  memoryManagerRef.current?.close();
13765
13865
  memoryManagerRef.current = null;
@@ -14531,7 +14631,17 @@ function App({
14531
14631
  }
14532
14632
  permResolveRef.current = resolve2;
14533
14633
  setPerm({ tool: req.tool, args: req.args, resolve: resolve2 });
14534
- })
14634
+ }),
14635
+ onKimiMdStale: () => {
14636
+ if (!kimiMdStaleNudgedRef.current) {
14637
+ kimiMdStaleNudgedRef.current = true;
14638
+ setKimiMdStale(true);
14639
+ setEvents((e) => [
14640
+ ...e,
14641
+ { kind: "info", key: mkKey(), text: "Project context may be stale. Run /init to refresh KIMI.md based on recent changes." }
14642
+ ]);
14643
+ }
14644
+ }
14535
14645
  }
14536
14646
  });
14537
14647
  if (existsSync3(join22(cwd, "KIMI.md"))) {
@@ -14560,6 +14670,9 @@ function App({
14560
14670
  ...e,
14561
14671
  { kind: "info", key: mkKey(), text: "KIMI.md generated; context loaded for future turns" }
14562
14672
  ]);
14673
+ void memoryManagerRef.current?.recordKimiMdRefresh(cwd, ensureSessionId());
14674
+ setKimiMdStale(false);
14675
+ kimiMdStaleNudgedRef.current = false;
14563
14676
  }
14564
14677
  } catch (e) {
14565
14678
  if (e.name === "AbortError") {
@@ -15506,6 +15619,13 @@ ${lines.join("\n")}` }]);
15506
15619
  };
15507
15620
  }
15508
15621
  }
15622
+ turnCounterRef.current += 1;
15623
+ if (turnCounterRef.current % 15 === 0 && existsSync3(join22(process.cwd(), "KIMI.md")) && !kimiMdStale) {
15624
+ setEvents((e) => [
15625
+ ...e,
15626
+ { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
15627
+ ]);
15628
+ }
15509
15629
  setBusy(true);
15510
15630
  gatewayMetaRef.current = null;
15511
15631
  setGatewayMeta(null);
@@ -15643,7 +15763,17 @@ ${lines.join("\n")}` }]);
15643
15763
  onToolLimitReached: () => new Promise((resolve2) => {
15644
15764
  limitResolveRef.current = resolve2;
15645
15765
  setLimitModal({ limit: 50, resolve: resolve2 });
15646
- })
15766
+ }),
15767
+ onKimiMdStale: () => {
15768
+ if (!kimiMdStaleNudgedRef.current) {
15769
+ kimiMdStaleNudgedRef.current = true;
15770
+ setKimiMdStale(true);
15771
+ setEvents((e) => [
15772
+ ...e,
15773
+ { kind: "info", key: mkKey(), text: "Project context may be stale. Run /init to refresh KIMI.md based on recent changes." }
15774
+ ]);
15775
+ }
15776
+ }
15647
15777
  };
15648
15778
  try {
15649
15779
  await runAgentTurn({
@@ -16100,7 +16230,8 @@ ${lines.join("\n")}` }]);
16100
16230
  gatewayMeta,
16101
16231
  codeMode,
16102
16232
  cloudMode: cfg.cloudMode,
16103
- cloudBudget
16233
+ cloudBudget,
16234
+ kimiMdStale
16104
16235
  }
16105
16236
  ),
16106
16237
  activePicker?.kind === "file" && /* @__PURE__ */ jsx23(