kimiflare 0.88.3 → 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,
@@ -2590,801 +3233,345 @@ var require_node_gyp_build = __commonJS({
2590
3233
  }
2591
3234
  function compareTuples(a, b) {
2592
3235
  return a.architectures.length - b.architectures.length;
2593
- }
2594
- function parseTags(file) {
2595
- var arr = file.split(".");
2596
- var extension = arr.pop();
2597
- var tags = { file, specificity: 0 };
2598
- if (extension !== "node") return;
2599
- for (var i = 0; i < arr.length; i++) {
2600
- var tag2 = arr[i];
2601
- if (tag2 === "node" || tag2 === "electron" || tag2 === "node-webkit") {
2602
- tags.runtime = tag2;
2603
- } else if (tag2 === "napi") {
2604
- tags.napi = true;
2605
- } else if (tag2.slice(0, 3) === "abi") {
2606
- tags.abi = tag2.slice(3);
2607
- } else if (tag2.slice(0, 2) === "uv") {
2608
- tags.uv = tag2.slice(2);
2609
- } else if (tag2.slice(0, 4) === "armv") {
2610
- tags.armv = tag2.slice(4);
2611
- } else if (tag2 === "glibc" || tag2 === "musl") {
2612
- tags.libc = tag2;
2613
- } else {
2614
- continue;
2615
- }
2616
- tags.specificity++;
2617
- }
2618
- return tags;
2619
- }
2620
- function matchTags(runtime2, abi2) {
2621
- return function(tags) {
2622
- if (tags == null) return false;
2623
- if (tags.runtime && tags.runtime !== runtime2 && !runtimeAgnostic(tags)) return false;
2624
- if (tags.abi && tags.abi !== abi2 && !tags.napi) return false;
2625
- if (tags.uv && tags.uv !== uv) return false;
2626
- if (tags.armv && tags.armv !== armv) return false;
2627
- if (tags.libc && tags.libc !== libc) return false;
2628
- return true;
2629
- };
2630
- }
2631
- function runtimeAgnostic(tags) {
2632
- return tags.runtime === "node" && tags.napi;
2633
- }
2634
- function compareTags(runtime2) {
2635
- return function(a, b) {
2636
- 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";
3236
+ }
3237
+ function parseTags(file) {
3238
+ var arr = file.split(".");
3239
+ var extension = arr.pop();
3240
+ var tags = { file, specificity: 0 };
3241
+ if (extension !== "node") return;
3242
+ for (var i = 0; i < arr.length; i++) {
3243
+ var tag2 = arr[i];
3244
+ if (tag2 === "node" || tag2 === "electron" || tag2 === "node-webkit") {
3245
+ tags.runtime = tag2;
3246
+ } else if (tag2 === "napi") {
3247
+ tags.napi = true;
3248
+ } else if (tag2.slice(0, 3) === "abi") {
3249
+ tags.abi = tag2.slice(3);
3250
+ } else if (tag2.slice(0, 2) === "uv") {
3251
+ tags.uv = tag2.slice(2);
3252
+ } else if (tag2.slice(0, 4) === "armv") {
3253
+ tags.armv = tag2.slice(4);
3254
+ } else if (tag2 === "glibc" || tag2 === "musl") {
3255
+ tags.libc = tag2;
3256
+ } else {
3257
+ continue;
3258
+ }
3259
+ tags.specificity++;
3234
3260
  }
3261
+ return tags;
3235
3262
  }
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)}`);
3263
+ function matchTags(runtime2, abi2) {
3264
+ return function(tags) {
3265
+ if (tags == null) return false;
3266
+ if (tags.runtime && tags.runtime !== runtime2 && !runtimeAgnostic(tags)) return false;
3267
+ if (tags.abi && tags.abi !== abi2 && !tags.napi) return false;
3268
+ if (tags.uv && tags.uv !== uv) return false;
3269
+ if (tags.armv && tags.armv !== armv) return false;
3270
+ if (tags.libc && tags.libc !== libc) return false;
3271
+ return true;
3272
+ };
3251
3273
  }
3252
- }
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);
3274
+ function runtimeAgnostic(tags) {
3275
+ return tags.runtime === "node" && tags.napi;
3276
+ }
3277
+ function compareTags(runtime2) {
3278
+ return function(a, b) {
3279
+ if (a.runtime !== b.runtime) {
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;
3265
3287
  }
3266
- }
3288
+ };
3289
+ }
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");
3267
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;
3268
3307
  }
3269
- return { artifacts, stateDelta };
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();
3319
+ }
3320
+ }
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");
@@ -12515,19 +12727,19 @@ var init_pricing = __esm({
12515
12727
 
12516
12728
  // src/usage-tracker.ts
12517
12729
  import { readFile as readFile16, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
12518
- import { homedir as homedir13 } from "os";
12519
- import { join as join21 } from "path";
12730
+ import { homedir as homedir14 } from "os";
12731
+ import { join as join22 } from "path";
12520
12732
  import { EventEmitter as EventEmitter2 } from "events";
12521
12733
  import { randomUUID } from "crypto";
12522
12734
  function usageDir2() {
12523
- const xdg = process.env.XDG_DATA_HOME || join21(homedir13(), ".local", "share");
12524
- return join21(xdg, "kimiflare");
12735
+ const xdg = process.env.XDG_DATA_HOME || join22(homedir14(), ".local", "share");
12736
+ return join22(xdg, "kimiflare");
12525
12737
  }
12526
12738
  function usagePath2() {
12527
- return join21(usageDir2(), "usage.json");
12739
+ return join22(usageDir2(), "usage.json");
12528
12740
  }
12529
12741
  function historyPath() {
12530
- return join21(usageDir2(), "history.jsonl");
12742
+ return join22(usageDir2(), "history.jsonl");
12531
12743
  }
12532
12744
  function today2() {
12533
12745
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -12974,8 +13186,8 @@ var init_permissions = __esm({
12974
13186
 
12975
13187
  // src/sdk/session.ts
12976
13188
  import { resolve as resolve6 } from "path";
12977
- import { homedir as homedir14 } from "os";
12978
- import { join as join22 } from "path";
13189
+ import { homedir as homedir15 } from "os";
13190
+ import { join as join23 } from "path";
12979
13191
  import { existsSync as existsSync3 } from "fs";
12980
13192
  async function createAgentSession(opts2) {
12981
13193
  const config2 = await resolveSdkConfig(opts2);
@@ -12990,7 +13202,7 @@ async function createAgentSession(opts2) {
12990
13202
  let memoryManager = null;
12991
13203
  const memoryEnabled = opts2.memoryEnabled ?? config2.memoryEnabled ?? false;
12992
13204
  if (memoryEnabled) {
12993
- const dbPath = config2.memoryDbPath ?? join22(homedir14(), ".local", "share", "kimiflare", "memory.db");
13205
+ const dbPath = config2.memoryDbPath ?? join23(homedir15(), ".local", "share", "kimiflare", "memory.db");
12994
13206
  memoryManager = new MemoryManager({
12995
13207
  dbPath,
12996
13208
  accountId: config2.accountId,
@@ -13021,7 +13233,7 @@ async function createAgentSession(opts2) {
13021
13233
  }
13022
13234
  let sessionFile;
13023
13235
  if (opts2.sessionId) {
13024
- const filePath = join22(sessionsDir(), `${opts2.sessionId}.json`);
13236
+ const filePath = join23(sessionsDir(), `${opts2.sessionId}.json`);
13025
13237
  try {
13026
13238
  sessionFile = await loadSession(filePath);
13027
13239
  } catch {
@@ -13434,7 +13646,7 @@ var init_session = __esm({
13434
13646
  }
13435
13647
  }
13436
13648
  });
13437
- if (existsSync3(join22(this.cwd, "KIMI.md"))) {
13649
+ if (existsSync3(join23(this.cwd, "KIMI.md"))) {
13438
13650
  this.messages[0] = {
13439
13651
  role: "system",
13440
13652
  content: buildSystemPrompt({
@@ -13711,7 +13923,7 @@ var init_repo_info = __esm({
13711
13923
  // src/agent/supervisor.ts
13712
13924
  import { readdir as readdir5, readFile as readFile17, stat as stat5 } from "fs/promises";
13713
13925
  import { createHash as createHash2 } from "crypto";
13714
- import { resolve as resolve7, join as join23 } from "path";
13926
+ import { resolve as resolve7, join as join24 } from "path";
13715
13927
  async function preReadFilesForWorkers(files, repoRoot, maxChars = DEFAULT_PRE_READ_MAX_CHARS) {
13716
13928
  const results = [];
13717
13929
  const filesRead = [];
@@ -13746,7 +13958,7 @@ ${results.join("\n\n")}`,
13746
13958
  }
13747
13959
  function getPreReadFilesFromMemory(cfg, repoRoot, limit = 10) {
13748
13960
  if (!cfg.memoryEnabled) return [];
13749
- const dbPath = cfg.memoryDbPath ?? join23(repoRoot, ".kimiflare", "memory.db");
13961
+ const dbPath = cfg.memoryDbPath ?? join24(repoRoot, ".kimiflare", "memory.db");
13750
13962
  try {
13751
13963
  const db = openMemoryDb(dbPath);
13752
13964
  const files = getTopRelatedFiles(db, repoRoot, limit);
@@ -15247,7 +15459,7 @@ var init_attach_mode = __esm({
15247
15459
  import { spawn as spawn4 } from "child_process";
15248
15460
  import { mkdtemp, readFile as readFile18, writeFile as writeFile11, rm } from "fs/promises";
15249
15461
  import { tmpdir as tmpdir3 } from "os";
15250
- import { join as join24 } from "path";
15462
+ import { join as join25 } from "path";
15251
15463
  import { randomBytes as randomBytes2 } from "crypto";
15252
15464
  async function cfApiFetch(accountId, apiToken, path, init) {
15253
15465
  const url = `${CF_API}/accounts/${encodeURIComponent(accountId)}${path}`;
@@ -15478,8 +15690,8 @@ ${wranglerInstall.stderr.slice(-600)}`,
15478
15690
  ok: true
15479
15691
  };
15480
15692
  yield { message: "Prerequisites ready", ok: true };
15481
- const tmpRoot = await mkdtemp(join24(tmpdir3(), "kimiflare-commute-"));
15482
- const repoDir = join24(tmpRoot, "kimiflare-commute");
15693
+ const tmpRoot = await mkdtemp(join25(tmpdir3(), "kimiflare-commute-"));
15694
+ const repoDir = join25(tmpRoot, "kimiflare-commute");
15483
15695
  yield { message: `Fetching worker source from GitHub (${COMMUTE_REPO})\u2026` };
15484
15696
  const clone = await runCmd("git", ["clone", "--depth", "1", "--branch", COMMUTE_BRANCH, COMMUTE_REPO, repoDir], { timeoutMs: 6e4 });
15485
15697
  if (clone.code !== 0) {
@@ -15488,8 +15700,8 @@ ${(clone.stderr || clone.stdout).slice(0, 400)}`, error: true };
15488
15700
  throw new Error("clone failed");
15489
15701
  }
15490
15702
  yield { message: "Source fetched from GitHub", ok: true };
15491
- const workerDir = join24(repoDir, "remote", "worker");
15492
- const wranglerToml = join24(workerDir, "wrangler.toml");
15703
+ const workerDir = join25(repoDir, "remote", "worker");
15704
+ const wranglerToml = join25(workerDir, "wrangler.toml");
15493
15705
  yield { message: "Installing Worker dependencies (npm install)\u2026" };
15494
15706
  const install = await runCmd("npm", ["install", "--no-audit", "--no-fund", "--loglevel=error"], {
15495
15707
  cwd: workerDir,
@@ -15831,7 +16043,7 @@ __export(app_helpers_exports, {
15831
16043
  });
15832
16044
  import { execSync as execSync3, spawn as spawn5 } from "child_process";
15833
16045
  import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
15834
- import { join as join25 } from "path";
16046
+ import { join as join26 } from "path";
15835
16047
  import { platform as platform3 } from "os";
15836
16048
  function buildFilePickerIgnoreList(cwd) {
15837
16049
  const hardcoded = [
@@ -15903,7 +16115,7 @@ function buildFilePickerIgnoreList(cwd) {
15903
16115
  ];
15904
16116
  const gitignorePatterns = [];
15905
16117
  try {
15906
- const gitignorePath = join25(cwd, ".gitignore");
16118
+ const gitignorePath = join26(cwd, ".gitignore");
15907
16119
  const stats = statSync4(gitignorePath);
15908
16120
  if (stats.size > MAX_GITIGNORE_SIZE) {
15909
16121
  return hardcoded;
@@ -16449,7 +16661,7 @@ var init_frontmatter = __esm({
16449
16661
 
16450
16662
  // src/skills/loader.ts
16451
16663
  import { readFile as readFile19, readdir as readdir6, stat as stat6 } from "fs/promises";
16452
- import { join as join26, extname } from "path";
16664
+ import { join as join27, extname } from "path";
16453
16665
  function normalizeManifest(raw, filePath) {
16454
16666
  const name = typeof raw.name === "string" ? raw.name : "";
16455
16667
  const description = typeof raw.description === "string" ? raw.description : "";
@@ -16485,7 +16697,7 @@ async function loadSkillsFromDir(dirPath) {
16485
16697
  const entries = await readdir6(dirPath);
16486
16698
  const files = [];
16487
16699
  for (const entry of entries) {
16488
- const full = join26(dirPath, entry);
16700
+ const full = join27(dirPath, entry);
16489
16701
  const s = await stat6(full);
16490
16702
  if (s.isFile() && extname(entry) === ".md") {
16491
16703
  files.push(full);
@@ -16514,7 +16726,7 @@ var init_loader = __esm({
16514
16726
 
16515
16727
  // src/skills/discovery.ts
16516
16728
  import { readdir as readdir7, stat as stat7, readFile as readFile20 } from "fs/promises";
16517
- import { join as join27, extname as extname2 } from "path";
16729
+ import { join as join28, extname as extname2 } from "path";
16518
16730
  async function dirExists(path) {
16519
16731
  try {
16520
16732
  const s = await stat7(path);
@@ -16538,15 +16750,15 @@ async function scanSkillDir(dirPath, source) {
16538
16750
  for (const entry of entries) {
16539
16751
  if (!entry.isFile()) continue;
16540
16752
  if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
16541
- files.push({ filePath: join27(dirPath, entry.name), source });
16753
+ files.push({ filePath: join28(dirPath, entry.name), source });
16542
16754
  }
16543
16755
  return files;
16544
16756
  }
16545
16757
  async function discoverSkills(cwd) {
16546
- const agentsSkills = await scanSkillDir(join27(cwd, ".agents", "skills"), "agents");
16547
- const agentsMd = await fileExists(join27(cwd, "AGENTS.md")) ? [{ filePath: join27(cwd, "AGENTS.md"), source: "agents-md" }] : [];
16548
- const githubSkills = await scanSkillDir(join27(cwd, ".github", "skills"), "github");
16549
- 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");
16550
16762
  const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
16551
16763
  return ordered;
16552
16764
  }
@@ -18085,11 +18297,11 @@ var init_ai_gateway_api = __esm({
18085
18297
 
18086
18298
  // src/skills/manager.ts
18087
18299
  import { mkdir as mkdir10, writeFile as writeFile12, unlink as unlink2, readFile as readFile21 } from "fs/promises";
18088
- import { join as join28 } from "path";
18300
+ import { join as join29 } from "path";
18089
18301
  function getSkillDirs(cwd) {
18090
18302
  return {
18091
- projectDir: join28(cwd, ".kimiflare", "skills"),
18092
- globalDir: join28(process.env.HOME ?? "", ".config", "kimiflare", "skills")
18303
+ projectDir: join29(cwd, ".kimiflare", "skills"),
18304
+ globalDir: join29(process.env.HOME ?? "", ".config", "kimiflare", "skills")
18093
18305
  };
18094
18306
  }
18095
18307
  async function listAllSkills(cwd) {
@@ -18103,7 +18315,7 @@ async function listAllSkills(cwd) {
18103
18315
  async function createSkill(opts2) {
18104
18316
  const dirs = getSkillDirs(opts2.cwd);
18105
18317
  const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
18106
- const filepath = join28(dir, `${opts2.name}.md`);
18318
+ const filepath = join29(dir, `${opts2.name}.md`);
18107
18319
  const frontmatter = {
18108
18320
  name: opts2.name,
18109
18321
  enabled: true,
@@ -18223,14 +18435,14 @@ var init_frontmatter2 = __esm({
18223
18435
 
18224
18436
  // src/commands/loader.ts
18225
18437
  import { open, realpath as realpath2 } from "fs/promises";
18226
- import { homedir as homedir15 } from "os";
18227
- 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";
18228
18440
  function projectCommandsDir(cwd = process.cwd()) {
18229
- return join29(cwd, ".kimiflare", "commands");
18441
+ return join30(cwd, ".kimiflare", "commands");
18230
18442
  }
18231
18443
  function globalCommandsDir() {
18232
- const xdg = process.env.XDG_CONFIG_HOME || join29(homedir15(), ".config");
18233
- return join29(xdg, "kimiflare", "commands");
18444
+ const xdg = process.env.XDG_CONFIG_HOME || join30(homedir16(), ".config");
18445
+ return join30(xdg, "kimiflare", "commands");
18234
18446
  }
18235
18447
  async function loadCustomCommands(cwd = process.cwd()) {
18236
18448
  const warnings = [];
@@ -18601,12 +18813,12 @@ var init_recommended = __esm({
18601
18813
 
18602
18814
  // src/init/context-generator.ts
18603
18815
  import { existsSync as existsSync5, statSync as statSync5 } from "fs";
18604
- import { join as join30 } from "path";
18816
+ import { join as join31 } from "path";
18605
18817
  function detectFlavor(cwd) {
18606
18818
  for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
18607
18819
  if (flavor === "generic") continue;
18608
18820
  for (const sig of signatures) {
18609
- const path = join30(cwd, sig);
18821
+ const path = join31(cwd, sig);
18610
18822
  if (sig.includes("*")) {
18611
18823
  try {
18612
18824
  const parts = sig.split("*");
@@ -18627,14 +18839,14 @@ function detectFlavor(cwd) {
18627
18839
  }
18628
18840
  function findFile(cwd, candidates) {
18629
18841
  for (const c of candidates) {
18630
- if (existsSync5(join30(cwd, c))) return c;
18842
+ if (existsSync5(join31(cwd, c))) return c;
18631
18843
  }
18632
18844
  return null;
18633
18845
  }
18634
18846
  function findSourceRoots(cwd) {
18635
18847
  const roots = [];
18636
18848
  for (const r of SOURCE_ROOT_CANDIDATES) {
18637
- const p = join30(cwd, r);
18849
+ const p = join31(cwd, r);
18638
18850
  try {
18639
18851
  const s = statSync5(p);
18640
18852
  if (s.isDirectory()) roots.push(r);
@@ -18645,9 +18857,9 @@ function findSourceRoots(cwd) {
18645
18857
  }
18646
18858
  function findCiConfig(cwd) {
18647
18859
  for (const c of CI_PATHS) {
18648
- if (existsSync5(join30(cwd, c))) {
18860
+ if (existsSync5(join31(cwd, c))) {
18649
18861
  try {
18650
- const s = statSync5(join30(cwd, c));
18862
+ const s = statSync5(join31(cwd, c));
18651
18863
  return s.isDirectory() ? c : c;
18652
18864
  } catch {
18653
18865
  }
@@ -18784,7 +18996,7 @@ function analyzeProject(cwd) {
18784
18996
  ciConfig: findCiConfig(cwd),
18785
18997
  readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
18786
18998
  sourceRoots: findSourceRoots(cwd),
18787
- hasGit: existsSync5(join30(cwd, ".git"))
18999
+ hasGit: existsSync5(join31(cwd, ".git"))
18788
19000
  };
18789
19001
  }
18790
19002
  function bashDiscoveryCommands(profile) {
@@ -18949,7 +19161,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
18949
19161
  }
18950
19162
  function buildInitPrompt(cwd) {
18951
19163
  const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
18952
- (n) => existsSync5(join30(cwd, n))
19164
+ (n) => existsSync5(join31(cwd, n))
18953
19165
  );
18954
19166
  const isRefresh = existingName !== void 0;
18955
19167
  const targetFilename = existingName ?? "KIMI.md";
@@ -19038,10 +19250,10 @@ __export(ui_mode_exports, {
19038
19250
  runUiMode: () => runUiMode
19039
19251
  });
19040
19252
  import { execSync as execSync6, spawn as spawn6 } from "child_process";
19041
- import { appendFileSync, mkdirSync as mkdirSync4, openSync } from "fs";
19253
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, openSync } from "fs";
19042
19254
  import { unlink as unlink4 } from "fs/promises";
19043
- import { join as join31 } from "path";
19044
- 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";
19045
19257
  import { randomUUID as randomUUID2 } from "crypto";
19046
19258
  import { readFile as readFile22 } from "fs/promises";
19047
19259
  import QRCode from "qrcode";
@@ -19049,7 +19261,7 @@ function kimiLog(payload) {
19049
19261
  if (!KIMI_LOG_PATH) return;
19050
19262
  try {
19051
19263
  const line = JSON.stringify({ ts: Date.now() / 1e3, ...payload }) + "\n";
19052
- appendFileSync(KIMI_LOG_PATH, line);
19264
+ appendFileSync2(KIMI_LOG_PATH, line);
19053
19265
  } catch {
19054
19266
  }
19055
19267
  }
@@ -19081,13 +19293,13 @@ function gatewayFromOpts2(opts2) {
19081
19293
  }
19082
19294
  async function runUiMode(opts2) {
19083
19295
  await loadCamouflage();
19084
- const xdgConfig = process.env.XDG_CONFIG_HOME || join31(homedir16(), ".config");
19085
- const camoDbDir = join31(xdgConfig, "kimiflare");
19296
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join32(homedir17(), ".config");
19297
+ const camoDbDir = join32(xdgConfig, "kimiflare");
19086
19298
  try {
19087
- mkdirSync4(camoDbDir, { recursive: true });
19299
+ mkdirSync5(camoDbDir, { recursive: true });
19088
19300
  } catch {
19089
19301
  }
19090
- const camoDbPath = join31(camoDbDir, "camouflage-sessions.db");
19302
+ const camoDbPath = join32(camoDbDir, "camouflage-sessions.db");
19091
19303
  let cam;
19092
19304
  try {
19093
19305
  cam = await mount({
@@ -19143,7 +19355,7 @@ ${err instanceof Error ? err.message : err}`);
19143
19355
  let mcpInit = false;
19144
19356
  let lspInit = false;
19145
19357
  if (startupCfg?.memoryEnabled) {
19146
- const dbPath = startupCfg.memoryDbPath ?? join31(process.cwd(), ".kimiflare", "memory.db");
19358
+ const dbPath = startupCfg.memoryDbPath ?? join32(process.cwd(), ".kimiflare", "memory.db");
19147
19359
  memoryManager = new MemoryManager({
19148
19360
  dbPath,
19149
19361
  accountId: opts2.accountId,
@@ -19169,7 +19381,7 @@ ${err instanceof Error ? err.message : err}`);
19169
19381
  }
19170
19382
  });
19171
19383
  }
19172
- const skillDbPath = startupCfg?.memoryDbPath ?? join31(process.cwd(), ".kimiflare", "memory.db");
19384
+ const skillDbPath = startupCfg?.memoryDbPath ?? join32(process.cwd(), ".kimiflare", "memory.db");
19173
19385
  const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
19174
19386
  initSkillsSchema(skillDb);
19175
19387
  void indexSkills({
@@ -26813,10 +27025,10 @@ var init_wcag = __esm({
26813
27025
 
26814
27026
  // src/ui/theme-loader.ts
26815
27027
  import { readFile as readFile23, readdir as readdir9 } from "fs/promises";
26816
- import { join as join32 } from "path";
26817
- import { homedir as homedir17 } from "os";
27028
+ import { join as join33 } from "path";
27029
+ import { homedir as homedir18 } from "os";
26818
27030
  function projectThemesDir(cwd = process.cwd()) {
26819
- return join32(cwd, ".kimiflare", "themes");
27031
+ return join33(cwd, ".kimiflare", "themes");
26820
27032
  }
26821
27033
  function isHexColor(c) {
26822
27034
  return /^#[0-9a-fA-F]{6}$/.test(c);
@@ -26905,7 +27117,7 @@ async function loadThemesFromDir(dir, source) {
26905
27117
  return { themes, errors };
26906
27118
  }
26907
27119
  for (const file of files.filter((f) => f.endsWith(".json"))) {
26908
- const path = join32(dir, file);
27120
+ const path = join33(dir, file);
26909
27121
  let raw;
26910
27122
  try {
26911
27123
  raw = await readFile23(path, "utf-8");
@@ -27054,8 +27266,8 @@ var init_theme_loader = __esm({
27054
27266
  "use strict";
27055
27267
  init_wcag();
27056
27268
  init_theme();
27057
- USER_THEMES_DIR = join32(
27058
- process.env.XDG_CONFIG_HOME || join32(homedir17(), ".config"),
27269
+ USER_THEMES_DIR = join33(
27270
+ process.env.XDG_CONFIG_HOME || join33(homedir18(), ".config"),
27059
27271
  "kimiflare",
27060
27272
  "themes"
27061
27273
  );
@@ -31160,9 +31372,7 @@ function useSessionManager(deps) {
31160
31372
  const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
31161
31373
  if (results.length > 0) {
31162
31374
  const text = await manager.synthesizeRecalled(results);
31163
- const lastSystemIdx = d.messagesRef.current.findLastIndex((m) => m.role === "system");
31164
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : d.messagesRef.current.length;
31165
- d.messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
31375
+ injectRecalledMemoryOnce(d.messagesRef.current, text);
31166
31376
  }
31167
31377
  } catch {
31168
31378
  }
@@ -31279,6 +31489,7 @@ var init_use_session_manager = __esm({
31279
31489
  "use strict";
31280
31490
  init_sessions();
31281
31491
  init_session_state();
31492
+ init_recall_inject();
31282
31493
  init_usage_tracker();
31283
31494
  init_log_sink();
31284
31495
  init_app_helpers();
@@ -31432,14 +31643,14 @@ __export(tui_report_exports, {
31432
31643
  getCategoryReportText: () => getCategoryReportText
31433
31644
  });
31434
31645
  import { readFile as readFile24 } from "fs/promises";
31435
- import { join as join33 } from "path";
31436
- import { homedir as homedir18 } from "os";
31646
+ import { join as join34 } from "path";
31647
+ import { homedir as homedir19 } from "os";
31437
31648
  function usageDir3() {
31438
- const xdg = process.env.XDG_DATA_HOME || join33(homedir18(), ".local", "share");
31439
- return join33(xdg, "kimiflare");
31649
+ const xdg = process.env.XDG_DATA_HOME || join34(homedir19(), ".local", "share");
31650
+ return join34(xdg, "kimiflare");
31440
31651
  }
31441
31652
  function usagePath3() {
31442
- return join33(usageDir3(), "usage.json");
31653
+ return join34(usageDir3(), "usage.json");
31443
31654
  }
31444
31655
  function today3() {
31445
31656
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -31508,7 +31719,7 @@ var init_tui_report = __esm({
31508
31719
  });
31509
31720
 
31510
31721
  // src/ui/slash-commands.ts
31511
- import { join as join34 } from "path";
31722
+ import { join as join35 } from "path";
31512
31723
  import { unlink as unlink5 } from "fs/promises";
31513
31724
  import QRCode2 from "qrcode";
31514
31725
  function executeFreshStart(ctx, planText, overrideMode) {
@@ -32366,7 +32577,7 @@ ${lines.join("\n")}` }]);
32366
32577
  void (async () => {
32367
32578
  try {
32368
32579
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
32369
- const file = await loadSession(join34(sessionsDir3(), `${currentId}.json`));
32580
+ const file = await loadSession(join35(sessionsDir3(), `${currentId}.json`));
32370
32581
  const cps = file.checkpoints ?? [];
32371
32582
  if (cps.length === 0) {
32372
32583
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
@@ -32411,7 +32622,7 @@ ${lines.join("\n")}` }]);
32411
32622
  try {
32412
32623
  ctx.ensureSessionId();
32413
32624
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
32414
- const filePath = join34(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
32625
+ const filePath = join35(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
32415
32626
  await addCheckpoint(filePath, cp);
32416
32627
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
32417
32628
  } catch (e) {
@@ -33046,7 +33257,7 @@ project: ${projectSettingsPath(cwd)}`
33046
33257
 
33047
33258
  // src/init/run-init.ts
33048
33259
  import { existsSync as existsSync6 } from "fs";
33049
- import { join as join35 } from "path";
33260
+ import { join as join36 } from "path";
33050
33261
  async function runInit(deps) {
33051
33262
  const {
33052
33263
  cfg,
@@ -33249,7 +33460,7 @@ async function runInit(deps) {
33249
33460
  }
33250
33461
  }
33251
33462
  });
33252
- if (existsSync6(join35(cwd, "KIMI.md"))) {
33463
+ if (existsSync6(join36(cwd, "KIMI.md"))) {
33253
33464
  if (cacheStableRef.current) {
33254
33465
  messagesRef.current[1] = {
33255
33466
  role: "system",
@@ -33344,7 +33555,7 @@ var init_run_init = __esm({
33344
33555
 
33345
33556
  // src/ui/run-startup-tasks.ts
33346
33557
  import { existsSync as existsSync7 } from "fs";
33347
- import { join as join36 } from "path";
33558
+ import { join as join37 } from "path";
33348
33559
  function runStartupTasks(deps) {
33349
33560
  const {
33350
33561
  cfg,
@@ -33365,7 +33576,7 @@ function runStartupTasks(deps) {
33365
33576
  }
33366
33577
  });
33367
33578
  if (cfg.memoryEnabled) {
33368
- const dbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
33579
+ const dbPath = cfg.memoryDbPath ?? join37(process.cwd(), ".kimiflare", "memory.db");
33369
33580
  const manager = new MemoryManager({
33370
33581
  dbPath,
33371
33582
  accountId: cfg.accountId,
@@ -33399,7 +33610,7 @@ function runStartupTasks(deps) {
33399
33610
  });
33400
33611
  const cwd = process.cwd();
33401
33612
  sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
33402
- if (existsSync7(join36(cwd, "KIMI.md"))) {
33613
+ if (existsSync7(join37(cwd, "KIMI.md"))) {
33403
33614
  const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
33404
33615
  const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
33405
33616
  if (driftCount >= 5) {
@@ -33410,7 +33621,7 @@ function runStartupTasks(deps) {
33410
33621
  memoryManagerRef.current?.close();
33411
33622
  memoryManagerRef.current = null;
33412
33623
  }
33413
- const skillDbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
33624
+ const skillDbPath = cfg.memoryDbPath ?? join37(process.cwd(), ".kimiflare", "memory.db");
33414
33625
  const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
33415
33626
  initSkillsSchema(skillDb);
33416
33627
  void indexSkills({
@@ -33874,7 +34085,7 @@ __export(app_exports, {
33874
34085
  import React25, { useState as useState29, useRef as useRef7, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
33875
34086
  import { Box as Box42, Text as Text43, useApp, useInput as useInput21, render } from "ink";
33876
34087
  import { existsSync as existsSync8 } from "fs";
33877
- import { join as join37 } from "path";
34088
+ import { join as join38 } from "path";
33878
34089
  import { jsx as jsx44, jsxs as jsxs42 } from "react/jsx-runtime";
33879
34090
  function App({
33880
34091
  initialCfg,
@@ -34494,18 +34705,17 @@ ${wcagWarnings.join("\n")}` }
34494
34705
  const results = await manager.recall({ text: queryText, repoPath: cwd, limit: 5 });
34495
34706
  if (results.length > 0 && !signal.aborted) {
34496
34707
  const text = await manager.synthesizeRecalled(results);
34497
- const lastSystemIdx = result.newMessages.findLastIndex((m) => m.role === "system");
34498
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : result.newMessages.length;
34499
- result.newMessages.splice(insertIdx, 0, { role: "system", content: text });
34500
- setEvents((e) => [
34501
- ...e,
34502
- {
34503
- kind: "memory",
34504
- key: mkKey(),
34505
- text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
34506
- }
34507
- ]);
34508
- 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
+ }
34509
34719
  }
34510
34720
  } catch {
34511
34721
  }
@@ -35296,7 +35506,7 @@ ${wcagWarnings.join("\n")}` }
35296
35506
  }
35297
35507
  }
35298
35508
  turnCounterRef.current += 1;
35299
- 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) {
35300
35510
  setEvents((e) => [
35301
35511
  ...e,
35302
35512
  { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
@@ -35320,7 +35530,7 @@ ${wcagWarnings.join("\n")}` }
35320
35530
  };
35321
35531
  ensureSessionId();
35322
35532
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
35323
- const filePath = join37(sessionsDir3(), `${sessionIdRef.current}.json`);
35533
+ const filePath = join38(sessionsDir3(), `${sessionIdRef.current}.json`);
35324
35534
  await addCheckpoint(filePath, cp);
35325
35535
  }
35326
35536
  const summary = await generateContinuationSummary({
@@ -35776,18 +35986,17 @@ ${conflicts.join("\n")}` }
35776
35986
  const results = await manager.recall({ text: queryText, repoPath: cwd, limit: 5 });
35777
35987
  if (results.length > 0) {
35778
35988
  const text2 = await manager.synthesizeRecalled(results);
35779
- const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
35780
- const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
35781
- messagesRef.current.splice(insertIdx, 0, { role: "system", content: text2 });
35782
- setEvents((e) => [
35783
- ...e,
35784
- {
35785
- kind: "memory",
35786
- key: mkKey(),
35787
- text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
35788
- }
35789
- ]);
35790
- 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
+ }
35791
36000
  }
35792
36001
  } catch {
35793
36002
  }
@@ -35973,7 +36182,7 @@ ${conflicts.join("\n")}` }
35973
36182
  onCommandDelete: handleCommandDelete2,
35974
36183
  lspServers: cfg?.lspServers ?? {},
35975
36184
  lspScope,
35976
- hasProjectDir: existsSync8(join37(process.cwd(), ".kimiflare")),
36185
+ hasProjectDir: existsSync8(join38(process.cwd(), ".kimiflare")),
35977
36186
  onLspSave: handleLspSave2,
35978
36187
  themes: themeList(),
35979
36188
  onPickTheme: handleThemePick,
@@ -36366,6 +36575,7 @@ var init_app = __esm({
36366
36575
  init_sessions();
36367
36576
  init_image();
36368
36577
  init_usage_tracker();
36578
+ init_recall_inject();
36369
36579
  init_loader2();
36370
36580
  init_renderer2();
36371
36581
  init_builtins();