kimiflare 0.88.2 → 0.88.4

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
@@ -1363,6 +1363,562 @@ var init_messages = __esm({
1363
1363
  }
1364
1364
  });
1365
1365
 
1366
+ // src/agent/session-state.ts
1367
+ function emptySessionState(task = "") {
1368
+ return {
1369
+ task,
1370
+ user_constraints: [],
1371
+ repo_facts: [],
1372
+ files_touched: [],
1373
+ files_modified: [],
1374
+ confirmed_findings: [],
1375
+ open_questions: [],
1376
+ recent_failures: [],
1377
+ decisions: [],
1378
+ next_actions: [],
1379
+ artifact_index: {}
1380
+ };
1381
+ }
1382
+ function serializeArtifactStore(store) {
1383
+ const MAX_ARTIFACT_CHARS = 5e4;
1384
+ const out = [];
1385
+ for (const a of store.list()) {
1386
+ out.push({
1387
+ id: a.id,
1388
+ type: a.type,
1389
+ summary: a.summary,
1390
+ raw: a.raw.slice(0, MAX_ARTIFACT_CHARS),
1391
+ source: a.source,
1392
+ path: a.path,
1393
+ lineRange: a.lineRange,
1394
+ ts: a.ts
1395
+ });
1396
+ }
1397
+ return out;
1398
+ }
1399
+ function deserializeArtifactStore(data) {
1400
+ const store = new ArtifactStore();
1401
+ for (const a of data) {
1402
+ store.add({
1403
+ id: a.id,
1404
+ type: a.type,
1405
+ summary: a.summary,
1406
+ raw: a.raw,
1407
+ source: a.source,
1408
+ path: a.path,
1409
+ lineRange: a.lineRange,
1410
+ ts: a.ts
1411
+ });
1412
+ }
1413
+ return store;
1414
+ }
1415
+ function formatRecalledArtifacts(recalled) {
1416
+ if (recalled.length === 0) return "";
1417
+ const lines = ["[recalled artifacts]"];
1418
+ for (const { id, artifact } of recalled) {
1419
+ lines.push(`--- artifact:${id} (${artifact.type} from ${artifact.source}) ---`);
1420
+ lines.push(artifact.raw);
1421
+ }
1422
+ return lines.join("\n");
1423
+ }
1424
+ function serializeSessionState(state) {
1425
+ const lines = [];
1426
+ lines.push(`task: ${state.task || "(none)"}`);
1427
+ if (state.user_constraints.length) lines.push(`constraints:
1428
+ ${state.user_constraints.map((c) => " - " + c).join("\n")}`);
1429
+ if (state.repo_facts.length) lines.push(`repo_facts:
1430
+ ${state.repo_facts.map((f) => " - " + f).join("\n")}`);
1431
+ if (state.files_touched.length) lines.push(`files_touched: ${state.files_touched.join(", ")}`);
1432
+ if (state.files_modified.length) lines.push(`files_modified: ${state.files_modified.join(", ")}`);
1433
+ if (state.confirmed_findings.length) lines.push(`findings:
1434
+ ${state.confirmed_findings.map((f) => " - " + f).join("\n")}`);
1435
+ if (state.open_questions.length) lines.push(`open_questions:
1436
+ ${state.open_questions.map((q) => " - " + q).join("\n")}`);
1437
+ if (state.recent_failures.length) lines.push(`recent_failures:
1438
+ ${state.recent_failures.map((f) => " - " + f).join("\n")}`);
1439
+ if (state.decisions.length) lines.push(`decisions:
1440
+ ${state.decisions.map((d) => " - " + d).join("\n")}`);
1441
+ if (state.next_actions.length) lines.push(`next_actions:
1442
+ ${state.next_actions.map((a) => " - " + a).join("\n")}`);
1443
+ if (Object.keys(state.artifact_index).length) {
1444
+ lines.push("artifact_index:");
1445
+ for (const [id, meta] of Object.entries(state.artifact_index)) {
1446
+ lines.push(` ${id}: [${meta.type}] ${meta.summary}`);
1447
+ }
1448
+ }
1449
+ return lines.join("\n");
1450
+ }
1451
+ function buildSessionStateMessage(state) {
1452
+ return {
1453
+ role: "system",
1454
+ content: `[compiled session state]
1455
+ ${serializeSessionState(state)}`
1456
+ };
1457
+ }
1458
+ var ArtifactStore;
1459
+ var init_session_state = __esm({
1460
+ "src/agent/session-state.ts"() {
1461
+ "use strict";
1462
+ ArtifactStore = class {
1463
+ artifacts = /* @__PURE__ */ new Map();
1464
+ maxArtifacts;
1465
+ maxTotalChars;
1466
+ constructor(opts2) {
1467
+ this.maxArtifacts = opts2?.maxArtifacts ?? 200;
1468
+ this.maxTotalChars = opts2?.maxTotalChars ?? 5e5;
1469
+ }
1470
+ add(a) {
1471
+ while (this.totalChars() + a.raw.length > this.maxTotalChars && this.artifacts.size > 0) {
1472
+ this.evictSizeWeighted();
1473
+ }
1474
+ while (this.artifacts.size >= this.maxArtifacts) {
1475
+ this.evictOldest();
1476
+ }
1477
+ this.artifacts.set(a.id, a);
1478
+ }
1479
+ get(id) {
1480
+ return this.artifacts.get(id);
1481
+ }
1482
+ has(id) {
1483
+ return this.artifacts.has(id);
1484
+ }
1485
+ list() {
1486
+ return [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
1487
+ }
1488
+ recall(ids) {
1489
+ const out = [];
1490
+ for (const id of ids) {
1491
+ const a = this.artifacts.get(id);
1492
+ if (a) out.push({ id, artifact: a });
1493
+ }
1494
+ return out;
1495
+ }
1496
+ size() {
1497
+ return this.artifacts.size;
1498
+ }
1499
+ totalChars() {
1500
+ let sum = 0;
1501
+ for (const a of this.artifacts.values()) {
1502
+ sum += a.raw.length;
1503
+ }
1504
+ return sum;
1505
+ }
1506
+ evictOldest() {
1507
+ let oldest;
1508
+ for (const a of this.artifacts.values()) {
1509
+ if (!oldest || a.ts < oldest.ts) oldest = a;
1510
+ }
1511
+ if (oldest) this.artifacts.delete(oldest.id);
1512
+ }
1513
+ /** Evict the largest artifact among the oldest quartile (by timestamp).
1514
+ * Bounded by the oldest quartile so we never evict freshly-added artifacts;
1515
+ * size-weighted within that window so one big artifact gets dropped instead
1516
+ * of many small ones. */
1517
+ evictSizeWeighted() {
1518
+ const sorted = [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
1519
+ if (sorted.length === 0) return;
1520
+ const quartile = Math.max(1, Math.ceil(sorted.length / 4));
1521
+ const candidates = sorted.slice(0, quartile);
1522
+ let pick3 = candidates[0];
1523
+ for (const a of candidates) {
1524
+ if (a.raw.length > pick3.raw.length) pick3 = a;
1525
+ }
1526
+ this.artifacts.delete(pick3.id);
1527
+ }
1528
+ };
1529
+ }
1530
+ });
1531
+
1532
+ // src/agent/artifact-compaction.ts
1533
+ function approxTokens(n) {
1534
+ return Math.round(n / 3.5);
1535
+ }
1536
+ function estimateMessageTokens(m) {
1537
+ let chars = 0;
1538
+ if (typeof m.content === "string") {
1539
+ chars = m.content.length;
1540
+ } else if (Array.isArray(m.content)) {
1541
+ chars = m.content.map((p) => p.type === "text" ? p.text.length : 0).reduce((a, b) => a + b, 0);
1542
+ }
1543
+ if (m.reasoning_content) chars += m.reasoning_content.length;
1544
+ if (m.tool_calls) {
1545
+ for (const tc of m.tool_calls) {
1546
+ chars += tc.function.name.length + tc.function.arguments.length;
1547
+ }
1548
+ }
1549
+ return approxTokens(chars);
1550
+ }
1551
+ function estimatePromptTokens(messages) {
1552
+ return messages.reduce((sum, m) => sum + estimateMessageTokens(m), 0);
1553
+ }
1554
+ function groupIntoTurns(messages) {
1555
+ const prefix = [];
1556
+ let i = 0;
1557
+ while (i < messages.length && messages[i].role === "system") {
1558
+ prefix.push(messages[i]);
1559
+ i++;
1560
+ }
1561
+ const turns = [];
1562
+ while (i < messages.length) {
1563
+ if (messages[i].role !== "user") {
1564
+ i++;
1565
+ continue;
1566
+ }
1567
+ const user = messages[i];
1568
+ i++;
1569
+ if (i >= messages.length || messages[i].role !== "assistant") {
1570
+ continue;
1571
+ }
1572
+ const assistant = messages[i];
1573
+ i++;
1574
+ const tools = [];
1575
+ while (i < messages.length && messages[i].role === "tool") {
1576
+ tools.push(messages[i]);
1577
+ i++;
1578
+ }
1579
+ turns.push({ user, assistant, tools });
1580
+ }
1581
+ return { prefix, turns };
1582
+ }
1583
+ function makeArtifactId(type, index) {
1584
+ return `${type}_${Date.now()}_${index}`;
1585
+ }
1586
+ function extractArtifactsFromTurn(turn, startIndex, store) {
1587
+ const artifacts = [];
1588
+ const stateDelta = {
1589
+ files_touched: [],
1590
+ files_modified: [],
1591
+ confirmed_findings: [],
1592
+ recent_failures: [],
1593
+ decisions: [],
1594
+ next_actions: []
1595
+ };
1596
+ const toolCalls = turn.assistant.tool_calls ?? [];
1597
+ for (let ti = 0; ti < turn.tools.length; ti++) {
1598
+ const tm = turn.tools[ti];
1599
+ const tc = toolCalls[ti];
1600
+ const name = tm.name ?? tc?.function.name ?? "unknown";
1601
+ const content = typeof tm.content === "string" ? tm.content : "";
1602
+ let type = "tool_result";
1603
+ let summary = `${name} result`;
1604
+ let path;
1605
+ if (name === "read") {
1606
+ type = "read_slice";
1607
+ try {
1608
+ const args = tc ? JSON.parse(tc.function.arguments) : {};
1609
+ path = args.path;
1610
+ summary = `read ${path ?? "file"}`;
1611
+ if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
1612
+ } catch {
1613
+ summary = "read file";
1614
+ }
1615
+ } else if (name === "bash") {
1616
+ type = "bash_log";
1617
+ try {
1618
+ const args = tc ? JSON.parse(tc.function.arguments) : {};
1619
+ const cmd = args.command ?? "";
1620
+ summary = `bash: ${cmd.slice(0, 60)}`;
1621
+ if (content.includes("Error") || content.includes("error") || content.includes("FAIL")) {
1622
+ stateDelta.recent_failures.push(`bash failed: ${cmd.slice(0, 80)}`);
1623
+ }
1624
+ } catch {
1625
+ summary = "bash command";
1626
+ }
1627
+ } else if (name === "grep") {
1628
+ type = "grep_result";
1629
+ summary = `grep results (${content.split("\n").length} lines)`;
1630
+ } else if (name === "web_fetch") {
1631
+ type = "web_fetch";
1632
+ try {
1633
+ const args = tc ? JSON.parse(tc.function.arguments) : {};
1634
+ summary = `web_fetch: ${args.url ?? "url"}`;
1635
+ } catch {
1636
+ summary = "web_fetch";
1637
+ }
1638
+ } else if (name === "write" || name === "edit") {
1639
+ try {
1640
+ const args = tc ? JSON.parse(tc.function.arguments) : {};
1641
+ path = args.path;
1642
+ if (path && !stateDelta.files_modified.includes(path)) stateDelta.files_modified.push(path);
1643
+ if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
1644
+ } catch {
1645
+ }
1646
+ continue;
1647
+ } else if (name === "glob") {
1648
+ try {
1649
+ const args = tc ? JSON.parse(tc.function.arguments) : {};
1650
+ summary = `glob: ${args.pattern ?? ""}`;
1651
+ } catch {
1652
+ summary = "glob";
1653
+ }
1654
+ } else if (name === "tasks_set") {
1655
+ try {
1656
+ const args = tc ? JSON.parse(tc.function.arguments) : {};
1657
+ const tasks = args.tasks ?? [];
1658
+ const inProgress = tasks.filter((t) => t.status === "in_progress").map((t) => t.title);
1659
+ const pending = tasks.filter((t) => t.status === "pending").map((t) => t.title);
1660
+ if (inProgress.length) stateDelta.next_actions.push(...inProgress);
1661
+ if (pending.length) stateDelta.next_actions.push(...pending);
1662
+ summary = `tasks_set: ${tasks.length} tasks`;
1663
+ } catch {
1664
+ summary = "tasks_set";
1665
+ }
1666
+ }
1667
+ const maxRaw = 5e4;
1668
+ const raw = content.length > maxRaw ? content.slice(0, maxRaw) + `
1669
+ ...[${content.length - maxRaw} chars truncated]` : content;
1670
+ const artifact = {
1671
+ id: makeArtifactId(type, startIndex + ti),
1672
+ type,
1673
+ summary,
1674
+ raw,
1675
+ source: name,
1676
+ path,
1677
+ ts: (/* @__PURE__ */ new Date()).toISOString()
1678
+ };
1679
+ artifacts.push(artifact);
1680
+ if (!content.includes("Error") && !content.includes("error") && content.length > 0 && content.length < 2e3) {
1681
+ stateDelta.confirmed_findings.push(`${name}: ${content.slice(0, 200)}`);
1682
+ }
1683
+ }
1684
+ const assistantText = typeof turn.assistant.content === "string" ? turn.assistant.content : "";
1685
+ if (assistantText.length > 0) {
1686
+ const decisionPatterns = [
1687
+ /(?:decided?|will|plan to|going to|should|need to)\s+(.{10,200})/gi,
1688
+ /(?:let's|let us)\s+(.{10,200})/gi
1689
+ ];
1690
+ for (const pattern of decisionPatterns) {
1691
+ let match;
1692
+ while ((match = pattern.exec(assistantText)) !== null) {
1693
+ const decision = match[1].trim().replace(/\.$/, "");
1694
+ if (decision.length > 10 && !stateDelta.decisions.includes(decision)) {
1695
+ stateDelta.decisions.push(decision);
1696
+ }
1697
+ }
1698
+ }
1699
+ }
1700
+ return { artifacts, stateDelta };
1701
+ }
1702
+ function mergeState(state, delta) {
1703
+ const mergeArr = (a, b) => {
1704
+ if (!b) return a;
1705
+ const set = new Set(a);
1706
+ for (const item of b) set.add(item);
1707
+ return [...set];
1708
+ };
1709
+ return {
1710
+ ...state,
1711
+ task: state.task || delta.task || "",
1712
+ user_constraints: mergeArr(state.user_constraints, delta.user_constraints),
1713
+ repo_facts: mergeArr(state.repo_facts, delta.repo_facts),
1714
+ files_touched: mergeArr(state.files_touched, delta.files_touched),
1715
+ files_modified: mergeArr(state.files_modified, delta.files_modified),
1716
+ confirmed_findings: mergeArr(state.confirmed_findings, delta.confirmed_findings),
1717
+ open_questions: mergeArr(state.open_questions, delta.open_questions),
1718
+ recent_failures: mergeArr(state.recent_failures, delta.recent_failures),
1719
+ decisions: mergeArr(state.decisions, delta.decisions),
1720
+ next_actions: mergeArr(state.next_actions, delta.next_actions),
1721
+ artifact_index: { ...state.artifact_index, ...delta.artifact_index }
1722
+ };
1723
+ }
1724
+ function shouldCompact(opts2) {
1725
+ const tokenThreshold = opts2.tokenThreshold ?? 8e4;
1726
+ const turnThreshold = opts2.turnThreshold ?? 12;
1727
+ const tokens = estimatePromptTokens(opts2.messages);
1728
+ const { turns } = groupIntoTurns(opts2.messages);
1729
+ return tokens > tokenThreshold || turns.length > turnThreshold;
1730
+ }
1731
+ function compactMessagesViaArtifacts(opts2) {
1732
+ const keepLastTurns = opts2.keepLastTurns ?? 4;
1733
+ const { prefix, turns } = groupIntoTurns(opts2.messages);
1734
+ const tokensBefore = estimatePromptTokens(opts2.messages);
1735
+ if (turns.length <= keepLastTurns) {
1736
+ return {
1737
+ newMessages: opts2.messages,
1738
+ newState: opts2.state,
1739
+ metrics: {
1740
+ estimatedTokensBefore: tokensBefore,
1741
+ estimatedTokensAfter: tokensBefore,
1742
+ archivedArtifacts: 0,
1743
+ recalledArtifacts: 0,
1744
+ rawTurnsRemoved: 0,
1745
+ rawTurnsKept: turns.length
1746
+ }
1747
+ };
1748
+ }
1749
+ const toCompact = turns.slice(0, turns.length - keepLastTurns);
1750
+ const toKeep = turns.slice(turns.length - keepLastTurns);
1751
+ let newState = { ...opts2.state };
1752
+ let archivedCount = 0;
1753
+ for (let i = 0; i < toCompact.length; i++) {
1754
+ const turn = toCompact[i];
1755
+ const { artifacts, stateDelta } = extractArtifactsFromTurn(turn, i, opts2.store);
1756
+ for (const artifact of artifacts) {
1757
+ opts2.store.add(artifact);
1758
+ archivedCount++;
1759
+ newState.artifact_index[artifact.id] = {
1760
+ type: artifact.type,
1761
+ summary: artifact.summary,
1762
+ source: artifact.source,
1763
+ path: artifact.path
1764
+ };
1765
+ }
1766
+ newState = mergeState(newState, stateDelta);
1767
+ if (!newState.task && typeof turn.user.content === "string") {
1768
+ newState.task = turn.user.content.slice(0, 200);
1769
+ }
1770
+ }
1771
+ const workingMemory = [];
1772
+ for (const turn of toKeep) {
1773
+ workingMemory.push(turn.user);
1774
+ workingMemory.push(turn.assistant);
1775
+ for (const tm of turn.tools) {
1776
+ workingMemory.push(tm);
1777
+ }
1778
+ }
1779
+ const stateMsg = buildSessionStateMessage(newState);
1780
+ const newMessages = [...prefix, stateMsg, ...workingMemory];
1781
+ const tokensAfter = estimatePromptTokens(newMessages);
1782
+ const metrics = {
1783
+ estimatedTokensBefore: tokensBefore,
1784
+ estimatedTokensAfter: tokensAfter,
1785
+ archivedArtifacts: archivedCount,
1786
+ recalledArtifacts: 0,
1787
+ rawTurnsRemoved: toCompact.length,
1788
+ rawTurnsKept: toKeep.length
1789
+ };
1790
+ return { newMessages, newState, metrics };
1791
+ }
1792
+ function recallArtifacts(messages, store, state) {
1793
+ const text = messages.map((m) => typeof m.content === "string" ? m.content : "").join(" ");
1794
+ const ids = [];
1795
+ for (const [id, meta] of Object.entries(state.artifact_index)) {
1796
+ if (meta.path && text.includes(meta.path)) {
1797
+ ids.push(id);
1798
+ }
1799
+ }
1800
+ for (const failure of state.recent_failures) {
1801
+ const keyword = failure.split(":")[0];
1802
+ if (!keyword) continue;
1803
+ const lowerKeyword = keyword.toLowerCase();
1804
+ if (!text.toLowerCase().includes(lowerKeyword)) continue;
1805
+ for (const [id, meta] of Object.entries(state.artifact_index)) {
1806
+ if (meta.source === "bash" && !ids.includes(id) && meta.summary.toLowerCase().includes(lowerKeyword)) {
1807
+ ids.push(id);
1808
+ }
1809
+ }
1810
+ }
1811
+ const uniqueIds = [...new Set(ids)].slice(0, 5);
1812
+ const recalled = store.recall(uniqueIds);
1813
+ return { ids: uniqueIds, recalled };
1814
+ }
1815
+ var init_artifact_compaction = __esm({
1816
+ "src/agent/artifact-compaction.ts"() {
1817
+ "use strict";
1818
+ init_session_state();
1819
+ }
1820
+ });
1821
+
1822
+ // src/util/llm-dump.ts
1823
+ import { homedir as homedir5 } from "os";
1824
+ import { join as join8 } from "path";
1825
+ import { mkdirSync as mkdirSync2, appendFileSync, writeFileSync } from "fs";
1826
+ function isLlmDumpEnabled() {
1827
+ const raw = process.env.KIMIFLARE_DUMP_LLM;
1828
+ if (raw === void 0) return false;
1829
+ const v = raw.toLowerCase();
1830
+ return v === "1" || v === "true";
1831
+ }
1832
+ function defaultDumpRoot() {
1833
+ const override = process.env.KIMIFLARE_DUMP_LLM_DIR;
1834
+ if (override && override.trim()) return override;
1835
+ const xdg = process.env.XDG_CONFIG_HOME || join8(homedir5(), ".config");
1836
+ return join8(xdg, "kimiflare", "llm-dumps");
1837
+ }
1838
+ function dumpDir(sessionId) {
1839
+ return join8(defaultDumpRoot(), sessionId && sessionId.trim() ? sessionId : "nosession");
1840
+ }
1841
+ function messageChars(m) {
1842
+ let chars = 0;
1843
+ if (typeof m.content === "string") {
1844
+ chars = m.content.length;
1845
+ } else if (Array.isArray(m.content)) {
1846
+ chars = m.content.reduce((a, p) => a + (p.type === "text" ? p.text.length : 0), 0);
1847
+ }
1848
+ if (m.reasoning_content) chars += m.reasoning_content.length;
1849
+ if (m.tool_calls) {
1850
+ for (const tc of m.tool_calls) {
1851
+ chars += tc.function.name.length + tc.function.arguments.length;
1852
+ }
1853
+ }
1854
+ return chars;
1855
+ }
1856
+ function computeBreakdown(messages, tools) {
1857
+ const perMessage = [];
1858
+ let systemChars = 0;
1859
+ let historyChars = 0;
1860
+ messages.forEach((m, index) => {
1861
+ const chars = messageChars(m);
1862
+ if (m.role === "system") systemChars += chars;
1863
+ else historyChars += chars;
1864
+ perMessage.push({
1865
+ index,
1866
+ role: m.role,
1867
+ ...m.name ? { name: m.name } : {},
1868
+ chars,
1869
+ estTokens: approxTokens(chars),
1870
+ ...m.tool_calls && m.tool_calls.length ? { toolName: m.tool_calls.map((t) => t.function.name).join(",") } : {}
1871
+ });
1872
+ });
1873
+ const toolsChars = tools.reduce(
1874
+ (a, t) => a + t.function.name.length + t.function.description.length + JSON.stringify(t.function.parameters).length,
1875
+ 0
1876
+ );
1877
+ const totalChars = systemChars + historyChars + toolsChars;
1878
+ return {
1879
+ totalChars,
1880
+ estTokens: approxTokens(totalChars),
1881
+ messageCount: messages.length,
1882
+ toolCount: tools.length,
1883
+ systemChars,
1884
+ toolsChars,
1885
+ historyChars,
1886
+ perMessage
1887
+ };
1888
+ }
1889
+ function safeSegment(s) {
1890
+ return s.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
1891
+ }
1892
+ function writeLlmDump(record) {
1893
+ if (!isLlmDumpEnabled()) return;
1894
+ try {
1895
+ const dir = dumpDir(record.meta.sessionId);
1896
+ mkdirSync2(dir, { recursive: true });
1897
+ const tsPart = safeSegment(record.meta.ts);
1898
+ const turnPart = safeSegment(record.meta.turnId ?? "noturn");
1899
+ const reqPart = safeSegment(record.meta.requestId);
1900
+ const file = join8(dir, `${tsPart}-${turnPart}-${reqPart}.json`);
1901
+ writeFileSync(file, JSON.stringify(record, null, 2));
1902
+ const summary = {
1903
+ ...record.meta,
1904
+ ...record.breakdown,
1905
+ perMessage: void 0,
1906
+ // keep the index line thin
1907
+ finishReason: record.response.finishReason,
1908
+ usage: record.response.usage,
1909
+ file
1910
+ };
1911
+ appendFileSync(join8(dir, "index.jsonl"), JSON.stringify(summary) + "\n");
1912
+ } catch {
1913
+ }
1914
+ }
1915
+ var init_llm_dump = __esm({
1916
+ "src/util/llm-dump.ts"() {
1917
+ "use strict";
1918
+ init_artifact_compaction();
1919
+ }
1920
+ });
1921
+
1366
1922
  // src/models/registry.ts
1367
1923
  function isUnifiedEligible(entry) {
1368
1924
  if (entry.provider === "workers-ai") return false;
@@ -1485,6 +2041,38 @@ async function* runKimi(opts2) {
1485
2041
  if (opts2.reasoningEffort && supportsReasoning) {
1486
2042
  body.reasoning_effort = opts2.reasoningEffort;
1487
2043
  }
2044
+ let dumpRecord = null;
2045
+ if (isLlmDumpEnabled()) {
2046
+ const dumpMessages = body.messages;
2047
+ const dumpTools = body.tools ?? [];
2048
+ const { messages: _m, tools: _t, ...params } = body;
2049
+ const dumpResponse = {
2050
+ text: "",
2051
+ reasoning: "",
2052
+ toolCalls: [],
2053
+ finishReason: null,
2054
+ usage: null
2055
+ };
2056
+ dumpRecord = {
2057
+ meta: {
2058
+ requestId,
2059
+ sessionId: opts2.sessionId ?? getLogSessionId(),
2060
+ turnId: getLogTurnId(),
2061
+ model: opts2.model,
2062
+ url,
2063
+ ts: (/* @__PURE__ */ new Date()).toISOString()
2064
+ },
2065
+ request: {
2066
+ system: dumpMessages.filter((m) => m.role === "system"),
2067
+ messages: dumpMessages,
2068
+ tools: dumpTools,
2069
+ params,
2070
+ rawSerialized: stableStringify(body, jsonReplacer)
2071
+ },
2072
+ breakdown: computeBreakdown(dumpMessages, dumpTools),
2073
+ response: dumpResponse
2074
+ };
2075
+ }
1488
2076
  logger.debug("runKimi:request", { requestId, attempt: 0, model: opts2.model });
1489
2077
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
1490
2078
  let res;
@@ -1565,13 +2153,41 @@ async function* runKimi(opts2) {
1565
2153
  const meta = readGatewayMeta(res.headers);
1566
2154
  if (meta) yield { type: "gateway_meta", meta };
1567
2155
  logger.debug("runKimi:stream_start", { requestId });
1568
- for await (const ev of parseStream(res.body, opts2.signal, opts2.idleTimeoutMs, opts2.postFirstByteIdleTimeoutMs)) {
1569
- yield ev;
2156
+ try {
2157
+ for await (const ev of parseStream(res.body, opts2.signal, opts2.idleTimeoutMs, opts2.postFirstByteIdleTimeoutMs)) {
2158
+ if (dumpRecord) accumulateDumpResponse(dumpRecord.response, ev);
2159
+ yield ev;
2160
+ }
2161
+ } finally {
2162
+ if (dumpRecord) {
2163
+ dumpRecord.meta.attempt = attempt;
2164
+ writeLlmDump(dumpRecord);
2165
+ }
1570
2166
  }
1571
2167
  logger.debug("runKimi:stream_end", { requestId });
1572
2168
  return;
1573
2169
  }
1574
2170
  }
2171
+ function accumulateDumpResponse(resp, ev) {
2172
+ switch (ev.type) {
2173
+ case "text":
2174
+ resp.text += ev.delta;
2175
+ break;
2176
+ case "reasoning":
2177
+ resp.reasoning += ev.delta;
2178
+ break;
2179
+ case "tool_call_complete":
2180
+ resp.toolCalls.push({ name: ev.name, arguments: ev.arguments });
2181
+ break;
2182
+ case "usage":
2183
+ resp.usage = ev.usage;
2184
+ break;
2185
+ case "done":
2186
+ resp.finishReason = ev.finishReason;
2187
+ if (ev.usage) resp.usage = ev.usage;
2188
+ break;
2189
+ }
2190
+ }
1575
2191
  function validateModelId(model) {
1576
2192
  if (!model) throw new KimiApiError(`Invalid model ID: ${model}`, 400);
1577
2193
  if (/^@[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+(\/[a-zA-Z0-9._-]+)*$/.test(model)) return;
@@ -1823,6 +2439,8 @@ var init_client = __esm({
1823
2439
  init_version();
1824
2440
  init_messages();
1825
2441
  init_logger();
2442
+ init_log_sink();
2443
+ init_llm_dump();
1826
2444
  init_registry();
1827
2445
  RETRYABLE_CODES = /* @__PURE__ */ new Set([3040]);
1828
2446
  MAX_ATTEMPTS = 5;
@@ -1869,20 +2487,45 @@ var init_registry2 = __esm({
1869
2487
  }
1870
2488
  });
1871
2489
 
1872
- // src/storage-limits.ts
1873
- import { readdir as readdir2, stat as stat2, unlink } from "fs/promises";
1874
- import { join as join8 } from "path";
1875
- async function listFilesByMtime(dir, pattern = /.*/) {
1876
- let entries;
1877
- try {
1878
- entries = await readdir2(dir);
2490
+ // src/memory/recall-inject.ts
2491
+ function hasRecalledMemory(messages) {
2492
+ return messages.some(
2493
+ (m) => m.role === "system" && typeof m.content === "string" && m.content.startsWith(RECALLED_MEMORY_HEADER)
2494
+ );
2495
+ }
2496
+ function injectRecalledMemoryOnce(messages, text) {
2497
+ if (!text || hasRecalledMemory(messages)) return false;
2498
+ const lastSystemIdx = messages.findLastIndex((m) => m.role === "system");
2499
+ const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messages.length;
2500
+ messages.splice(insertIdx, 0, {
2501
+ role: "system",
2502
+ content: `${RECALLED_MEMORY_HEADER}
2503
+ ${text}`
2504
+ });
2505
+ return true;
2506
+ }
2507
+ var RECALLED_MEMORY_HEADER;
2508
+ var init_recall_inject = __esm({
2509
+ "src/memory/recall-inject.ts"() {
2510
+ "use strict";
2511
+ RECALLED_MEMORY_HEADER = "[recalled project memory]";
2512
+ }
2513
+ });
2514
+
2515
+ // src/storage-limits.ts
2516
+ import { readdir as readdir2, stat as stat2, unlink } from "fs/promises";
2517
+ import { join as join9 } from "path";
2518
+ async function listFilesByMtime(dir, pattern = /.*/) {
2519
+ let entries;
2520
+ try {
2521
+ entries = await readdir2(dir);
1879
2522
  } catch {
1880
2523
  return [];
1881
2524
  }
1882
2525
  const files = [];
1883
2526
  for (const name of entries) {
1884
2527
  if (!pattern.test(name)) continue;
1885
- const p = join8(dir, name);
2528
+ const p = join9(dir, name);
1886
2529
  try {
1887
2530
  const s = await stat2(p);
1888
2531
  if (s.isFile()) files.push({ path: p, mtime: s.mtime });
@@ -1966,19 +2609,19 @@ var init_storage_limits = __esm({
1966
2609
 
1967
2610
  // src/cost-debug.ts
1968
2611
  import { appendFile, mkdir as mkdir5 } from "fs/promises";
1969
- import { homedir as homedir5 } from "os";
1970
- import { join as join9 } from "path";
2612
+ import { homedir as homedir6 } from "os";
2613
+ import { join as join10 } from "path";
1971
2614
  function debugDir() {
1972
- const xdg = process.env.XDG_DATA_HOME || join9(homedir5(), ".local", "share");
1973
- return join9(xdg, "kimiflare");
2615
+ const xdg = process.env.XDG_DATA_HOME || join10(homedir6(), ".local", "share");
2616
+ return join10(xdg, "kimiflare");
1974
2617
  }
1975
2618
  function debugPath() {
1976
- return join9(debugDir(), "cost-debug.jsonl");
2619
+ return join10(debugDir(), "cost-debug.jsonl");
1977
2620
  }
1978
2621
  function now() {
1979
2622
  return (/* @__PURE__ */ new Date()).toISOString();
1980
2623
  }
1981
- function approxTokens(chars) {
2624
+ function approxTokens2(chars) {
1982
2625
  return Math.round(chars / 4);
1983
2626
  }
1984
2627
  function analyzePrompt(messages) {
@@ -1994,14 +2637,14 @@ function analyzePrompt(messages) {
1994
2637
  const base = {
1995
2638
  role: m.role,
1996
2639
  chars,
1997
- approxTokens: approxTokens(chars)
2640
+ approxTokens: approxTokens2(chars)
1998
2641
  };
1999
2642
  if (m.role === "assistant" && m.reasoning_content) {
2000
2643
  sections.push({
2001
2644
  ...base,
2002
- detail: `content+reasoning (${approxTokens(m.reasoning_content.length)} reasoning tokens)`,
2645
+ detail: `content+reasoning (${approxTokens2(m.reasoning_content.length)} reasoning tokens)`,
2003
2646
  chars: chars + m.reasoning_content.length,
2004
- approxTokens: approxTokens(chars + m.reasoning_content.length)
2647
+ approxTokens: approxTokens2(chars + m.reasoning_content.length)
2005
2648
  });
2006
2649
  } else if (m.role === "tool") {
2007
2650
  sections.push({
@@ -2110,7 +2753,7 @@ async function logTurnDebug(ctx) {
2110
2753
  usage: ctx.usage,
2111
2754
  promptSections,
2112
2755
  promptTotalChars,
2113
- promptTotalApproxTokens: approxTokens(promptTotalChars),
2756
+ promptTotalApproxTokens: approxTokens2(promptTotalChars),
2114
2757
  toolStats,
2115
2758
  toolTotalRawBytes: toolTotalRaw,
2116
2759
  toolTotalReducedBytes: toolTotalReduced,
@@ -2634,757 +3277,301 @@ var require_node_gyp_build = __commonJS({
2634
3277
  function compareTags(runtime2) {
2635
3278
  return function(a, b) {
2636
3279
  if (a.runtime !== b.runtime) {
2637
- return a.runtime === runtime2 ? -1 : 1;
2638
- } else if (a.abi !== b.abi) {
2639
- return a.abi ? -1 : 1;
2640
- } else if (a.specificity !== b.specificity) {
2641
- return a.specificity > b.specificity ? -1 : 1;
2642
- } else {
2643
- return 0;
2644
- }
2645
- };
2646
- }
2647
- function isNwjs() {
2648
- return !!(process.versions && process.versions.nw);
2649
- }
2650
- function isElectron() {
2651
- if (process.versions && process.versions.electron) return true;
2652
- if (process.env.ELECTRON_RUN_AS_NODE) return true;
2653
- return typeof window !== "undefined" && window.process && window.process.type === "renderer";
2654
- }
2655
- function isAlpine(platform9) {
2656
- return platform9 === "linux" && fs.existsSync("/etc/alpine-release");
2657
- }
2658
- load.parseTags = parseTags;
2659
- load.matchTags = matchTags;
2660
- load.compareTags = compareTags;
2661
- load.parseTuple = parseTuple;
2662
- load.matchTuple = matchTuple;
2663
- load.compareTuples = compareTuples;
2664
- }
2665
- });
2666
-
2667
- // node_modules/node-gyp-build/index.js
2668
- var require_node_gyp_build2 = __commonJS({
2669
- "node_modules/node-gyp-build/index.js"(exports, module) {
2670
- "use strict";
2671
- var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
2672
- if (typeof runtimeRequire.addon === "function") {
2673
- module.exports = runtimeRequire.addon.bind(runtimeRequire);
2674
- } else {
2675
- module.exports = require_node_gyp_build();
2676
- }
2677
- }
2678
- });
2679
-
2680
- // node_modules/isolated-vm/isolated-vm.js
2681
- var require_isolated_vm = __commonJS({
2682
- "node_modules/isolated-vm/isolated-vm.js"(exports, module) {
2683
- "use strict";
2684
- module.exports = require_node_gyp_build2()(__dirname).ivm;
2685
- }
2686
- });
2687
-
2688
- // src/code-mode/sandbox.ts
2689
- import { join as join10, dirname as dirname5 } from "path";
2690
- import { pathToFileURL } from "url";
2691
- function stripTypescript(code) {
2692
- let js = code;
2693
- js = js.replace(/interface\s+\w+\s*\{[\s\S]*?\n\}/g, "");
2694
- js = js.replace(/type\s+\w+\s*=\s*[^;]+;/g, "");
2695
- js = js.replace(/(\w+)<[^>]+>(\s*\()/g, "$1$2");
2696
- js = js.replace(/(\b(?:const|let|var)\s+\w+)\s*:\s*[^=;]+/g, "$1");
2697
- js = js.replace(/(\(|,\s*)(\w+)\s*:\s*[^,)=]+/g, "$1$2");
2698
- js = js.replace(/(\)[\s]*)\s*:\s*[^{]+(\s*\{)/g, "$1$2");
2699
- js = js.replace(/(\)[\s]*)\s*:\s*Promise<[^>]+>(\s*\{)/g, "$1$2");
2700
- js = js.replace(/\s+as\s+\w+(?:\[\])?/g, "");
2701
- js = js.replace(/(\w+)(\??)!/g, "$1$2");
2702
- js = js.replace(/import\s+type\s+[^;]+;/g, "");
2703
- js = js.replace(/declare\s+[^;]+;/g, "");
2704
- js = js.replace(/\n{3,}/g, "\n\n");
2705
- return js.trim();
2706
- }
2707
- async function loadTypescript(cwd) {
2708
- try {
2709
- const tsPath = await import.meta.resolve("typescript");
2710
- return await import(tsPath);
2711
- } catch {
2712
- }
2713
- let dir = cwd;
2714
- while (dir !== dirname5(dir)) {
2715
- try {
2716
- const tsPath = join10(dir, "node_modules", "typescript", "lib", "typescript.js");
2717
- return await import(pathToFileURL(tsPath).href);
2718
- } catch {
2719
- }
2720
- dir = dirname5(dir);
2721
- }
2722
- return null;
2723
- }
2724
- async function transpileOrStrip(code, cwd) {
2725
- const ts = await loadTypescript(cwd);
2726
- if (ts) {
2727
- const result = ts.transpileModule(code, {
2728
- compilerOptions: {
2729
- module: ts.ModuleKind.ES2022,
2730
- target: ts.ScriptTarget.ES2022,
2731
- esModuleInterop: true,
2732
- isolatedModules: true
2733
- }
2734
- });
2735
- return { js: result.outputText, warnings: [] };
2736
- }
2737
- return {
2738
- js: stripTypescript(code),
2739
- warnings: ["TypeScript not found in node_modules. Using fallback parser; install typescript for reliable transpilation."]
2740
- };
2741
- }
2742
- async function runWithIsolatedVm(opts2) {
2743
- const { Isolate } = await Promise.resolve().then(() => __toESM(require_isolated_vm(), 1));
2744
- const isolate = new Isolate({ memoryLimit: opts2.memoryLimitMB ?? 128 });
2745
- const context = await isolate.createContext();
2746
- const jail = context.global;
2747
- await jail.set("global", jail.derefInto());
2748
- const logs = [];
2749
- const toolCalls = [];
2750
- await context.evalClosure(
2751
- `globalThis._log = function(...args) {
2752
- $0.applySync(undefined, [args.map(String).join(" ")], { arguments: { copy: true } });
2753
- };`,
2754
- [(msg) => logs.push(msg)],
2755
- { arguments: { reference: true } }
2756
- );
2757
- await context.eval(`var console = { log: function(...args) { _log(args.map(String).join(" ")); } };`);
2758
- const toolMap = new Map(opts2.tools.map((t) => [t.name, t]));
2759
- for (const tool of opts2.tools) {
2760
- const ref = new (await Promise.resolve().then(() => __toESM(require_isolated_vm(), 1))).Reference(
2761
- async (argsJson) => {
2762
- const args = JSON.parse(argsJson);
2763
- const toolCallId = `code_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2764
- const result = await opts2.executor.run(
2765
- { id: toolCallId, name: tool.name, arguments: JSON.stringify(args) },
2766
- opts2.askPermission,
2767
- opts2.ctx
2768
- );
2769
- toolCalls.push({
2770
- name: tool.name,
2771
- args,
2772
- result: result.content
2773
- });
2774
- return result.content;
2775
- }
2776
- );
2777
- await context.evalClosure(
2778
- `globalThis["_api_${tool.name}"] = function(argsJson) {
2779
- return $0.applySyncPromise(undefined, [argsJson], { arguments: { copy: true } });
2780
- };`,
2781
- [ref],
2782
- { arguments: { reference: true } }
2783
- );
2784
- }
2785
- const apiMethods = opts2.tools.map((t) => ` ${t.name}: function(input) { return _api_${t.name}(JSON.stringify(input ?? {})); }`).join(",\n");
2786
- await context.eval(`var api = {
2787
- ${apiMethods}
2788
- };`);
2789
- const { js: jsCode, warnings } = await transpileOrStrip(opts2.code, opts2.ctx.cwd);
2790
- const wrapped = `(async function() {
2791
- ${jsCode}
2792
- })();`;
2793
- try {
2794
- const timeout = opts2.timeoutMs ?? 3e4;
2795
- const script = await isolate.compileScript(wrapped);
2796
- await script.run(context, { timeout, release: true });
2797
- await new Promise((r) => setTimeout(r, 10));
2798
- } catch (err) {
2799
- const message2 = err instanceof Error ? err.message : String(err);
2800
- return { output: "", logs, error: message2, toolCalls, warnings };
2801
- } finally {
2802
- isolate.dispose();
2803
- }
2804
- return { output: logs.join("\n"), logs, toolCalls, warnings };
2805
- }
2806
- async function runWithNodeVm(opts2) {
2807
- const { runInNewContext } = await import("vm");
2808
- const logs = [];
2809
- const toolCalls = [];
2810
- const sandbox = {
2811
- console: {
2812
- log: (...args) => {
2813
- logs.push(args.map(String).join(" "));
2814
- }
2815
- },
2816
- api: {},
2817
- setTimeout,
2818
- clearTimeout,
2819
- setInterval,
2820
- clearInterval,
2821
- Promise,
2822
- JSON,
2823
- Math,
2824
- Date,
2825
- Array,
2826
- Object,
2827
- String,
2828
- Number,
2829
- Boolean,
2830
- RegExp,
2831
- Error,
2832
- TypeError,
2833
- RangeError,
2834
- SyntaxError,
2835
- ReferenceError,
2836
- Map,
2837
- Set,
2838
- WeakMap,
2839
- WeakSet,
2840
- Symbol,
2841
- parseInt,
2842
- parseFloat,
2843
- isNaN,
2844
- isFinite,
2845
- encodeURI,
2846
- decodeURI,
2847
- encodeURIComponent,
2848
- decodeURIComponent,
2849
- escape,
2850
- unescape,
2851
- Infinity: Infinity,
2852
- NaN: NaN,
2853
- undefined: void 0
2854
- };
2855
- for (const tool of opts2.tools) {
2856
- sandbox.api[tool.name] = async (input) => {
2857
- const args = input ?? {};
2858
- const toolCallId = `code_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2859
- const result = await opts2.executor.run(
2860
- { id: toolCallId, name: tool.name, arguments: JSON.stringify(args) },
2861
- opts2.askPermission,
2862
- opts2.ctx
2863
- );
2864
- toolCalls.push({
2865
- name: tool.name,
2866
- args,
2867
- result: result.content
2868
- });
2869
- return result.content;
2870
- };
2871
- }
2872
- const { js: jsCode, warnings } = await transpileOrStrip(opts2.code, opts2.ctx.cwd);
2873
- const wrapped = `"use strict";
2874
- (async function() {
2875
- ${jsCode}
2876
- })();`;
2877
- try {
2878
- const timeout = opts2.timeoutMs ?? 3e4;
2879
- await runInNewContext(wrapped, sandbox, { timeout, displayErrors: true });
2880
- await new Promise((r) => setTimeout(r, 10));
2881
- } catch (err) {
2882
- const message2 = err instanceof Error ? err.message : String(err);
2883
- return { output: "", logs, error: message2, toolCalls, warnings };
2884
- }
2885
- return { output: logs.join("\n"), logs, toolCalls, warnings };
2886
- }
2887
- function buildFallbackWarning(errMessage) {
2888
- let reason;
2889
- let fix;
2890
- if (errMessage.includes("Cannot find module") || errMessage.includes("isolated-vm")) {
2891
- reason = "The optional dependency `isolated-vm` is not installed.";
2892
- fix = "Run `npm install isolated-vm` in your project (or `npm install -g isolated-vm` if KimiFlare is installed globally).";
2893
- } else if (errMessage.includes("bindings") || errMessage.includes(".node")) {
2894
- reason = "The `isolated-vm` native bindings are incompatible with this Node version or architecture.";
2895
- fix = "Try `npm rebuild isolated-vm`, or switch to Node 22 LTS if you're on Apple Silicon.";
2896
- } else {
2897
- reason = "The secure sandbox (`isolated-vm`) could not be loaded.";
2898
- fix = "Ensure build tools are installed, then run `npm install isolated-vm`.";
2899
- }
2900
- return `Code Mode is using the built-in Node.js sandbox. Tool execution works normally, but without memory limits or full process isolation. ${reason} ${fix}`;
2901
- }
2902
- async function runInSandbox(opts2) {
2903
- try {
2904
- return await runWithIsolatedVm(opts2);
2905
- } catch (err) {
2906
- const message2 = err instanceof Error ? err.message : String(err);
2907
- const result = await runWithNodeVm(opts2);
2908
- const sessionKey = opts2.ctx.sessionId ?? NO_SESSION_KEY;
2909
- if (!fallbackWarningShownSessions.has(sessionKey)) {
2910
- fallbackWarningShownSessions.add(sessionKey);
2911
- const warning = buildFallbackWarning(message2);
2912
- return { ...result, warnings: [warning, ...result.warnings ?? []] };
2913
- }
2914
- return result;
2915
- }
2916
- }
2917
- var fallbackWarningShownSessions, NO_SESSION_KEY;
2918
- var init_sandbox = __esm({
2919
- "src/code-mode/sandbox.ts"() {
2920
- "use strict";
2921
- fallbackWarningShownSessions = /* @__PURE__ */ new Set();
2922
- NO_SESSION_KEY = "__no_session__";
2923
- }
2924
- });
2925
-
2926
- // src/code-mode/index.ts
2927
- var init_code_mode = __esm({
2928
- "src/code-mode/index.ts"() {
2929
- "use strict";
2930
- init_api_generator();
2931
- init_sandbox();
2932
- }
2933
- });
2934
-
2935
- // src/agent/session-state.ts
2936
- function emptySessionState(task = "") {
2937
- return {
2938
- task,
2939
- user_constraints: [],
2940
- repo_facts: [],
2941
- files_touched: [],
2942
- files_modified: [],
2943
- confirmed_findings: [],
2944
- open_questions: [],
2945
- recent_failures: [],
2946
- decisions: [],
2947
- next_actions: [],
2948
- artifact_index: {}
2949
- };
2950
- }
2951
- function serializeArtifactStore(store) {
2952
- const MAX_ARTIFACT_CHARS = 5e4;
2953
- const out = [];
2954
- for (const a of store.list()) {
2955
- out.push({
2956
- id: a.id,
2957
- type: a.type,
2958
- summary: a.summary,
2959
- raw: a.raw.slice(0, MAX_ARTIFACT_CHARS),
2960
- source: a.source,
2961
- path: a.path,
2962
- lineRange: a.lineRange,
2963
- ts: a.ts
2964
- });
2965
- }
2966
- return out;
2967
- }
2968
- function deserializeArtifactStore(data) {
2969
- const store = new ArtifactStore();
2970
- for (const a of data) {
2971
- store.add({
2972
- id: a.id,
2973
- type: a.type,
2974
- summary: a.summary,
2975
- raw: a.raw,
2976
- source: a.source,
2977
- path: a.path,
2978
- lineRange: a.lineRange,
2979
- ts: a.ts
2980
- });
2981
- }
2982
- return store;
2983
- }
2984
- function formatRecalledArtifacts(recalled) {
2985
- if (recalled.length === 0) return "";
2986
- const lines = ["[recalled artifacts]"];
2987
- for (const { id, artifact } of recalled) {
2988
- lines.push(`--- artifact:${id} (${artifact.type} from ${artifact.source}) ---`);
2989
- lines.push(artifact.raw);
2990
- }
2991
- return lines.join("\n");
2992
- }
2993
- function serializeSessionState(state) {
2994
- const lines = [];
2995
- lines.push(`task: ${state.task || "(none)"}`);
2996
- if (state.user_constraints.length) lines.push(`constraints:
2997
- ${state.user_constraints.map((c) => " - " + c).join("\n")}`);
2998
- if (state.repo_facts.length) lines.push(`repo_facts:
2999
- ${state.repo_facts.map((f) => " - " + f).join("\n")}`);
3000
- if (state.files_touched.length) lines.push(`files_touched: ${state.files_touched.join(", ")}`);
3001
- if (state.files_modified.length) lines.push(`files_modified: ${state.files_modified.join(", ")}`);
3002
- if (state.confirmed_findings.length) lines.push(`findings:
3003
- ${state.confirmed_findings.map((f) => " - " + f).join("\n")}`);
3004
- if (state.open_questions.length) lines.push(`open_questions:
3005
- ${state.open_questions.map((q) => " - " + q).join("\n")}`);
3006
- if (state.recent_failures.length) lines.push(`recent_failures:
3007
- ${state.recent_failures.map((f) => " - " + f).join("\n")}`);
3008
- if (state.decisions.length) lines.push(`decisions:
3009
- ${state.decisions.map((d) => " - " + d).join("\n")}`);
3010
- if (state.next_actions.length) lines.push(`next_actions:
3011
- ${state.next_actions.map((a) => " - " + a).join("\n")}`);
3012
- if (Object.keys(state.artifact_index).length) {
3013
- lines.push("artifact_index:");
3014
- for (const [id, meta] of Object.entries(state.artifact_index)) {
3015
- lines.push(` ${id}: [${meta.type}] ${meta.summary}`);
3016
- }
3017
- }
3018
- return lines.join("\n");
3019
- }
3020
- function buildSessionStateMessage(state) {
3021
- return {
3022
- role: "system",
3023
- content: `[compiled session state]
3024
- ${serializeSessionState(state)}`
3025
- };
3026
- }
3027
- var ArtifactStore;
3028
- var init_session_state = __esm({
3029
- "src/agent/session-state.ts"() {
3030
- "use strict";
3031
- ArtifactStore = class {
3032
- artifacts = /* @__PURE__ */ new Map();
3033
- maxArtifacts;
3034
- maxTotalChars;
3035
- constructor(opts2) {
3036
- this.maxArtifacts = opts2?.maxArtifacts ?? 200;
3037
- this.maxTotalChars = opts2?.maxTotalChars ?? 5e5;
3038
- }
3039
- add(a) {
3040
- while (this.totalChars() + a.raw.length > this.maxTotalChars && this.artifacts.size > 0) {
3041
- this.evictSizeWeighted();
3042
- }
3043
- while (this.artifacts.size >= this.maxArtifacts) {
3044
- this.evictOldest();
3045
- }
3046
- this.artifacts.set(a.id, a);
3047
- }
3048
- get(id) {
3049
- return this.artifacts.get(id);
3050
- }
3051
- has(id) {
3052
- return this.artifacts.has(id);
3053
- }
3054
- list() {
3055
- return [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
3056
- }
3057
- recall(ids) {
3058
- const out = [];
3059
- for (const id of ids) {
3060
- const a = this.artifacts.get(id);
3061
- if (a) out.push({ id, artifact: a });
3062
- }
3063
- return out;
3064
- }
3065
- size() {
3066
- return this.artifacts.size;
3067
- }
3068
- totalChars() {
3069
- let sum = 0;
3070
- for (const a of this.artifacts.values()) {
3071
- sum += a.raw.length;
3072
- }
3073
- return sum;
3074
- }
3075
- evictOldest() {
3076
- let oldest;
3077
- for (const a of this.artifacts.values()) {
3078
- if (!oldest || a.ts < oldest.ts) oldest = a;
3079
- }
3080
- if (oldest) this.artifacts.delete(oldest.id);
3081
- }
3082
- /** Evict the largest artifact among the oldest quartile (by timestamp).
3083
- * Bounded by the oldest quartile so we never evict freshly-added artifacts;
3084
- * size-weighted within that window so one big artifact gets dropped instead
3085
- * of many small ones. */
3086
- evictSizeWeighted() {
3087
- const sorted = [...this.artifacts.values()].sort((a, b) => a.ts < b.ts ? -1 : 1);
3088
- if (sorted.length === 0) return;
3089
- const quartile = Math.max(1, Math.ceil(sorted.length / 4));
3090
- const candidates = sorted.slice(0, quartile);
3091
- let pick3 = candidates[0];
3092
- for (const a of candidates) {
3093
- if (a.raw.length > pick3.raw.length) pick3 = a;
3094
- }
3095
- this.artifacts.delete(pick3.id);
3096
- }
3097
- };
3098
- }
3099
- });
3100
-
3101
- // src/agent/artifact-compaction.ts
3102
- function approxTokens2(n) {
3103
- return Math.round(n / 3.5);
3104
- }
3105
- function estimateMessageTokens(m) {
3106
- let chars = 0;
3107
- if (typeof m.content === "string") {
3108
- chars = m.content.length;
3109
- } else if (Array.isArray(m.content)) {
3110
- chars = m.content.map((p) => p.type === "text" ? p.text.length : 0).reduce((a, b) => a + b, 0);
3111
- }
3112
- if (m.reasoning_content) chars += m.reasoning_content.length;
3113
- if (m.tool_calls) {
3114
- for (const tc of m.tool_calls) {
3115
- chars += tc.function.name.length + tc.function.arguments.length;
3116
- }
3117
- }
3118
- return approxTokens2(chars);
3119
- }
3120
- function estimatePromptTokens(messages) {
3121
- return messages.reduce((sum, m) => sum + estimateMessageTokens(m), 0);
3122
- }
3123
- function groupIntoTurns(messages) {
3124
- const prefix = [];
3125
- let i = 0;
3126
- while (i < messages.length && messages[i].role === "system") {
3127
- prefix.push(messages[i]);
3128
- i++;
3129
- }
3130
- const turns = [];
3131
- while (i < messages.length) {
3132
- if (messages[i].role !== "user") {
3133
- i++;
3134
- continue;
3135
- }
3136
- const user = messages[i];
3137
- i++;
3138
- if (i >= messages.length || messages[i].role !== "assistant") {
3139
- continue;
3140
- }
3141
- const assistant = messages[i];
3142
- i++;
3143
- const tools = [];
3144
- while (i < messages.length && messages[i].role === "tool") {
3145
- tools.push(messages[i]);
3146
- i++;
3147
- }
3148
- turns.push({ user, assistant, tools });
3149
- }
3150
- return { prefix, turns };
3151
- }
3152
- function makeArtifactId(type, index) {
3153
- return `${type}_${Date.now()}_${index}`;
3154
- }
3155
- function extractArtifactsFromTurn(turn, startIndex, store) {
3156
- const artifacts = [];
3157
- const stateDelta = {
3158
- files_touched: [],
3159
- files_modified: [],
3160
- confirmed_findings: [],
3161
- recent_failures: [],
3162
- decisions: [],
3163
- next_actions: []
3164
- };
3165
- const toolCalls = turn.assistant.tool_calls ?? [];
3166
- for (let ti = 0; ti < turn.tools.length; ti++) {
3167
- const tm = turn.tools[ti];
3168
- const tc = toolCalls[ti];
3169
- const name = tm.name ?? tc?.function.name ?? "unknown";
3170
- const content = typeof tm.content === "string" ? tm.content : "";
3171
- let type = "tool_result";
3172
- let summary = `${name} result`;
3173
- let path;
3174
- if (name === "read") {
3175
- type = "read_slice";
3176
- try {
3177
- const args = tc ? JSON.parse(tc.function.arguments) : {};
3178
- path = args.path;
3179
- summary = `read ${path ?? "file"}`;
3180
- if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
3181
- } catch {
3182
- summary = "read file";
3183
- }
3184
- } else if (name === "bash") {
3185
- type = "bash_log";
3186
- try {
3187
- const args = tc ? JSON.parse(tc.function.arguments) : {};
3188
- const cmd = args.command ?? "";
3189
- summary = `bash: ${cmd.slice(0, 60)}`;
3190
- if (content.includes("Error") || content.includes("error") || content.includes("FAIL")) {
3191
- stateDelta.recent_failures.push(`bash failed: ${cmd.slice(0, 80)}`);
3192
- }
3193
- } catch {
3194
- summary = "bash command";
3195
- }
3196
- } else if (name === "grep") {
3197
- type = "grep_result";
3198
- summary = `grep results (${content.split("\n").length} lines)`;
3199
- } else if (name === "web_fetch") {
3200
- type = "web_fetch";
3201
- try {
3202
- const args = tc ? JSON.parse(tc.function.arguments) : {};
3203
- summary = `web_fetch: ${args.url ?? "url"}`;
3204
- } catch {
3205
- summary = "web_fetch";
3206
- }
3207
- } else if (name === "write" || name === "edit") {
3208
- try {
3209
- const args = tc ? JSON.parse(tc.function.arguments) : {};
3210
- path = args.path;
3211
- if (path && !stateDelta.files_modified.includes(path)) stateDelta.files_modified.push(path);
3212
- if (path && !stateDelta.files_touched.includes(path)) stateDelta.files_touched.push(path);
3213
- } catch {
3214
- }
3215
- continue;
3216
- } else if (name === "glob") {
3217
- try {
3218
- const args = tc ? JSON.parse(tc.function.arguments) : {};
3219
- summary = `glob: ${args.pattern ?? ""}`;
3220
- } catch {
3221
- summary = "glob";
3222
- }
3223
- } else if (name === "tasks_set") {
3224
- try {
3225
- const args = tc ? JSON.parse(tc.function.arguments) : {};
3226
- const tasks = args.tasks ?? [];
3227
- const inProgress = tasks.filter((t) => t.status === "in_progress").map((t) => t.title);
3228
- const pending = tasks.filter((t) => t.status === "pending").map((t) => t.title);
3229
- if (inProgress.length) stateDelta.next_actions.push(...inProgress);
3230
- if (pending.length) stateDelta.next_actions.push(...pending);
3231
- summary = `tasks_set: ${tasks.length} tasks`;
3232
- } catch {
3233
- summary = "tasks_set";
3234
- }
3280
+ return a.runtime === runtime2 ? -1 : 1;
3281
+ } else if (a.abi !== b.abi) {
3282
+ return a.abi ? -1 : 1;
3283
+ } else if (a.specificity !== b.specificity) {
3284
+ return a.specificity > b.specificity ? -1 : 1;
3285
+ } else {
3286
+ return 0;
3287
+ }
3288
+ };
3235
3289
  }
3236
- const maxRaw = 5e4;
3237
- const raw = content.length > maxRaw ? content.slice(0, maxRaw) + `
3238
- ...[${content.length - maxRaw} chars truncated]` : content;
3239
- const artifact = {
3240
- id: makeArtifactId(type, startIndex + ti),
3241
- type,
3242
- summary,
3243
- raw,
3244
- source: name,
3245
- path,
3246
- ts: (/* @__PURE__ */ new Date()).toISOString()
3247
- };
3248
- artifacts.push(artifact);
3249
- if (!content.includes("Error") && !content.includes("error") && content.length > 0 && content.length < 2e3) {
3250
- stateDelta.confirmed_findings.push(`${name}: ${content.slice(0, 200)}`);
3290
+ function isNwjs() {
3291
+ return !!(process.versions && process.versions.nw);
3292
+ }
3293
+ function isElectron() {
3294
+ if (process.versions && process.versions.electron) return true;
3295
+ if (process.env.ELECTRON_RUN_AS_NODE) return true;
3296
+ return typeof window !== "undefined" && window.process && window.process.type === "renderer";
3297
+ }
3298
+ function isAlpine(platform9) {
3299
+ return platform9 === "linux" && fs.existsSync("/etc/alpine-release");
3251
3300
  }
3301
+ load.parseTags = parseTags;
3302
+ load.matchTags = matchTags;
3303
+ load.compareTags = compareTags;
3304
+ load.parseTuple = parseTuple;
3305
+ load.matchTuple = matchTuple;
3306
+ load.compareTuples = compareTuples;
3252
3307
  }
3253
- const assistantText = typeof turn.assistant.content === "string" ? turn.assistant.content : "";
3254
- if (assistantText.length > 0) {
3255
- const decisionPatterns = [
3256
- /(?:decided?|will|plan to|going to|should|need to)\s+(.{10,200})/gi,
3257
- /(?:let's|let us)\s+(.{10,200})/gi
3258
- ];
3259
- for (const pattern of decisionPatterns) {
3260
- let match;
3261
- while ((match = pattern.exec(assistantText)) !== null) {
3262
- const decision = match[1].trim().replace(/\.$/, "");
3263
- if (decision.length > 10 && !stateDelta.decisions.includes(decision)) {
3264
- stateDelta.decisions.push(decision);
3265
- }
3266
- }
3308
+ });
3309
+
3310
+ // node_modules/node-gyp-build/index.js
3311
+ var require_node_gyp_build2 = __commonJS({
3312
+ "node_modules/node-gyp-build/index.js"(exports, module) {
3313
+ "use strict";
3314
+ var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
3315
+ if (typeof runtimeRequire.addon === "function") {
3316
+ module.exports = runtimeRequire.addon.bind(runtimeRequire);
3317
+ } else {
3318
+ module.exports = require_node_gyp_build();
3267
3319
  }
3268
3320
  }
3269
- return { artifacts, stateDelta };
3321
+ });
3322
+
3323
+ // node_modules/isolated-vm/isolated-vm.js
3324
+ var require_isolated_vm = __commonJS({
3325
+ "node_modules/isolated-vm/isolated-vm.js"(exports, module) {
3326
+ "use strict";
3327
+ module.exports = require_node_gyp_build2()(__dirname).ivm;
3328
+ }
3329
+ });
3330
+
3331
+ // src/code-mode/sandbox.ts
3332
+ import { join as join11, dirname as dirname5 } from "path";
3333
+ import { pathToFileURL } from "url";
3334
+ function stripTypescript(code) {
3335
+ let js = code;
3336
+ js = js.replace(/interface\s+\w+\s*\{[\s\S]*?\n\}/g, "");
3337
+ js = js.replace(/type\s+\w+\s*=\s*[^;]+;/g, "");
3338
+ js = js.replace(/(\w+)<[^>]+>(\s*\()/g, "$1$2");
3339
+ js = js.replace(/(\b(?:const|let|var)\s+\w+)\s*:\s*[^=;]+/g, "$1");
3340
+ js = js.replace(/(\(|,\s*)(\w+)\s*:\s*[^,)=]+/g, "$1$2");
3341
+ js = js.replace(/(\)[\s]*)\s*:\s*[^{]+(\s*\{)/g, "$1$2");
3342
+ js = js.replace(/(\)[\s]*)\s*:\s*Promise<[^>]+>(\s*\{)/g, "$1$2");
3343
+ js = js.replace(/\s+as\s+\w+(?:\[\])?/g, "");
3344
+ js = js.replace(/(\w+)(\??)!/g, "$1$2");
3345
+ js = js.replace(/import\s+type\s+[^;]+;/g, "");
3346
+ js = js.replace(/declare\s+[^;]+;/g, "");
3347
+ js = js.replace(/\n{3,}/g, "\n\n");
3348
+ return js.trim();
3270
3349
  }
3271
- function mergeState(state, delta) {
3272
- const mergeArr = (a, b) => {
3273
- if (!b) return a;
3274
- const set = new Set(a);
3275
- for (const item of b) set.add(item);
3276
- return [...set];
3277
- };
3350
+ async function loadTypescript(cwd) {
3351
+ try {
3352
+ const tsPath = await import.meta.resolve("typescript");
3353
+ return await import(tsPath);
3354
+ } catch {
3355
+ }
3356
+ let dir = cwd;
3357
+ while (dir !== dirname5(dir)) {
3358
+ try {
3359
+ const tsPath = join11(dir, "node_modules", "typescript", "lib", "typescript.js");
3360
+ return await import(pathToFileURL(tsPath).href);
3361
+ } catch {
3362
+ }
3363
+ dir = dirname5(dir);
3364
+ }
3365
+ return null;
3366
+ }
3367
+ async function transpileOrStrip(code, cwd) {
3368
+ const ts = await loadTypescript(cwd);
3369
+ if (ts) {
3370
+ const result = ts.transpileModule(code, {
3371
+ compilerOptions: {
3372
+ module: ts.ModuleKind.ES2022,
3373
+ target: ts.ScriptTarget.ES2022,
3374
+ esModuleInterop: true,
3375
+ isolatedModules: true
3376
+ }
3377
+ });
3378
+ return { js: result.outputText, warnings: [] };
3379
+ }
3278
3380
  return {
3279
- ...state,
3280
- task: state.task || delta.task || "",
3281
- user_constraints: mergeArr(state.user_constraints, delta.user_constraints),
3282
- repo_facts: mergeArr(state.repo_facts, delta.repo_facts),
3283
- files_touched: mergeArr(state.files_touched, delta.files_touched),
3284
- files_modified: mergeArr(state.files_modified, delta.files_modified),
3285
- confirmed_findings: mergeArr(state.confirmed_findings, delta.confirmed_findings),
3286
- open_questions: mergeArr(state.open_questions, delta.open_questions),
3287
- recent_failures: mergeArr(state.recent_failures, delta.recent_failures),
3288
- decisions: mergeArr(state.decisions, delta.decisions),
3289
- next_actions: mergeArr(state.next_actions, delta.next_actions),
3290
- artifact_index: { ...state.artifact_index, ...delta.artifact_index }
3381
+ js: stripTypescript(code),
3382
+ warnings: ["TypeScript not found in node_modules. Using fallback parser; install typescript for reliable transpilation."]
3291
3383
  };
3292
3384
  }
3293
- function shouldCompact(opts2) {
3294
- const tokenThreshold = opts2.tokenThreshold ?? 8e4;
3295
- const turnThreshold = opts2.turnThreshold ?? 12;
3296
- const tokens = estimatePromptTokens(opts2.messages);
3297
- const { turns } = groupIntoTurns(opts2.messages);
3298
- return tokens > tokenThreshold || turns.length > turnThreshold;
3385
+ async function runWithIsolatedVm(opts2) {
3386
+ const { Isolate } = await Promise.resolve().then(() => __toESM(require_isolated_vm(), 1));
3387
+ const isolate = new Isolate({ memoryLimit: opts2.memoryLimitMB ?? 128 });
3388
+ const context = await isolate.createContext();
3389
+ const jail = context.global;
3390
+ await jail.set("global", jail.derefInto());
3391
+ const logs = [];
3392
+ const toolCalls = [];
3393
+ await context.evalClosure(
3394
+ `globalThis._log = function(...args) {
3395
+ $0.applySync(undefined, [args.map(String).join(" ")], { arguments: { copy: true } });
3396
+ };`,
3397
+ [(msg) => logs.push(msg)],
3398
+ { arguments: { reference: true } }
3399
+ );
3400
+ await context.eval(`var console = { log: function(...args) { _log(args.map(String).join(" ")); } };`);
3401
+ const toolMap = new Map(opts2.tools.map((t) => [t.name, t]));
3402
+ for (const tool of opts2.tools) {
3403
+ const ref = new (await Promise.resolve().then(() => __toESM(require_isolated_vm(), 1))).Reference(
3404
+ async (argsJson) => {
3405
+ const args = JSON.parse(argsJson);
3406
+ const toolCallId = `code_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
3407
+ const result = await opts2.executor.run(
3408
+ { id: toolCallId, name: tool.name, arguments: JSON.stringify(args) },
3409
+ opts2.askPermission,
3410
+ opts2.ctx
3411
+ );
3412
+ toolCalls.push({
3413
+ name: tool.name,
3414
+ args,
3415
+ result: result.content
3416
+ });
3417
+ return result.content;
3418
+ }
3419
+ );
3420
+ await context.evalClosure(
3421
+ `globalThis["_api_${tool.name}"] = function(argsJson) {
3422
+ return $0.applySyncPromise(undefined, [argsJson], { arguments: { copy: true } });
3423
+ };`,
3424
+ [ref],
3425
+ { arguments: { reference: true } }
3426
+ );
3427
+ }
3428
+ const apiMethods = opts2.tools.map((t) => ` ${t.name}: function(input) { return _api_${t.name}(JSON.stringify(input ?? {})); }`).join(",\n");
3429
+ await context.eval(`var api = {
3430
+ ${apiMethods}
3431
+ };`);
3432
+ const { js: jsCode, warnings } = await transpileOrStrip(opts2.code, opts2.ctx.cwd);
3433
+ const wrapped = `(async function() {
3434
+ ${jsCode}
3435
+ })();`;
3436
+ try {
3437
+ const timeout = opts2.timeoutMs ?? 3e4;
3438
+ const script = await isolate.compileScript(wrapped);
3439
+ await script.run(context, { timeout, release: true });
3440
+ await new Promise((r) => setTimeout(r, 10));
3441
+ } catch (err) {
3442
+ const message2 = err instanceof Error ? err.message : String(err);
3443
+ return { output: "", logs, error: message2, toolCalls, warnings };
3444
+ } finally {
3445
+ isolate.dispose();
3446
+ }
3447
+ return { output: logs.join("\n"), logs, toolCalls, warnings };
3299
3448
  }
3300
- function compactMessagesViaArtifacts(opts2) {
3301
- const keepLastTurns = opts2.keepLastTurns ?? 4;
3302
- const { prefix, turns } = groupIntoTurns(opts2.messages);
3303
- const tokensBefore = estimatePromptTokens(opts2.messages);
3304
- if (turns.length <= keepLastTurns) {
3305
- return {
3306
- newMessages: opts2.messages,
3307
- newState: opts2.state,
3308
- metrics: {
3309
- estimatedTokensBefore: tokensBefore,
3310
- estimatedTokensAfter: tokensBefore,
3311
- archivedArtifacts: 0,
3312
- recalledArtifacts: 0,
3313
- rawTurnsRemoved: 0,
3314
- rawTurnsKept: turns.length
3449
+ async function runWithNodeVm(opts2) {
3450
+ const { runInNewContext } = await import("vm");
3451
+ const logs = [];
3452
+ const toolCalls = [];
3453
+ const sandbox = {
3454
+ console: {
3455
+ log: (...args) => {
3456
+ logs.push(args.map(String).join(" "));
3315
3457
  }
3458
+ },
3459
+ api: {},
3460
+ setTimeout,
3461
+ clearTimeout,
3462
+ setInterval,
3463
+ clearInterval,
3464
+ Promise,
3465
+ JSON,
3466
+ Math,
3467
+ Date,
3468
+ Array,
3469
+ Object,
3470
+ String,
3471
+ Number,
3472
+ Boolean,
3473
+ RegExp,
3474
+ Error,
3475
+ TypeError,
3476
+ RangeError,
3477
+ SyntaxError,
3478
+ ReferenceError,
3479
+ Map,
3480
+ Set,
3481
+ WeakMap,
3482
+ WeakSet,
3483
+ Symbol,
3484
+ parseInt,
3485
+ parseFloat,
3486
+ isNaN,
3487
+ isFinite,
3488
+ encodeURI,
3489
+ decodeURI,
3490
+ encodeURIComponent,
3491
+ decodeURIComponent,
3492
+ escape,
3493
+ unescape,
3494
+ Infinity: Infinity,
3495
+ NaN: NaN,
3496
+ undefined: void 0
3497
+ };
3498
+ for (const tool of opts2.tools) {
3499
+ sandbox.api[tool.name] = async (input) => {
3500
+ const args = input ?? {};
3501
+ const toolCallId = `code_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
3502
+ const result = await opts2.executor.run(
3503
+ { id: toolCallId, name: tool.name, arguments: JSON.stringify(args) },
3504
+ opts2.askPermission,
3505
+ opts2.ctx
3506
+ );
3507
+ toolCalls.push({
3508
+ name: tool.name,
3509
+ args,
3510
+ result: result.content
3511
+ });
3512
+ return result.content;
3316
3513
  };
3317
3514
  }
3318
- const toCompact = turns.slice(0, turns.length - keepLastTurns);
3319
- const toKeep = turns.slice(turns.length - keepLastTurns);
3320
- let newState = { ...opts2.state };
3321
- let archivedCount = 0;
3322
- for (let i = 0; i < toCompact.length; i++) {
3323
- const turn = toCompact[i];
3324
- const { artifacts, stateDelta } = extractArtifactsFromTurn(turn, i, opts2.store);
3325
- for (const artifact of artifacts) {
3326
- opts2.store.add(artifact);
3327
- archivedCount++;
3328
- newState.artifact_index[artifact.id] = {
3329
- type: artifact.type,
3330
- summary: artifact.summary,
3331
- source: artifact.source,
3332
- path: artifact.path
3333
- };
3334
- }
3335
- newState = mergeState(newState, stateDelta);
3336
- if (!newState.task && typeof turn.user.content === "string") {
3337
- newState.task = turn.user.content.slice(0, 200);
3338
- }
3339
- }
3340
- const workingMemory = [];
3341
- for (const turn of toKeep) {
3342
- workingMemory.push(turn.user);
3343
- workingMemory.push(turn.assistant);
3344
- for (const tm of turn.tools) {
3345
- workingMemory.push(tm);
3346
- }
3515
+ const { js: jsCode, warnings } = await transpileOrStrip(opts2.code, opts2.ctx.cwd);
3516
+ const wrapped = `"use strict";
3517
+ (async function() {
3518
+ ${jsCode}
3519
+ })();`;
3520
+ try {
3521
+ const timeout = opts2.timeoutMs ?? 3e4;
3522
+ await runInNewContext(wrapped, sandbox, { timeout, displayErrors: true });
3523
+ await new Promise((r) => setTimeout(r, 10));
3524
+ } catch (err) {
3525
+ const message2 = err instanceof Error ? err.message : String(err);
3526
+ return { output: "", logs, error: message2, toolCalls, warnings };
3347
3527
  }
3348
- const stateMsg = buildSessionStateMessage(newState);
3349
- const newMessages = [...prefix, stateMsg, ...workingMemory];
3350
- const tokensAfter = estimatePromptTokens(newMessages);
3351
- const metrics = {
3352
- estimatedTokensBefore: tokensBefore,
3353
- estimatedTokensAfter: tokensAfter,
3354
- archivedArtifacts: archivedCount,
3355
- recalledArtifacts: 0,
3356
- rawTurnsRemoved: toCompact.length,
3357
- rawTurnsKept: toKeep.length
3358
- };
3359
- return { newMessages, newState, metrics };
3528
+ return { output: logs.join("\n"), logs, toolCalls, warnings };
3360
3529
  }
3361
- function recallArtifacts(messages, store, state) {
3362
- const text = messages.map((m) => typeof m.content === "string" ? m.content : "").join(" ");
3363
- const ids = [];
3364
- for (const [id, meta] of Object.entries(state.artifact_index)) {
3365
- if (meta.path && text.includes(meta.path)) {
3366
- ids.push(id);
3367
- }
3530
+ function buildFallbackWarning(errMessage) {
3531
+ let reason;
3532
+ let fix;
3533
+ if (errMessage.includes("Cannot find module") || errMessage.includes("isolated-vm")) {
3534
+ reason = "The optional dependency `isolated-vm` is not installed.";
3535
+ fix = "Run `npm install isolated-vm` in your project (or `npm install -g isolated-vm` if KimiFlare is installed globally).";
3536
+ } else if (errMessage.includes("bindings") || errMessage.includes(".node")) {
3537
+ reason = "The `isolated-vm` native bindings are incompatible with this Node version or architecture.";
3538
+ fix = "Try `npm rebuild isolated-vm`, or switch to Node 22 LTS if you're on Apple Silicon.";
3539
+ } else {
3540
+ reason = "The secure sandbox (`isolated-vm`) could not be loaded.";
3541
+ fix = "Ensure build tools are installed, then run `npm install isolated-vm`.";
3368
3542
  }
3369
- for (const failure of state.recent_failures) {
3370
- const keyword = failure.split(":")[0];
3371
- if (!keyword) continue;
3372
- const lowerKeyword = keyword.toLowerCase();
3373
- if (!text.toLowerCase().includes(lowerKeyword)) continue;
3374
- for (const [id, meta] of Object.entries(state.artifact_index)) {
3375
- if (meta.source === "bash" && !ids.includes(id) && meta.summary.toLowerCase().includes(lowerKeyword)) {
3376
- ids.push(id);
3377
- }
3543
+ return `Code Mode is using the built-in Node.js sandbox. Tool execution works normally, but without memory limits or full process isolation. ${reason} ${fix}`;
3544
+ }
3545
+ async function runInSandbox(opts2) {
3546
+ try {
3547
+ return await runWithIsolatedVm(opts2);
3548
+ } catch (err) {
3549
+ const message2 = err instanceof Error ? err.message : String(err);
3550
+ const result = await runWithNodeVm(opts2);
3551
+ const sessionKey = opts2.ctx.sessionId ?? NO_SESSION_KEY;
3552
+ if (!fallbackWarningShownSessions.has(sessionKey)) {
3553
+ fallbackWarningShownSessions.add(sessionKey);
3554
+ const warning = buildFallbackWarning(message2);
3555
+ return { ...result, warnings: [warning, ...result.warnings ?? []] };
3378
3556
  }
3557
+ return result;
3379
3558
  }
3380
- const uniqueIds = [...new Set(ids)].slice(0, 5);
3381
- const recalled = store.recall(uniqueIds);
3382
- return { ids: uniqueIds, recalled };
3383
3559
  }
3384
- var init_artifact_compaction = __esm({
3385
- "src/agent/artifact-compaction.ts"() {
3560
+ var fallbackWarningShownSessions, NO_SESSION_KEY;
3561
+ var init_sandbox = __esm({
3562
+ "src/code-mode/sandbox.ts"() {
3386
3563
  "use strict";
3387
- init_session_state();
3564
+ fallbackWarningShownSessions = /* @__PURE__ */ new Set();
3565
+ NO_SESSION_KEY = "__no_session__";
3566
+ }
3567
+ });
3568
+
3569
+ // src/code-mode/index.ts
3570
+ var init_code_mode = __esm({
3571
+ "src/code-mode/index.ts"() {
3572
+ "use strict";
3573
+ init_api_generator();
3574
+ init_sandbox();
3388
3575
  }
3389
3576
  });
3390
3577
 
@@ -3987,12 +4174,12 @@ var init_mode = __esm({
3987
4174
  });
3988
4175
 
3989
4176
  // src/agent/system-prompt.ts
3990
- import { platform, release, homedir as homedir6 } from "os";
3991
- import { basename, join as join11 } from "path";
4177
+ import { platform, release, homedir as homedir7 } from "os";
4178
+ import { basename, join as join12 } from "path";
3992
4179
  import { readFileSync as readFileSync2, statSync as statSync2 } from "fs";
3993
4180
  function loadContextFile(cwd) {
3994
4181
  for (const name of CONTEXT_FILENAMES) {
3995
- const path = join11(cwd, name);
4182
+ const path = join12(cwd, name);
3996
4183
  try {
3997
4184
  const s = statSync2(path);
3998
4185
  if (!s.isFile() || s.size > MAX_CONTEXT_BYTES) continue;
@@ -4042,7 +4229,7 @@ If the user asks what model you are, answer with exactly: \`${opts2.model}\`. Th
4042
4229
  - Working directory: ${opts2.cwd}
4043
4230
  - Platform: ${platform()} ${release()}
4044
4231
  - Shell: ${shell}
4045
- - Home: ${homedir6()}
4232
+ - Home: ${homedir7()}
4046
4233
  - Today: ${date}`;
4047
4234
  const hasLsp = opts2.tools.some((t) => t.name.startsWith("lsp_"));
4048
4235
  const lspBlock = hasLsp ? "\n\nLSP tools are available for semantic code intelligence. Prefer `lsp_definition` over `grep` when looking for the source of a symbol. Prefer `lsp_references` over `grep` when finding usages. Use `lsp_hover` to confirm types before refactoring." : "";
@@ -4118,6 +4305,9 @@ function getSessionWebFetchHistory(sessionId) {
4118
4305
  function isHighSignalMemory(memory) {
4119
4306
  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;
4120
4307
  }
4308
+ function codeModeRedirectMessage(tool) {
4309
+ return `Code Mode is on: \`${tool}\` is not available as a direct tool because its full output would flood your context. Call \`api.${tool}({ ... })\` INSIDE an \`execute_code\` block instead \u2014 only what you \`console.log\` is returned to you. You can batch several reads/greps/commands in a single execute_code call.`;
4310
+ }
4121
4311
  function extractLastUserText(messages) {
4122
4312
  const lastUser = [...messages].reverse().find((m) => m.role === "user");
4123
4313
  if (!lastUser) return "";
@@ -4162,7 +4352,7 @@ async function runAgentTurn(opts2) {
4162
4352
  const lastUserPrompt = extractLastUserText(opts2.messages);
4163
4353
  const userPromptPreview = lastUserPrompt.slice(0, 200);
4164
4354
  const skipSkillRouting = opts2.intentClassification?.tier === "light" && lastUserPrompt.length < 40;
4165
- const recallPromise = opts2.sessionStartRecall && opts2.memoryManager ? (async () => {
4355
+ const recallPromise = opts2.sessionStartRecall && opts2.memoryManager && !hasRecalledMemory(opts2.messages) ? (async () => {
4166
4356
  const results = await opts2.sessionStartRecall;
4167
4357
  if (results.length === 0 || !opts2.memoryManager) return null;
4168
4358
  const text = await opts2.memoryManager.synthesizeRecalled(results, opts2.signal);
@@ -4193,11 +4383,10 @@ async function runAgentTurn(opts2) {
4193
4383
  }
4194
4384
  if (recallSettled.status === "fulfilled" && recallSettled.value) {
4195
4385
  const { text, count } = recallSettled.value;
4196
- const lastSystemIdx = opts2.messages.findLastIndex((m) => m.role === "system");
4197
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : opts2.messages.length;
4198
- opts2.messages.splice(insertIdx, 0, { role: "system", content: text });
4199
- memoryRecalledCount = count;
4200
- opts2.callbacks.onMemoryRecalled?.(count);
4386
+ if (injectRecalledMemoryOnce(opts2.messages, text)) {
4387
+ memoryRecalledCount = count;
4388
+ opts2.callbacks.onMemoryRecalled?.(count);
4389
+ }
4201
4390
  }
4202
4391
  if (skillsSettled.status === "fulfilled" && skillsSettled.value) {
4203
4392
  skillResult = skillsSettled.value;
@@ -4257,7 +4446,9 @@ async function runAgentTurn(opts2) {
4257
4446
  Available APIs:
4258
4447
  ${codeModeApiString}
4259
4448
 
4260
- Use console.log() to return results. Only console.log output will be sent back to you.`,
4449
+ Use console.log() to return results. Only console.log output is sent back to you.
4450
+
4451
+ IMPORTANT \u2014 explore through code, not direct tools: to read files, run shell commands, grep, or glob, call api.read(...), api.bash(...), api.grep(...), api.glob(...) INSIDE this code block and console.log only what you need. Do NOT call read/bash/grep/glob as separate tools \u2014 their full output floods your context, while here only what you log returns. Batch multiple reads/greps/commands into a single execute_code call.`,
4261
4452
  parameters: {
4262
4453
  type: "object",
4263
4454
  properties: {
@@ -4286,6 +4477,7 @@ Use console.log() to return results. Only console.log output will be sent back t
4286
4477
  const LOOP_THRESHOLD = 2;
4287
4478
  const webFetchHistory = getSessionWebFetchHistory(opts2.sessionId);
4288
4479
  let webFetchesThisTurn = 0;
4480
+ let codeModeRedirects = 0;
4289
4481
  const MAX_WEB_FETCH_PER_TURN = 5;
4290
4482
  const WEB_FETCH_DOMAIN_THRESHOLD = 2;
4291
4483
  let cumulativePromptTokens = 0;
@@ -4542,6 +4734,16 @@ Use console.log() to return results. Only console.log output will be sent back t
4542
4734
  });
4543
4735
  continue;
4544
4736
  }
4737
+ if (codeMode && CODE_MODE_REDIRECT_TOOLS.has(tc.function.name) && codeModeRedirects < MAX_CODE_MODE_REDIRECTS) {
4738
+ codeModeRedirects++;
4739
+ items.push({
4740
+ kind: "blocked",
4741
+ tc,
4742
+ loopSignature,
4743
+ result: { tool_call_id: tc.id, name: tc.function.name, content: codeModeRedirectMessage(tc.function.name), ok: false }
4744
+ });
4745
+ continue;
4746
+ }
4545
4747
  if (tc.function.name === "web_fetch") {
4546
4748
  const args = JSON.parse(tc.function.arguments || "{}");
4547
4749
  const url = args.url || "";
@@ -4906,6 +5108,13 @@ ${sandboxResult.output}` : sandboxResult.output;
4906
5108
  opts2.callbacks.onToolResult?.(result);
4907
5109
  recentToolCalls.push(loopSignature);
4908
5110
  if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
5111
+ } else if (codeMode && CODE_MODE_REDIRECT_TOOLS.has(tc.function.name) && codeModeRedirects < MAX_CODE_MODE_REDIRECTS) {
5112
+ codeModeRedirects++;
5113
+ const msg = codeModeRedirectMessage(tc.function.name);
5114
+ const redirectResult = { tool_call_id: tc.id, name: tc.function.name, content: msg, ok: false };
5115
+ toolResults.push(redirectResult);
5116
+ opts2.messages.push({ role: "tool", tool_call_id: tc.id, content: msg, name: tc.function.name });
5117
+ opts2.callbacks.onToolResult?.(redirectResult);
4909
5118
  } else {
4910
5119
  opts2.callbacks.onToolWillExecute?.(tc.id, tc.function.name);
4911
5120
  logger.debug("turn:tool_start", { sessionId: opts2.sessionId, tool: tc.function.name, toolCallId: tc.id });
@@ -5142,13 +5351,14 @@ function validateToolArguments(raw) {
5142
5351
  return "{}";
5143
5352
  }
5144
5353
  }
5145
- var BudgetExhaustedError, AgentLoopError, codeModeApiCache, driftEvents, DRIFT_WINDOW, DRIFT_THRESHOLD, memoryExtractionErrorCounts, sessionWebFetchHistory, SESSION_WEB_FETCH_CAP, DEFAULT_MAX_COMPLETION_TOKENS, BUDGET_SAFETY_MARGIN_TOKENS, MAX_TOOL_CONTENT_CHARS;
5354
+ var BudgetExhaustedError, AgentLoopError, codeModeApiCache, driftEvents, DRIFT_WINDOW, DRIFT_THRESHOLD, memoryExtractionErrorCounts, sessionWebFetchHistory, SESSION_WEB_FETCH_CAP, DEFAULT_MAX_COMPLETION_TOKENS, BUDGET_SAFETY_MARGIN_TOKENS, MAX_TOOL_CONTENT_CHARS, CODE_MODE_REDIRECT_TOOLS, MAX_CODE_MODE_REDIRECTS;
5146
5355
  var init_loop = __esm({
5147
5356
  "src/agent/loop.ts"() {
5148
5357
  "use strict";
5149
5358
  init_client();
5150
5359
  init_registry2();
5151
5360
  init_messages();
5361
+ init_recall_inject();
5152
5362
  init_cost_debug();
5153
5363
  init_extractors();
5154
5364
  init_strip_reasoning();
@@ -5180,6 +5390,8 @@ var init_loop = __esm({
5180
5390
  DEFAULT_MAX_COMPLETION_TOKENS = 16384;
5181
5391
  BUDGET_SAFETY_MARGIN_TOKENS = 8192;
5182
5392
  MAX_TOOL_CONTENT_CHARS = 1e4;
5393
+ CODE_MODE_REDIRECT_TOOLS = /* @__PURE__ */ new Set(["read", "bash", "grep", "glob"]);
5394
+ MAX_CODE_MODE_REDIRECTS = 4;
5183
5395
  }
5184
5396
  });
5185
5397
 
@@ -5243,10 +5455,10 @@ var init_tool_error = __esm({
5243
5455
 
5244
5456
  // src/util/paths.ts
5245
5457
  import { resolve, isAbsolute, relative, sep } from "path";
5246
- import { homedir as homedir7 } from "os";
5458
+ import { homedir as homedir8 } from "os";
5247
5459
  function resolvePath(cwd, input) {
5248
5460
  if (input.startsWith("~/") || input === "~") {
5249
- return resolve(homedir7(), input === "~" ? "." : input.slice(2));
5461
+ return resolve(homedir8(), input === "~" ? "." : input.slice(2));
5250
5462
  }
5251
5463
  return isAbsolute(input) ? input : resolve(cwd, input);
5252
5464
  }
@@ -5473,7 +5685,7 @@ var init_edit = __esm({
5473
5685
  // src/tools/bash.ts
5474
5686
  import { spawn } from "child_process";
5475
5687
  import { tmpdir, platform as platform2 } from "os";
5476
- import { join as join12 } from "path";
5688
+ import { join as join13 } from "path";
5477
5689
  function getShellCommand(override) {
5478
5690
  const raw = override?.trim();
5479
5691
  if (raw && raw !== "auto") {
@@ -5519,7 +5731,7 @@ function injectCoauthor(command, coauthor) {
5519
5731
  const mentionsGit = /\bgit\b/.test(trimmed);
5520
5732
  if (!createsCommit && !isRebaseContinue && !mentionsGit) return command;
5521
5733
  if (movesHeadOnly) return command;
5522
- const tmpFile = join12(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
5734
+ const tmpFile = join13(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
5523
5735
  const amendBlock = `
5524
5736
  if ! git log -1 --pretty=%B 2>/dev/null | grep -qF "${trailer}"; then
5525
5737
  git log -1 --pretty=%B | git interpret-trailers --trailer "${trailer}" > "${tmpFile}" && git commit --amend -F "${tmpFile}" --no-edit && rm -f "${tmpFile}"
@@ -5635,7 +5847,7 @@ var init_bash = __esm({
5635
5847
 
5636
5848
  // src/util/glob.ts
5637
5849
  import { readdir as readdir3, lstat, realpath } from "fs/promises";
5638
- import { join as join13, relative as relative2, resolve as resolve2 } from "path";
5850
+ import { join as join14, relative as relative2, resolve as resolve2 } from "path";
5639
5851
  function parsePattern(pattern) {
5640
5852
  const parts = pattern.split(/\//g);
5641
5853
  return parts.map((p) => {
@@ -5744,7 +5956,7 @@ async function* walk(root, pattern, options) {
5744
5956
  if (!dot && name.startsWith(".")) continue;
5745
5957
  const matched = seg.type === "literal" ? seg.value === name : matchSegment(name, seg.value);
5746
5958
  if (!matched) continue;
5747
- const childPath = join13(dirPath, name);
5959
+ const childPath = join14(dirPath, name);
5748
5960
  const childRel = [...relativeParts, name];
5749
5961
  const childRelStr = childRel.join("/");
5750
5962
  if (shouldIgnore(childRelStr, ignorePatterns)) continue;
@@ -5769,7 +5981,7 @@ async function* walk(root, pattern, options) {
5769
5981
  if (!dot && name.startsWith(".")) continue;
5770
5982
  const matched = seg.type === "literal" ? seg.value === name : matchSegment(name, seg.value);
5771
5983
  if (!matched) continue;
5772
- const childPath = join13(dirPath, name);
5984
+ const childPath = join14(dirPath, name);
5773
5985
  const childRel = [...relativeParts, name];
5774
5986
  const childRelStr = childRel.join("/");
5775
5987
  if (shouldIgnore(childRelStr, ignorePatterns)) continue;
@@ -5811,7 +6023,7 @@ async function* walk(root, pattern, options) {
5811
6023
  const name = String(ent.name);
5812
6024
  if (name === "." || name === "..") continue;
5813
6025
  if (!dot && name.startsWith(".")) continue;
5814
- const childPath = join13(dirPath, name);
6026
+ const childPath = join14(dirPath, name);
5815
6027
  const childRel = [...relativeParts, name];
5816
6028
  const childRelStr = childRel.join("/");
5817
6029
  if (shouldIgnore(childRelStr, ignorePatterns)) continue;
@@ -6497,7 +6709,7 @@ __export(changelog_image_exports, {
6497
6709
  });
6498
6710
  import { readFile as readFile9 } from "fs/promises";
6499
6711
  import { writeFile as writeFile7 } from "fs/promises";
6500
- import { join as join14 } from "path";
6712
+ import { join as join15 } from "path";
6501
6713
  import { Resvg } from "@resvg/resvg-js";
6502
6714
  async function githubFetch2(path, token) {
6503
6715
  const controller = new AbortController();
@@ -6529,7 +6741,7 @@ function escapeXml(str) {
6529
6741
  }
6530
6742
  async function loadLogoBase64() {
6531
6743
  try {
6532
- const buf = await readFile9(join14(process.cwd(), "docs", "logo.png"));
6744
+ const buf = await readFile9(join15(process.cwd(), "docs", "logo.png"));
6533
6745
  return `data:image/png;base64,${buf.toString("base64")}`;
6534
6746
  } catch {
6535
6747
  return null;
@@ -6837,7 +7049,7 @@ Format your response as plain text bullet points, one per line, starting with "\
6837
7049
  // src/tools/browser.ts
6838
7050
  import { mkdir as mkdir7 } from "fs/promises";
6839
7051
  import { tmpdir as tmpdir2 } from "os";
6840
- import { join as join15, dirname as dirname7 } from "path";
7052
+ import { join as join16, dirname as dirname7 } from "path";
6841
7053
  async function autoScroll(page) {
6842
7054
  await page.evaluate(async () => {
6843
7055
  await new Promise((resolve8) => {
@@ -6913,7 +7125,7 @@ var init_browser = __esm({
6913
7125
  }
6914
7126
  let screenshotPath;
6915
7127
  if (args.screenshot) {
6916
- screenshotPath = join15(tmpdir2(), `kimiflare-browser-${Date.now()}.png`);
7128
+ screenshotPath = join16(tmpdir2(), `kimiflare-browser-${Date.now()}.png`);
6917
7129
  await mkdir7(dirname7(screenshotPath), { recursive: true });
6918
7130
  await page.screenshot({ path: screenshotPath, fullPage: true });
6919
7131
  }
@@ -8295,11 +8507,11 @@ __export(sessions_exports, {
8295
8507
  sessionsDir: () => sessionsDir
8296
8508
  });
8297
8509
  import { readFile as readFile10, writeFile as writeFile9, mkdir as mkdir8, readdir as readdir4, stat as stat4 } from "fs/promises";
8298
- import { homedir as homedir8 } from "os";
8299
- import { join as join16 } from "path";
8510
+ import { homedir as homedir9 } from "os";
8511
+ import { join as join17 } from "path";
8300
8512
  function sessionsDir() {
8301
- const xdg = process.env.XDG_DATA_HOME || join16(homedir8(), ".local", "share");
8302
- return join16(xdg, "kimiflare", "sessions");
8513
+ const xdg = process.env.XDG_DATA_HOME || join17(homedir9(), ".local", "share");
8514
+ return join17(xdg, "kimiflare", "sessions");
8303
8515
  }
8304
8516
  function sanitize(text) {
8305
8517
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -8319,7 +8531,7 @@ function makeSessionId(firstPrompt) {
8319
8531
  async function saveSession(file) {
8320
8532
  const dir = sessionsDir();
8321
8533
  await mkdir8(dir, { recursive: true });
8322
- const path = join16(dir, `${file.id}.json`);
8534
+ const path = join17(dir, `${file.id}.json`);
8323
8535
  await writeFile9(path, JSON.stringify(file, null, 2), "utf8");
8324
8536
  return path;
8325
8537
  }
@@ -8339,7 +8551,7 @@ async function listSessions(limit = 30, cwd) {
8339
8551
  const summaries = [];
8340
8552
  for (const name of entries) {
8341
8553
  if (!name.endsWith(".json")) continue;
8342
- const path = join16(dir, name);
8554
+ const path = join17(dir, name);
8343
8555
  try {
8344
8556
  const [s, raw] = await Promise.all([stat4(path), readFile10(path, "utf8")]);
8345
8557
  const parsed = JSON.parse(raw);
@@ -8450,7 +8662,7 @@ var init_image = __esm({
8450
8662
 
8451
8663
  // src/permissions-evaluator.ts
8452
8664
  import { resolve as resolve3, relative as relative3 } from "path";
8453
- import { homedir as homedir9 } from "os";
8665
+ import { homedir as homedir10 } from "os";
8454
8666
  function evaluatePermissionRules(req, rules) {
8455
8667
  const toolRules = rules[req.tool];
8456
8668
  if (!toolRules) return "ask";
@@ -8466,7 +8678,7 @@ function evaluatePermissionRules(req, rules) {
8466
8678
  }
8467
8679
  function expandHome(path) {
8468
8680
  if (path.startsWith("~/")) {
8469
- return resolve3(homedir9(), path.slice(2));
8681
+ return resolve3(homedir10(), path.slice(2));
8470
8682
  }
8471
8683
  return path;
8472
8684
  }
@@ -8492,7 +8704,7 @@ function extractTarget(tool, args, cwd) {
8492
8704
  function matchPattern(target, pattern, cwd) {
8493
8705
  let expandedPattern = pattern;
8494
8706
  if (pattern.startsWith("~/")) {
8495
- expandedPattern = resolve3(homedir9(), pattern.slice(2));
8707
+ expandedPattern = resolve3(homedir10(), pattern.slice(2));
8496
8708
  }
8497
8709
  if (target.startsWith(expandedPattern)) {
8498
8710
  return true;
@@ -8539,16 +8751,16 @@ var init_types = __esm({
8539
8751
  });
8540
8752
 
8541
8753
  // src/hooks/settings.ts
8542
- import { existsSync as existsSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync2, writeFileSync } from "fs";
8543
- import { homedir as homedir10 } from "os";
8544
- import { join as join17, dirname as dirname8 } from "path";
8754
+ import { existsSync as existsSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
8755
+ import { homedir as homedir11 } from "os";
8756
+ import { join as join18, dirname as dirname8 } from "path";
8545
8757
  import { createHash } from "crypto";
8546
8758
  function globalSettingsPath() {
8547
- const xdg = process.env.XDG_CONFIG_HOME || join17(homedir10(), ".config");
8548
- return join17(xdg, "kimiflare", "settings.json");
8759
+ const xdg = process.env.XDG_CONFIG_HOME || join18(homedir11(), ".config");
8760
+ return join18(xdg, "kimiflare", "settings.json");
8549
8761
  }
8550
8762
  function projectSettingsPath(cwd) {
8551
- return join17(cwd, ".kimiflare", "settings.json");
8763
+ return join18(cwd, ".kimiflare", "settings.json");
8552
8764
  }
8553
8765
  function readSettingsFile(path) {
8554
8766
  if (!existsSync2(path)) return null;
@@ -8624,8 +8836,8 @@ function appendHook(scope, cwd, event, hook) {
8624
8836
  return rest;
8625
8837
  });
8626
8838
  existing.hooks = hooks;
8627
- mkdirSync2(dirname8(path), { recursive: true });
8628
- writeFileSync(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
8839
+ mkdirSync3(dirname8(path), { recursive: true });
8840
+ writeFileSync2(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
8629
8841
  return path;
8630
8842
  }
8631
8843
  function setHookEnabled(cwd, id, enabled) {
@@ -8650,8 +8862,8 @@ function setHookEnabled(cwd, id, enabled) {
8650
8862
  }
8651
8863
  }
8652
8864
  if (changed) {
8653
- mkdirSync2(dirname8(path), { recursive: true });
8654
- writeFileSync(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
8865
+ mkdirSync3(dirname8(path), { recursive: true });
8866
+ writeFileSync2(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
8655
8867
  return path;
8656
8868
  }
8657
8869
  void scope;
@@ -9381,11 +9593,11 @@ var init_heuristic = __esm({
9381
9593
 
9382
9594
  // src/cost-attribution/classify-from-session.ts
9383
9595
  import { readFile as readFile13 } from "fs/promises";
9384
- import { join as join18 } from "path";
9385
- import { homedir as homedir11 } from "os";
9596
+ import { join as join19 } from "path";
9597
+ import { homedir as homedir12 } from "os";
9386
9598
  function sessionsDir2() {
9387
- const xdg = process.env.XDG_DATA_HOME || join18(homedir11(), ".local", "share");
9388
- return join18(xdg, "kimiflare", "sessions");
9599
+ const xdg = process.env.XDG_DATA_HOME || join19(homedir12(), ".local", "share");
9600
+ return join19(xdg, "kimiflare", "sessions");
9389
9601
  }
9390
9602
  function parseToolCalls(calls) {
9391
9603
  return calls.map((c) => {
@@ -9399,7 +9611,7 @@ function parseToolCalls(calls) {
9399
9611
  }
9400
9612
  async function classifyFromSessionFile(sessionId) {
9401
9613
  try {
9402
- const raw = await readFile13(join18(sessionsDir2(), `${sessionId}.json`), "utf8");
9614
+ const raw = await readFile13(join19(sessionsDir2(), `${sessionId}.json`), "utf8");
9403
9615
  const session = JSON.parse(raw);
9404
9616
  const messages = session.messages ?? [];
9405
9617
  const turns = [];
@@ -9433,14 +9645,14 @@ __export(cli_exports, {
9433
9645
  runCostCommand: () => runCostCommand
9434
9646
  });
9435
9647
  import { readFile as readFile14 } from "fs/promises";
9436
- import { join as join19 } from "path";
9437
- import { homedir as homedir12 } from "os";
9648
+ import { join as join20 } from "path";
9649
+ import { homedir as homedir13 } from "os";
9438
9650
  function usageDir() {
9439
- const xdg = process.env.XDG_DATA_HOME || join19(homedir12(), ".local", "share");
9440
- return join19(xdg, "kimiflare");
9651
+ const xdg = process.env.XDG_DATA_HOME || join20(homedir13(), ".local", "share");
9652
+ return join20(xdg, "kimiflare");
9441
9653
  }
9442
9654
  function usagePath() {
9443
- return join19(usageDir(), "usage.json");
9655
+ return join20(usageDir(), "usage.json");
9444
9656
  }
9445
9657
  function today() {
9446
9658
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -10442,7 +10654,7 @@ __export(db_exports, {
10442
10654
  });
10443
10655
  import Database from "better-sqlite3";
10444
10656
  import { dirname as dirname9 } from "path";
10445
- import { mkdirSync as mkdirSync3, statSync as statSync3 } from "fs";
10657
+ import { mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
10446
10658
  function initSchema(db) {
10447
10659
  db.exec(`
10448
10660
  CREATE TABLE IF NOT EXISTS memories (
@@ -10526,7 +10738,7 @@ function openMemoryDb(dbPath) {
10526
10738
  if (dbInstance) {
10527
10739
  dbInstance.close();
10528
10740
  }
10529
- mkdirSync3(dirname9(dbPath), { recursive: true });
10741
+ mkdirSync4(dirname9(dbPath), { recursive: true });
10530
10742
  dbInstance = new Database(dbPath);
10531
10743
  dbInstance.pragma("journal_mode = WAL");
10532
10744
  dbInstance.pragma("foreign_keys = ON");
@@ -11237,6 +11449,50 @@ Return a JSON array of strings. Example:
11237
11449
  }
11238
11450
  return { id: memory.id, superseded: supersededIds.length > 0 ? supersededIds : void 0 };
11239
11451
  }
11452
+ /**
11453
+ * Store a plan directly under a deterministic topic key.
11454
+ * Skips embedding, verification, and hypothetical queries so it is fast and
11455
+ * deterministic. Supersedes any previous plan stored under the same key.
11456
+ */
11457
+ async rememberPlan(plan, repoPath, sessionId, topicKey = "current_dev_plan") {
11458
+ if (!this.db) throw new Error("Memory DB not open");
11459
+ const safeContent = this.shouldRedact() ? redactSecrets(plan) : plan;
11460
+ if (!safeContent.trim()) {
11461
+ throw new Error("Plan content is empty after redaction");
11462
+ }
11463
+ const normalizedKey = topicKey.trim();
11464
+ if (!normalizedKey) {
11465
+ throw new Error("Plan topic key cannot be empty");
11466
+ }
11467
+ const supersededIds = [];
11468
+ const existing = findMemoriesByTopicKey(this.db, repoPath, normalizedKey);
11469
+ for (const old of existing) {
11470
+ supersedeMemory(this.db, old.id, "pending");
11471
+ supersededIds.push(old.id);
11472
+ }
11473
+ const zeroEmbedding = new Float32Array(DEFAULT_EMBEDDING_DIM);
11474
+ const memory = insertMemory(this.db, {
11475
+ content: safeContent,
11476
+ category: "task",
11477
+ sourceSessionId: sessionId,
11478
+ repoPath,
11479
+ importance: 4,
11480
+ topicKey: normalizedKey
11481
+ }, zeroEmbedding);
11482
+ for (const oldId of supersededIds) {
11483
+ supersedeMemory(this.db, oldId, memory.id);
11484
+ }
11485
+ return { id: memory.id, superseded: supersededIds.length > 0 ? supersededIds : void 0 };
11486
+ }
11487
+ /**
11488
+ * Recall the latest memory for an exact topic key.
11489
+ * Does not use embeddings; returns null if no matching memory exists.
11490
+ */
11491
+ getByTopicKey(repoPath, topicKey) {
11492
+ if (!this.db) return null;
11493
+ const rows = findMemoriesByTopicKey(this.db, repoPath, topicKey);
11494
+ return rows[0] ?? null;
11495
+ }
11240
11496
  /**
11241
11497
  * Count high-signal memories created since the given timestamp.
11242
11498
  * Used for KIMI.md drift detection (Trigger A: session-start check).
@@ -12471,19 +12727,19 @@ var init_pricing = __esm({
12471
12727
 
12472
12728
  // src/usage-tracker.ts
12473
12729
  import { readFile as readFile16, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
12474
- import { homedir as homedir13 } from "os";
12475
- import { join as join21 } from "path";
12730
+ import { homedir as homedir14 } from "os";
12731
+ import { join as join22 } from "path";
12476
12732
  import { EventEmitter as EventEmitter2 } from "events";
12477
12733
  import { randomUUID } from "crypto";
12478
12734
  function usageDir2() {
12479
- const xdg = process.env.XDG_DATA_HOME || join21(homedir13(), ".local", "share");
12480
- return join21(xdg, "kimiflare");
12735
+ const xdg = process.env.XDG_DATA_HOME || join22(homedir14(), ".local", "share");
12736
+ return join22(xdg, "kimiflare");
12481
12737
  }
12482
12738
  function usagePath2() {
12483
- return join21(usageDir2(), "usage.json");
12739
+ return join22(usageDir2(), "usage.json");
12484
12740
  }
12485
12741
  function historyPath() {
12486
- return join21(usageDir2(), "history.jsonl");
12742
+ return join22(usageDir2(), "history.jsonl");
12487
12743
  }
12488
12744
  function today2() {
12489
12745
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -12930,8 +13186,8 @@ var init_permissions = __esm({
12930
13186
 
12931
13187
  // src/sdk/session.ts
12932
13188
  import { resolve as resolve6 } from "path";
12933
- import { homedir as homedir14 } from "os";
12934
- import { join as join22 } from "path";
13189
+ import { homedir as homedir15 } from "os";
13190
+ import { join as join23 } from "path";
12935
13191
  import { existsSync as existsSync3 } from "fs";
12936
13192
  async function createAgentSession(opts2) {
12937
13193
  const config2 = await resolveSdkConfig(opts2);
@@ -12946,7 +13202,7 @@ async function createAgentSession(opts2) {
12946
13202
  let memoryManager = null;
12947
13203
  const memoryEnabled = opts2.memoryEnabled ?? config2.memoryEnabled ?? false;
12948
13204
  if (memoryEnabled) {
12949
- const dbPath = config2.memoryDbPath ?? join22(homedir14(), ".local", "share", "kimiflare", "memory.db");
13205
+ const dbPath = config2.memoryDbPath ?? join23(homedir15(), ".local", "share", "kimiflare", "memory.db");
12950
13206
  memoryManager = new MemoryManager({
12951
13207
  dbPath,
12952
13208
  accountId: config2.accountId,
@@ -12977,7 +13233,7 @@ async function createAgentSession(opts2) {
12977
13233
  }
12978
13234
  let sessionFile;
12979
13235
  if (opts2.sessionId) {
12980
- const filePath = join22(sessionsDir(), `${opts2.sessionId}.json`);
13236
+ const filePath = join23(sessionsDir(), `${opts2.sessionId}.json`);
12981
13237
  try {
12982
13238
  sessionFile = await loadSession(filePath);
12983
13239
  } catch {
@@ -13390,7 +13646,7 @@ var init_session = __esm({
13390
13646
  }
13391
13647
  }
13392
13648
  });
13393
- if (existsSync3(join22(this.cwd, "KIMI.md"))) {
13649
+ if (existsSync3(join23(this.cwd, "KIMI.md"))) {
13394
13650
  this.messages[0] = {
13395
13651
  role: "system",
13396
13652
  content: buildSystemPrompt({
@@ -13667,7 +13923,7 @@ var init_repo_info = __esm({
13667
13923
  // src/agent/supervisor.ts
13668
13924
  import { readdir as readdir5, readFile as readFile17, stat as stat5 } from "fs/promises";
13669
13925
  import { createHash as createHash2 } from "crypto";
13670
- import { resolve as resolve7, join as join23 } from "path";
13926
+ import { resolve as resolve7, join as join24 } from "path";
13671
13927
  async function preReadFilesForWorkers(files, repoRoot, maxChars = DEFAULT_PRE_READ_MAX_CHARS) {
13672
13928
  const results = [];
13673
13929
  const filesRead = [];
@@ -13702,7 +13958,7 @@ ${results.join("\n\n")}`,
13702
13958
  }
13703
13959
  function getPreReadFilesFromMemory(cfg, repoRoot, limit = 10) {
13704
13960
  if (!cfg.memoryEnabled) return [];
13705
- const dbPath = cfg.memoryDbPath ?? join23(repoRoot, ".kimiflare", "memory.db");
13961
+ const dbPath = cfg.memoryDbPath ?? join24(repoRoot, ".kimiflare", "memory.db");
13706
13962
  try {
13707
13963
  const db = openMemoryDb(dbPath);
13708
13964
  const files = getTopRelatedFiles(db, repoRoot, limit);
@@ -15203,7 +15459,7 @@ var init_attach_mode = __esm({
15203
15459
  import { spawn as spawn4 } from "child_process";
15204
15460
  import { mkdtemp, readFile as readFile18, writeFile as writeFile11, rm } from "fs/promises";
15205
15461
  import { tmpdir as tmpdir3 } from "os";
15206
- import { join as join24 } from "path";
15462
+ import { join as join25 } from "path";
15207
15463
  import { randomBytes as randomBytes2 } from "crypto";
15208
15464
  async function cfApiFetch(accountId, apiToken, path, init) {
15209
15465
  const url = `${CF_API}/accounts/${encodeURIComponent(accountId)}${path}`;
@@ -15434,8 +15690,8 @@ ${wranglerInstall.stderr.slice(-600)}`,
15434
15690
  ok: true
15435
15691
  };
15436
15692
  yield { message: "Prerequisites ready", ok: true };
15437
- const tmpRoot = await mkdtemp(join24(tmpdir3(), "kimiflare-commute-"));
15438
- const repoDir = join24(tmpRoot, "kimiflare-commute");
15693
+ const tmpRoot = await mkdtemp(join25(tmpdir3(), "kimiflare-commute-"));
15694
+ const repoDir = join25(tmpRoot, "kimiflare-commute");
15439
15695
  yield { message: `Fetching worker source from GitHub (${COMMUTE_REPO})\u2026` };
15440
15696
  const clone = await runCmd("git", ["clone", "--depth", "1", "--branch", COMMUTE_BRANCH, COMMUTE_REPO, repoDir], { timeoutMs: 6e4 });
15441
15697
  if (clone.code !== 0) {
@@ -15444,8 +15700,8 @@ ${(clone.stderr || clone.stdout).slice(0, 400)}`, error: true };
15444
15700
  throw new Error("clone failed");
15445
15701
  }
15446
15702
  yield { message: "Source fetched from GitHub", ok: true };
15447
- const workerDir = join24(repoDir, "remote", "worker");
15448
- const wranglerToml = join24(workerDir, "wrangler.toml");
15703
+ const workerDir = join25(repoDir, "remote", "worker");
15704
+ const wranglerToml = join25(workerDir, "wrangler.toml");
15449
15705
  yield { message: "Installing Worker dependencies (npm install)\u2026" };
15450
15706
  const install = await runCmd("npm", ["install", "--no-audit", "--no-fund", "--loglevel=error"], {
15451
15707
  cwd: workerDir,
@@ -15787,7 +16043,7 @@ __export(app_helpers_exports, {
15787
16043
  });
15788
16044
  import { execSync as execSync3, spawn as spawn5 } from "child_process";
15789
16045
  import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
15790
- import { join as join25 } from "path";
16046
+ import { join as join26 } from "path";
15791
16047
  import { platform as platform3 } from "os";
15792
16048
  function buildFilePickerIgnoreList(cwd) {
15793
16049
  const hardcoded = [
@@ -15859,7 +16115,7 @@ function buildFilePickerIgnoreList(cwd) {
15859
16115
  ];
15860
16116
  const gitignorePatterns = [];
15861
16117
  try {
15862
- const gitignorePath = join25(cwd, ".gitignore");
16118
+ const gitignorePath = join26(cwd, ".gitignore");
15863
16119
  const stats = statSync4(gitignorePath);
15864
16120
  if (stats.size > MAX_GITIGNORE_SIZE) {
15865
16121
  return hardcoded;
@@ -16405,7 +16661,7 @@ var init_frontmatter = __esm({
16405
16661
 
16406
16662
  // src/skills/loader.ts
16407
16663
  import { readFile as readFile19, readdir as readdir6, stat as stat6 } from "fs/promises";
16408
- import { join as join26, extname } from "path";
16664
+ import { join as join27, extname } from "path";
16409
16665
  function normalizeManifest(raw, filePath) {
16410
16666
  const name = typeof raw.name === "string" ? raw.name : "";
16411
16667
  const description = typeof raw.description === "string" ? raw.description : "";
@@ -16441,7 +16697,7 @@ async function loadSkillsFromDir(dirPath) {
16441
16697
  const entries = await readdir6(dirPath);
16442
16698
  const files = [];
16443
16699
  for (const entry of entries) {
16444
- const full = join26(dirPath, entry);
16700
+ const full = join27(dirPath, entry);
16445
16701
  const s = await stat6(full);
16446
16702
  if (s.isFile() && extname(entry) === ".md") {
16447
16703
  files.push(full);
@@ -16470,7 +16726,7 @@ var init_loader = __esm({
16470
16726
 
16471
16727
  // src/skills/discovery.ts
16472
16728
  import { readdir as readdir7, stat as stat7, readFile as readFile20 } from "fs/promises";
16473
- import { join as join27, extname as extname2 } from "path";
16729
+ import { join as join28, extname as extname2 } from "path";
16474
16730
  async function dirExists(path) {
16475
16731
  try {
16476
16732
  const s = await stat7(path);
@@ -16494,15 +16750,15 @@ async function scanSkillDir(dirPath, source) {
16494
16750
  for (const entry of entries) {
16495
16751
  if (!entry.isFile()) continue;
16496
16752
  if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
16497
- files.push({ filePath: join27(dirPath, entry.name), source });
16753
+ files.push({ filePath: join28(dirPath, entry.name), source });
16498
16754
  }
16499
16755
  return files;
16500
16756
  }
16501
16757
  async function discoverSkills(cwd) {
16502
- const agentsSkills = await scanSkillDir(join27(cwd, ".agents", "skills"), "agents");
16503
- const agentsMd = await fileExists(join27(cwd, "AGENTS.md")) ? [{ filePath: join27(cwd, "AGENTS.md"), source: "agents-md" }] : [];
16504
- const githubSkills = await scanSkillDir(join27(cwd, ".github", "skills"), "github");
16505
- const kimiflareSkills = await scanSkillDir(join27(cwd, ".kimiflare", "skills"), "kimiflare");
16758
+ const agentsSkills = await scanSkillDir(join28(cwd, ".agents", "skills"), "agents");
16759
+ const agentsMd = await fileExists(join28(cwd, "AGENTS.md")) ? [{ filePath: join28(cwd, "AGENTS.md"), source: "agents-md" }] : [];
16760
+ const githubSkills = await scanSkillDir(join28(cwd, ".github", "skills"), "github");
16761
+ const kimiflareSkills = await scanSkillDir(join28(cwd, ".kimiflare", "skills"), "kimiflare");
16506
16762
  const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
16507
16763
  return ordered;
16508
16764
  }
@@ -16823,6 +17079,32 @@ var init_distill = __esm({
16823
17079
  }
16824
17080
  });
16825
17081
 
17082
+ // src/agent/plan-resolver.ts
17083
+ function resolvePlanForFresh(opts2) {
17084
+ const { mode, messages, sessionPlan, memoryManager, memoryEnabled, repoPath } = opts2;
17085
+ if (mode !== "plan") {
17086
+ return null;
17087
+ }
17088
+ if (sessionPlan) {
17089
+ return sessionPlan;
17090
+ }
17091
+ if (memoryEnabled && memoryManager) {
17092
+ const stored = memoryManager.getByTopicKey(repoPath, PLAN_MEMORY_TOPIC_KEY);
17093
+ if (stored?.content) {
17094
+ return stored.content;
17095
+ }
17096
+ }
17097
+ return distillSessionPlan(messages);
17098
+ }
17099
+ var PLAN_MEMORY_TOPIC_KEY;
17100
+ var init_plan_resolver = __esm({
17101
+ "src/agent/plan-resolver.ts"() {
17102
+ "use strict";
17103
+ init_distill();
17104
+ PLAN_MEMORY_TOPIC_KEY = "current_dev_plan";
17105
+ }
17106
+ });
17107
+
16826
17108
  // src/agent/continuation-summary.ts
16827
17109
  import { execSync as execSync4 } from "child_process";
16828
17110
  function extractFirstUserGoal(messages) {
@@ -18015,11 +18297,11 @@ var init_ai_gateway_api = __esm({
18015
18297
 
18016
18298
  // src/skills/manager.ts
18017
18299
  import { mkdir as mkdir10, writeFile as writeFile12, unlink as unlink2, readFile as readFile21 } from "fs/promises";
18018
- import { join as join28 } from "path";
18300
+ import { join as join29 } from "path";
18019
18301
  function getSkillDirs(cwd) {
18020
18302
  return {
18021
- projectDir: join28(cwd, ".kimiflare", "skills"),
18022
- globalDir: join28(process.env.HOME ?? "", ".config", "kimiflare", "skills")
18303
+ projectDir: join29(cwd, ".kimiflare", "skills"),
18304
+ globalDir: join29(process.env.HOME ?? "", ".config", "kimiflare", "skills")
18023
18305
  };
18024
18306
  }
18025
18307
  async function listAllSkills(cwd) {
@@ -18033,7 +18315,7 @@ async function listAllSkills(cwd) {
18033
18315
  async function createSkill(opts2) {
18034
18316
  const dirs = getSkillDirs(opts2.cwd);
18035
18317
  const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
18036
- const filepath = join28(dir, `${opts2.name}.md`);
18318
+ const filepath = join29(dir, `${opts2.name}.md`);
18037
18319
  const frontmatter = {
18038
18320
  name: opts2.name,
18039
18321
  enabled: true,
@@ -18153,14 +18435,14 @@ var init_frontmatter2 = __esm({
18153
18435
 
18154
18436
  // src/commands/loader.ts
18155
18437
  import { open, realpath as realpath2 } from "fs/promises";
18156
- import { homedir as homedir15 } from "os";
18157
- import { join as join29, relative as relative6, sep as sep2 } from "path";
18438
+ import { homedir as homedir16 } from "os";
18439
+ import { join as join30, relative as relative6, sep as sep2 } from "path";
18158
18440
  function projectCommandsDir(cwd = process.cwd()) {
18159
- return join29(cwd, ".kimiflare", "commands");
18441
+ return join30(cwd, ".kimiflare", "commands");
18160
18442
  }
18161
18443
  function globalCommandsDir() {
18162
- const xdg = process.env.XDG_CONFIG_HOME || join29(homedir15(), ".config");
18163
- return join29(xdg, "kimiflare", "commands");
18444
+ const xdg = process.env.XDG_CONFIG_HOME || join30(homedir16(), ".config");
18445
+ return join30(xdg, "kimiflare", "commands");
18164
18446
  }
18165
18447
  async function loadCustomCommands(cwd = process.cwd()) {
18166
18448
  const warnings = [];
@@ -18531,12 +18813,12 @@ var init_recommended = __esm({
18531
18813
 
18532
18814
  // src/init/context-generator.ts
18533
18815
  import { existsSync as existsSync5, statSync as statSync5 } from "fs";
18534
- import { join as join30 } from "path";
18816
+ import { join as join31 } from "path";
18535
18817
  function detectFlavor(cwd) {
18536
18818
  for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
18537
18819
  if (flavor === "generic") continue;
18538
18820
  for (const sig of signatures) {
18539
- const path = join30(cwd, sig);
18821
+ const path = join31(cwd, sig);
18540
18822
  if (sig.includes("*")) {
18541
18823
  try {
18542
18824
  const parts = sig.split("*");
@@ -18557,14 +18839,14 @@ function detectFlavor(cwd) {
18557
18839
  }
18558
18840
  function findFile(cwd, candidates) {
18559
18841
  for (const c of candidates) {
18560
- if (existsSync5(join30(cwd, c))) return c;
18842
+ if (existsSync5(join31(cwd, c))) return c;
18561
18843
  }
18562
18844
  return null;
18563
18845
  }
18564
18846
  function findSourceRoots(cwd) {
18565
18847
  const roots = [];
18566
18848
  for (const r of SOURCE_ROOT_CANDIDATES) {
18567
- const p = join30(cwd, r);
18849
+ const p = join31(cwd, r);
18568
18850
  try {
18569
18851
  const s = statSync5(p);
18570
18852
  if (s.isDirectory()) roots.push(r);
@@ -18575,9 +18857,9 @@ function findSourceRoots(cwd) {
18575
18857
  }
18576
18858
  function findCiConfig(cwd) {
18577
18859
  for (const c of CI_PATHS) {
18578
- if (existsSync5(join30(cwd, c))) {
18860
+ if (existsSync5(join31(cwd, c))) {
18579
18861
  try {
18580
- const s = statSync5(join30(cwd, c));
18862
+ const s = statSync5(join31(cwd, c));
18581
18863
  return s.isDirectory() ? c : c;
18582
18864
  } catch {
18583
18865
  }
@@ -18714,7 +18996,7 @@ function analyzeProject(cwd) {
18714
18996
  ciConfig: findCiConfig(cwd),
18715
18997
  readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
18716
18998
  sourceRoots: findSourceRoots(cwd),
18717
- hasGit: existsSync5(join30(cwd, ".git"))
18999
+ hasGit: existsSync5(join31(cwd, ".git"))
18718
19000
  };
18719
19001
  }
18720
19002
  function bashDiscoveryCommands(profile) {
@@ -18879,7 +19161,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
18879
19161
  }
18880
19162
  function buildInitPrompt(cwd) {
18881
19163
  const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
18882
- (n) => existsSync5(join30(cwd, n))
19164
+ (n) => existsSync5(join31(cwd, n))
18883
19165
  );
18884
19166
  const isRefresh = existingName !== void 0;
18885
19167
  const targetFilename = existingName ?? "KIMI.md";
@@ -18968,10 +19250,10 @@ __export(ui_mode_exports, {
18968
19250
  runUiMode: () => runUiMode
18969
19251
  });
18970
19252
  import { execSync as execSync6, spawn as spawn6 } from "child_process";
18971
- import { appendFileSync, mkdirSync as mkdirSync4, openSync } from "fs";
19253
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, openSync } from "fs";
18972
19254
  import { unlink as unlink4 } from "fs/promises";
18973
- import { join as join31 } from "path";
18974
- import { homedir as homedir16, platform as platform6 } from "os";
19255
+ import { join as join32 } from "path";
19256
+ import { homedir as homedir17, platform as platform6 } from "os";
18975
19257
  import { randomUUID as randomUUID2 } from "crypto";
18976
19258
  import { readFile as readFile22 } from "fs/promises";
18977
19259
  import QRCode from "qrcode";
@@ -18979,7 +19261,7 @@ function kimiLog(payload) {
18979
19261
  if (!KIMI_LOG_PATH) return;
18980
19262
  try {
18981
19263
  const line = JSON.stringify({ ts: Date.now() / 1e3, ...payload }) + "\n";
18982
- appendFileSync(KIMI_LOG_PATH, line);
19264
+ appendFileSync2(KIMI_LOG_PATH, line);
18983
19265
  } catch {
18984
19266
  }
18985
19267
  }
@@ -19011,13 +19293,13 @@ function gatewayFromOpts2(opts2) {
19011
19293
  }
19012
19294
  async function runUiMode(opts2) {
19013
19295
  await loadCamouflage();
19014
- const xdgConfig = process.env.XDG_CONFIG_HOME || join31(homedir16(), ".config");
19015
- const camoDbDir = join31(xdgConfig, "kimiflare");
19296
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join32(homedir17(), ".config");
19297
+ const camoDbDir = join32(xdgConfig, "kimiflare");
19016
19298
  try {
19017
- mkdirSync4(camoDbDir, { recursive: true });
19299
+ mkdirSync5(camoDbDir, { recursive: true });
19018
19300
  } catch {
19019
19301
  }
19020
- const camoDbPath = join31(camoDbDir, "camouflage-sessions.db");
19302
+ const camoDbPath = join32(camoDbDir, "camouflage-sessions.db");
19021
19303
  let cam;
19022
19304
  try {
19023
19305
  cam = await mount({
@@ -19073,7 +19355,7 @@ ${err instanceof Error ? err.message : err}`);
19073
19355
  let mcpInit = false;
19074
19356
  let lspInit = false;
19075
19357
  if (startupCfg?.memoryEnabled) {
19076
- const dbPath = startupCfg.memoryDbPath ?? join31(process.cwd(), ".kimiflare", "memory.db");
19358
+ const dbPath = startupCfg.memoryDbPath ?? join32(process.cwd(), ".kimiflare", "memory.db");
19077
19359
  memoryManager = new MemoryManager({
19078
19360
  dbPath,
19079
19361
  accountId: opts2.accountId,
@@ -19099,7 +19381,7 @@ ${err instanceof Error ? err.message : err}`);
19099
19381
  }
19100
19382
  });
19101
19383
  }
19102
- const skillDbPath = startupCfg?.memoryDbPath ?? join31(process.cwd(), ".kimiflare", "memory.db");
19384
+ const skillDbPath = startupCfg?.memoryDbPath ?? join32(process.cwd(), ".kimiflare", "memory.db");
19103
19385
  const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
19104
19386
  initSkillsSchema(skillDb);
19105
19387
  void indexSkills({
@@ -19909,6 +20191,10 @@ Executor opened PR: ${prUrl}` : plan });
19909
20191
  const plan = distillSessionPlan(messages);
19910
20192
  if (plan) {
19911
20193
  sessionPlan = plan;
20194
+ if (startupCfg?.memoryEnabled && memoryManager) {
20195
+ void memoryManager.rememberPlan(plan, process.cwd(), randomUUID2()).catch(() => {
20196
+ });
20197
+ }
19912
20198
  }
19913
20199
  }
19914
20200
  if (planOptionsRef.current && !currentController?.signal.aborted) {
@@ -21655,7 +21941,14 @@ changelog-image failed: ${err instanceof Error ? err.message : String(err)}
21655
21941
  return true;
21656
21942
  }
21657
21943
  void (async () => {
21658
- const summary = await generateContinuationSummary({
21944
+ const summary = currentMode === "plan" ? resolvePlanForFresh({
21945
+ mode: currentMode,
21946
+ messages,
21947
+ sessionPlan,
21948
+ memoryManager,
21949
+ memoryEnabled: startupCfg?.memoryEnabled,
21950
+ repoPath: process.cwd()
21951
+ }) : await generateContinuationSummary({
21659
21952
  messages,
21660
21953
  mode: currentMode,
21661
21954
  accountId: opts2.accountId,
@@ -21954,6 +22247,7 @@ var init_ui_mode = __esm({
21954
22247
  init_sessions();
21955
22248
  init_llm_summarize();
21956
22249
  init_distill();
22250
+ init_plan_resolver();
21957
22251
  init_continuation_summary();
21958
22252
  init_greetings();
21959
22253
  init_theme();
@@ -26731,10 +27025,10 @@ var init_wcag = __esm({
26731
27025
 
26732
27026
  // src/ui/theme-loader.ts
26733
27027
  import { readFile as readFile23, readdir as readdir9 } from "fs/promises";
26734
- import { join as join32 } from "path";
26735
- import { homedir as homedir17 } from "os";
27028
+ import { join as join33 } from "path";
27029
+ import { homedir as homedir18 } from "os";
26736
27030
  function projectThemesDir(cwd = process.cwd()) {
26737
- return join32(cwd, ".kimiflare", "themes");
27031
+ return join33(cwd, ".kimiflare", "themes");
26738
27032
  }
26739
27033
  function isHexColor(c) {
26740
27034
  return /^#[0-9a-fA-F]{6}$/.test(c);
@@ -26823,7 +27117,7 @@ async function loadThemesFromDir(dir, source) {
26823
27117
  return { themes, errors };
26824
27118
  }
26825
27119
  for (const file of files.filter((f) => f.endsWith(".json"))) {
26826
- const path = join32(dir, file);
27120
+ const path = join33(dir, file);
26827
27121
  let raw;
26828
27122
  try {
26829
27123
  raw = await readFile23(path, "utf-8");
@@ -26972,8 +27266,8 @@ var init_theme_loader = __esm({
26972
27266
  "use strict";
26973
27267
  init_wcag();
26974
27268
  init_theme();
26975
- USER_THEMES_DIR = join32(
26976
- process.env.XDG_CONFIG_HOME || join32(homedir17(), ".config"),
27269
+ USER_THEMES_DIR = join33(
27270
+ process.env.XDG_CONFIG_HOME || join33(homedir18(), ".config"),
26977
27271
  "kimiflare",
26978
27272
  "themes"
26979
27273
  );
@@ -31078,9 +31372,7 @@ function useSessionManager(deps) {
31078
31372
  const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
31079
31373
  if (results.length > 0) {
31080
31374
  const text = await manager.synthesizeRecalled(results);
31081
- const lastSystemIdx = d.messagesRef.current.findLastIndex((m) => m.role === "system");
31082
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : d.messagesRef.current.length;
31083
- d.messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
31375
+ injectRecalledMemoryOnce(d.messagesRef.current, text);
31084
31376
  }
31085
31377
  } catch {
31086
31378
  }
@@ -31197,6 +31489,7 @@ var init_use_session_manager = __esm({
31197
31489
  "use strict";
31198
31490
  init_sessions();
31199
31491
  init_session_state();
31492
+ init_recall_inject();
31200
31493
  init_usage_tracker();
31201
31494
  init_log_sink();
31202
31495
  init_app_helpers();
@@ -31350,14 +31643,14 @@ __export(tui_report_exports, {
31350
31643
  getCategoryReportText: () => getCategoryReportText
31351
31644
  });
31352
31645
  import { readFile as readFile24 } from "fs/promises";
31353
- import { join as join33 } from "path";
31354
- import { homedir as homedir18 } from "os";
31646
+ import { join as join34 } from "path";
31647
+ import { homedir as homedir19 } from "os";
31355
31648
  function usageDir3() {
31356
- const xdg = process.env.XDG_DATA_HOME || join33(homedir18(), ".local", "share");
31357
- return join33(xdg, "kimiflare");
31649
+ const xdg = process.env.XDG_DATA_HOME || join34(homedir19(), ".local", "share");
31650
+ return join34(xdg, "kimiflare");
31358
31651
  }
31359
31652
  function usagePath3() {
31360
- return join33(usageDir3(), "usage.json");
31653
+ return join34(usageDir3(), "usage.json");
31361
31654
  }
31362
31655
  function today3() {
31363
31656
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -31426,7 +31719,7 @@ var init_tui_report = __esm({
31426
31719
  });
31427
31720
 
31428
31721
  // src/ui/slash-commands.ts
31429
- import { join as join34 } from "path";
31722
+ import { join as join35 } from "path";
31430
31723
  import { unlink as unlink5 } from "fs/promises";
31431
31724
  import QRCode2 from "qrcode";
31432
31725
  function executeFreshStart(ctx, planText, overrideMode) {
@@ -31507,6 +31800,7 @@ var init_slash_commands = __esm({
31507
31800
  init_session_store();
31508
31801
  init_deploy();
31509
31802
  init_tui_auth();
31803
+ init_plan_resolver();
31510
31804
  init_continuation_summary();
31511
31805
  init_clipboard();
31512
31806
  handleExit = (ctx) => {
@@ -31559,7 +31853,14 @@ var init_slash_commands = __esm({
31559
31853
  ]);
31560
31854
  return true;
31561
31855
  }
31562
- const summary = await generateContinuationSummary({
31856
+ const summary = ctx.mode === "plan" ? resolvePlanForFresh({
31857
+ mode: ctx.mode,
31858
+ messages: ctx.messagesRef.current,
31859
+ sessionPlan: ctx.sessionPlanRef.current,
31860
+ memoryManager: ctx.memoryManagerRef.current,
31861
+ memoryEnabled: cfg?.memoryEnabled,
31862
+ repoPath: process.cwd()
31863
+ }) : await generateContinuationSummary({
31563
31864
  messages: ctx.messagesRef.current,
31564
31865
  mode: ctx.mode,
31565
31866
  accountId: cfg?.accountId ?? "",
@@ -32276,7 +32577,7 @@ ${lines.join("\n")}` }]);
32276
32577
  void (async () => {
32277
32578
  try {
32278
32579
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
32279
- const file = await loadSession(join34(sessionsDir3(), `${currentId}.json`));
32580
+ const file = await loadSession(join35(sessionsDir3(), `${currentId}.json`));
32280
32581
  const cps = file.checkpoints ?? [];
32281
32582
  if (cps.length === 0) {
32282
32583
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
@@ -32321,7 +32622,7 @@ ${lines.join("\n")}` }]);
32321
32622
  try {
32322
32623
  ctx.ensureSessionId();
32323
32624
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
32324
- const filePath = join34(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
32625
+ const filePath = join35(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
32325
32626
  await addCheckpoint(filePath, cp);
32326
32627
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
32327
32628
  } catch (e) {
@@ -32956,7 +33257,7 @@ project: ${projectSettingsPath(cwd)}`
32956
33257
 
32957
33258
  // src/init/run-init.ts
32958
33259
  import { existsSync as existsSync6 } from "fs";
32959
- import { join as join35 } from "path";
33260
+ import { join as join36 } from "path";
32960
33261
  async function runInit(deps) {
32961
33262
  const {
32962
33263
  cfg,
@@ -33159,7 +33460,7 @@ async function runInit(deps) {
33159
33460
  }
33160
33461
  }
33161
33462
  });
33162
- if (existsSync6(join35(cwd, "KIMI.md"))) {
33463
+ if (existsSync6(join36(cwd, "KIMI.md"))) {
33163
33464
  if (cacheStableRef.current) {
33164
33465
  messagesRef.current[1] = {
33165
33466
  role: "system",
@@ -33254,7 +33555,7 @@ var init_run_init = __esm({
33254
33555
 
33255
33556
  // src/ui/run-startup-tasks.ts
33256
33557
  import { existsSync as existsSync7 } from "fs";
33257
- import { join as join36 } from "path";
33558
+ import { join as join37 } from "path";
33258
33559
  function runStartupTasks(deps) {
33259
33560
  const {
33260
33561
  cfg,
@@ -33275,7 +33576,7 @@ function runStartupTasks(deps) {
33275
33576
  }
33276
33577
  });
33277
33578
  if (cfg.memoryEnabled) {
33278
- const dbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
33579
+ const dbPath = cfg.memoryDbPath ?? join37(process.cwd(), ".kimiflare", "memory.db");
33279
33580
  const manager = new MemoryManager({
33280
33581
  dbPath,
33281
33582
  accountId: cfg.accountId,
@@ -33309,7 +33610,7 @@ function runStartupTasks(deps) {
33309
33610
  });
33310
33611
  const cwd = process.cwd();
33311
33612
  sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
33312
- if (existsSync7(join36(cwd, "KIMI.md"))) {
33613
+ if (existsSync7(join37(cwd, "KIMI.md"))) {
33313
33614
  const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
33314
33615
  const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
33315
33616
  if (driftCount >= 5) {
@@ -33320,7 +33621,7 @@ function runStartupTasks(deps) {
33320
33621
  memoryManagerRef.current?.close();
33321
33622
  memoryManagerRef.current = null;
33322
33623
  }
33323
- const skillDbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
33624
+ const skillDbPath = cfg.memoryDbPath ?? join37(process.cwd(), ".kimiflare", "memory.db");
33324
33625
  const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
33325
33626
  initSkillsSchema(skillDb);
33326
33627
  void indexSkills({
@@ -33784,7 +34085,7 @@ __export(app_exports, {
33784
34085
  import React25, { useState as useState29, useRef as useRef7, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
33785
34086
  import { Box as Box42, Text as Text43, useApp, useInput as useInput21, render } from "ink";
33786
34087
  import { existsSync as existsSync8 } from "fs";
33787
- import { join as join37 } from "path";
34088
+ import { join as join38 } from "path";
33788
34089
  import { jsx as jsx44, jsxs as jsxs42 } from "react/jsx-runtime";
33789
34090
  function App({
33790
34091
  initialCfg,
@@ -34404,18 +34705,17 @@ ${wcagWarnings.join("\n")}` }
34404
34705
  const results = await manager.recall({ text: queryText, repoPath: cwd, limit: 5 });
34405
34706
  if (results.length > 0 && !signal.aborted) {
34406
34707
  const text = await manager.synthesizeRecalled(results);
34407
- const lastSystemIdx = result.newMessages.findLastIndex((m) => m.role === "system");
34408
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : result.newMessages.length;
34409
- result.newMessages.splice(insertIdx, 0, { role: "system", content: text });
34410
- setEvents((e) => [
34411
- ...e,
34412
- {
34413
- kind: "memory",
34414
- key: mkKey(),
34415
- text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
34416
- }
34417
- ]);
34418
- await saveSessionSafe();
34708
+ if (injectRecalledMemoryOnce(result.newMessages, text)) {
34709
+ setEvents((e) => [
34710
+ ...e,
34711
+ {
34712
+ kind: "memory",
34713
+ key: mkKey(),
34714
+ text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
34715
+ }
34716
+ ]);
34717
+ await saveSessionSafe();
34718
+ }
34419
34719
  }
34420
34720
  } catch {
34421
34721
  }
@@ -34999,7 +35299,14 @@ ${wcagWarnings.join("\n")}` }
34999
35299
  (picked) => {
35000
35300
  setShowPlanCompletePicker(false);
35001
35301
  if (!picked || picked === "continue") return;
35002
- const plan = sessionPlanRef.current ?? distillSessionPlan(messagesRef.current);
35302
+ const plan = resolvePlanForFresh({
35303
+ mode: "plan",
35304
+ messages: messagesRef.current,
35305
+ sessionPlan: sessionPlanRef.current,
35306
+ memoryManager: memoryManagerRef.current,
35307
+ memoryEnabled: cfg?.memoryEnabled,
35308
+ repoPath: process.cwd()
35309
+ });
35003
35310
  if (!plan) {
35004
35311
  setEvents((e) => [
35005
35312
  ...e,
@@ -35199,7 +35506,7 @@ ${wcagWarnings.join("\n")}` }
35199
35506
  }
35200
35507
  }
35201
35508
  turnCounterRef.current += 1;
35202
- if (turnCounterRef.current % 15 === 0 && existsSync8(join37(process.cwd(), "KIMI.md")) && !kimiMdStale) {
35509
+ if (turnCounterRef.current % 15 === 0 && existsSync8(join38(process.cwd(), "KIMI.md")) && !kimiMdStale) {
35203
35510
  setEvents((e) => [
35204
35511
  ...e,
35205
35512
  { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
@@ -35223,7 +35530,7 @@ ${wcagWarnings.join("\n")}` }
35223
35530
  };
35224
35531
  ensureSessionId();
35225
35532
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
35226
- const filePath = join37(sessionsDir3(), `${sessionIdRef.current}.json`);
35533
+ const filePath = join38(sessionsDir3(), `${sessionIdRef.current}.json`);
35227
35534
  await addCheckpoint(filePath, cp);
35228
35535
  }
35229
35536
  const summary = await generateContinuationSummary({
@@ -35679,18 +35986,17 @@ ${conflicts.join("\n")}` }
35679
35986
  const results = await manager.recall({ text: queryText, repoPath: cwd, limit: 5 });
35680
35987
  if (results.length > 0) {
35681
35988
  const text2 = await manager.synthesizeRecalled(results);
35682
- const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
35683
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
35684
- messagesRef.current.splice(insertIdx, 0, { role: "system", content: text2 });
35685
- setEvents((e) => [
35686
- ...e,
35687
- {
35688
- kind: "memory",
35689
- key: mkKey(),
35690
- text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
35691
- }
35692
- ]);
35693
- await saveSessionSafe();
35989
+ if (injectRecalledMemoryOnce(messagesRef.current, text2)) {
35990
+ setEvents((e) => [
35991
+ ...e,
35992
+ {
35993
+ kind: "memory",
35994
+ key: mkKey(),
35995
+ text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
35996
+ }
35997
+ ]);
35998
+ await saveSessionSafe();
35999
+ }
35694
36000
  }
35695
36001
  } catch {
35696
36002
  }
@@ -35703,6 +36009,10 @@ ${conflicts.join("\n")}` }
35703
36009
  const plan = distillSessionPlan(messagesRef.current);
35704
36010
  if (plan) {
35705
36011
  sessionPlanRef.current = plan;
36012
+ if (cfg?.memoryEnabled && memoryManagerRef.current && sessionIdRef.current) {
36013
+ void memoryManagerRef.current.rememberPlan(plan, process.cwd(), sessionIdRef.current).catch(() => {
36014
+ });
36015
+ }
35706
36016
  setShowPlanCompletePicker(true);
35707
36017
  }
35708
36018
  }
@@ -35872,7 +36182,7 @@ ${conflicts.join("\n")}` }
35872
36182
  onCommandDelete: handleCommandDelete2,
35873
36183
  lspServers: cfg?.lspServers ?? {},
35874
36184
  lspScope,
35875
- hasProjectDir: existsSync8(join37(process.cwd(), ".kimiflare")),
36185
+ hasProjectDir: existsSync8(join38(process.cwd(), ".kimiflare")),
35876
36186
  onLspSave: handleLspSave2,
35877
36187
  themes: themeList(),
35878
36188
  onPickTheme: handleThemePick,
@@ -36265,6 +36575,7 @@ var init_app = __esm({
36265
36575
  init_sessions();
36266
36576
  init_image();
36267
36577
  init_usage_tracker();
36578
+ init_recall_inject();
36268
36579
  init_loader2();
36269
36580
  init_renderer2();
36270
36581
  init_builtins();
@@ -36291,6 +36602,7 @@ var init_app = __esm({
36291
36602
  init_manager_init();
36292
36603
  init_run_compact();
36293
36604
  init_distill();
36605
+ init_plan_resolver();
36294
36606
  init_command_handlers();
36295
36607
  init_app_helpers();
36296
36608
  }