kimiflare 0.84.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4277,7 +4277,11 @@ ${sandboxResult.output}` : sandboxResult.output;
4277
4277
  sessionId: opts2.sessionId,
4278
4278
  githubToken: opts2.githubToken,
4279
4279
  shell: opts2.shell,
4280
- intentTier: opts2.intentClassification?.tier
4280
+ intentTier: opts2.intentClassification?.tier,
4281
+ accountId: opts2.accountId,
4282
+ apiToken: opts2.apiToken,
4283
+ model: opts2.model,
4284
+ gateway: opts2.gateway
4281
4285
  },
4282
4286
  opts2.onFileChange
4283
4287
  );
@@ -5640,13 +5644,81 @@ function makeOutput(content) {
5640
5644
  function getToken(ctx) {
5641
5645
  return ctx.githubToken || process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
5642
5646
  }
5643
- var GITHUB_API_BASE, TIMEOUT_MS3, githubReadPrTool, githubReadIssueTool, githubReadCodeTool;
5647
+ var GITHUB_API_BASE, TIMEOUT_MS3, githubListMergedPrsTool, githubListReleasesTool, githubReadPrTool, githubReadIssueTool, githubReadCodeTool;
5644
5648
  var init_github = __esm({
5645
5649
  "src/tools/github.ts"() {
5646
5650
  "use strict";
5647
5651
  init_version();
5648
5652
  GITHUB_API_BASE = "https://api.github.com";
5649
5653
  TIMEOUT_MS3 = 2e4;
5654
+ githubListMergedPrsTool = {
5655
+ name: "github_list_merged_prs",
5656
+ description: "List merged pull requests for a repository within the last N days. Returns PR number, title, author, merge date, and URL for each merged PR.",
5657
+ parameters: {
5658
+ type: "object",
5659
+ properties: {
5660
+ owner: { type: "string", description: "Repository owner (user or organization)." },
5661
+ repo: { type: "string", description: "Repository name." },
5662
+ days: { type: "integer", description: "Number of days to look back. Default: 7.", minimum: 1, maximum: 90 }
5663
+ },
5664
+ required: ["owner", "repo"],
5665
+ additionalProperties: false
5666
+ },
5667
+ needsPermission: false,
5668
+ render: (args) => ({ title: `GitHub merged PRs ${args.owner ?? ""}/${args.repo ?? ""}` }),
5669
+ async run(args, ctx) {
5670
+ const token = getToken(ctx);
5671
+ const days = args.days ?? 7;
5672
+ const since = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
5673
+ const prs = await githubFetch(
5674
+ `/repos/${args.owner}/${args.repo}/pulls?state=closed&sort=updated&direction=desc&per_page=100`,
5675
+ token
5676
+ );
5677
+ const merged = prs.filter((p) => p.merged_at && p.merged_at >= since).sort((a, b) => (b.merged_at ?? "").localeCompare(a.merged_at ?? ""));
5678
+ if (merged.length === 0) {
5679
+ return makeOutput(`No merged PRs in ${args.owner}/${args.repo} within the last ${days} day(s).`);
5680
+ }
5681
+ const lines = merged.map(
5682
+ (p) => `#${p.number} ${p.title} \u2014 @${p.user.login} \u2014 merged ${p.merged_at.slice(0, 10)} \u2014 ${p.html_url}`
5683
+ );
5684
+ return makeOutput(
5685
+ [`Merged PRs in ${args.owner}/${args.repo} (last ${days} days):`, "", ...lines].join("\n")
5686
+ );
5687
+ }
5688
+ };
5689
+ githubListReleasesTool = {
5690
+ name: "github_list_releases",
5691
+ description: "List recent GitHub releases for a repository. Returns tag name, name, published date, and URL for each release.",
5692
+ parameters: {
5693
+ type: "object",
5694
+ properties: {
5695
+ owner: { type: "string", description: "Repository owner (user or organization)." },
5696
+ repo: { type: "string", description: "Repository name." },
5697
+ limit: { type: "integer", description: "Max releases to return. Default: 5.", minimum: 1, maximum: 20 }
5698
+ },
5699
+ required: ["owner", "repo"],
5700
+ additionalProperties: false
5701
+ },
5702
+ needsPermission: false,
5703
+ render: (args) => ({ title: `GitHub releases ${args.owner ?? ""}/${args.repo ?? ""}` }),
5704
+ async run(args, ctx) {
5705
+ const token = getToken(ctx);
5706
+ const limit = args.limit ?? 5;
5707
+ const releases = await githubFetch(
5708
+ `/repos/${args.owner}/${args.repo}/releases?per_page=${limit}`,
5709
+ token
5710
+ );
5711
+ if (releases.length === 0) {
5712
+ return makeOutput(`No releases found for ${args.owner}/${args.repo}.`);
5713
+ }
5714
+ const lines = releases.map(
5715
+ (r) => `${r.tag_name} \u2014 ${r.name} \u2014 published ${r.published_at.slice(0, 10)} \u2014 ${r.html_url}`
5716
+ );
5717
+ return makeOutput(
5718
+ [`Releases for ${args.owner}/${args.repo}:`, "", ...lines].join("\n")
5719
+ );
5720
+ }
5721
+ };
5650
5722
  githubReadPrTool = {
5651
5723
  name: "github_read_pr",
5652
5724
  description: "Read a GitHub pull request by owner, repo, and PR number. Returns title, body, state, author, created/updated dates, and a summary of changed files. Use this in plan mode or when you need structured PR data without write permissions.",
@@ -5772,10 +5844,354 @@ var init_github = __esm({
5772
5844
  }
5773
5845
  });
5774
5846
 
5847
+ // src/tools/changelog-image.ts
5848
+ var changelog_image_exports = {};
5849
+ __export(changelog_image_exports, {
5850
+ changelogImageTool: () => changelogImageTool
5851
+ });
5852
+ import { readFile as readFile7 } from "fs/promises";
5853
+ import { writeFile as writeFile5 } from "fs/promises";
5854
+ import { join as join11 } from "path";
5855
+ import { Resvg } from "@resvg/resvg-js";
5856
+ async function githubFetch2(path, token) {
5857
+ const controller = new AbortController();
5858
+ const timer2 = setTimeout(() => controller.abort(), TIMEOUT_MS4);
5859
+ try {
5860
+ const headers = {
5861
+ Accept: "application/vnd.github+json",
5862
+ "X-GitHub-Api-Version": "2022-11-28"
5863
+ };
5864
+ if (token) headers.Authorization = `Bearer ${token}`;
5865
+ const res = await fetch(`${GITHUB_API_BASE2}${path}`, {
5866
+ signal: controller.signal,
5867
+ headers
5868
+ });
5869
+ if (!res.ok) {
5870
+ const body = await res.text().catch(() => "");
5871
+ throw new Error(`GitHub API ${res.status}: ${res.statusText}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`);
5872
+ }
5873
+ return await res.json();
5874
+ } finally {
5875
+ clearTimeout(timer2);
5876
+ }
5877
+ }
5878
+ function getToken2(ctx) {
5879
+ return ctx.githubToken;
5880
+ }
5881
+ function escapeXml(str) {
5882
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
5883
+ }
5884
+ async function loadLogoBase64() {
5885
+ try {
5886
+ const buf = await readFile7(join11(process.cwd(), "docs", "logo.png"));
5887
+ return `data:image/png;base64,${buf.toString("base64")}`;
5888
+ } catch {
5889
+ return null;
5890
+ }
5891
+ }
5892
+ function wrapText(text, maxWidth, fontSize) {
5893
+ const avgCharWidth = fontSize * 0.55;
5894
+ const maxChars = Math.floor(maxWidth / avgCharWidth);
5895
+ const lines = [];
5896
+ const rawLines = text.split("\n");
5897
+ for (const raw of rawLines) {
5898
+ const trimmed = raw.trim();
5899
+ if (!trimmed) {
5900
+ lines.push("");
5901
+ continue;
5902
+ }
5903
+ let remaining = trimmed;
5904
+ while (remaining.length > maxChars) {
5905
+ let cut = maxChars;
5906
+ while (cut > 0 && remaining[cut] !== " ") cut--;
5907
+ if (cut === 0) cut = maxChars;
5908
+ lines.push(remaining.slice(0, cut).trimEnd());
5909
+ remaining = remaining.slice(cut).trimStart();
5910
+ }
5911
+ if (remaining) lines.push(remaining);
5912
+ }
5913
+ return lines;
5914
+ }
5915
+ async function summarizeWithLlm(prs, ctx) {
5916
+ if (!ctx.accountId || !ctx.apiToken || !ctx.model) {
5917
+ return prs.map((p) => `\u2022 ${p.title} [PR #${p.number}]`).join("\n");
5918
+ }
5919
+ const prDescriptions = prs.map((p) => {
5920
+ const bodySnippet = p.body ? `
5921
+ ${p.body.slice(0, 300).replace(/\n/g, " ")}` : "";
5922
+ return `PR #${p.number}: ${p.title} (merged ${p.merged_at.slice(0, 10)} by @${p.user.login})${bodySnippet}`;
5923
+ }).join("\n\n");
5924
+ let summary = "";
5925
+ const events = runKimi({
5926
+ accountId: ctx.accountId,
5927
+ apiToken: ctx.apiToken,
5928
+ model: ctx.model,
5929
+ messages: [
5930
+ { role: "system", content: CHANGELOG_SYSTEM_PROMPT },
5931
+ {
5932
+ role: "user",
5933
+ content: `Write a changelog summary for the following merged pull requests:
5934
+
5935
+ ${prDescriptions}`
5936
+ }
5937
+ ],
5938
+ signal: ctx.signal,
5939
+ temperature: 0.4,
5940
+ reasoningEffort: "low",
5941
+ gateway: ctx.gateway,
5942
+ idleTimeoutMs: 6e4
5943
+ });
5944
+ for await (const ev of events) {
5945
+ if (ev.type === "text") summary += ev.delta;
5946
+ }
5947
+ return summary.trim() || prs.map((p) => `\u2022 ${p.title} [PR #${p.number}]`).join("\n");
5948
+ }
5949
+ function buildChangelogSvg(opts2) {
5950
+ const { owner, repo, version, writeUp, logoBase64 } = opts2;
5951
+ const width = 900;
5952
+ const padX = 72;
5953
+ const padTop = 64;
5954
+ const padBottom = 56;
5955
+ const contentW = width - padX * 2;
5956
+ const repoFontSize = 22;
5957
+ const labelFontSize = 12;
5958
+ const bodyFontSize = 16;
5959
+ const bodyLineHeight = 24;
5960
+ const bulletIndent = 20;
5961
+ const bulletGap = 32;
5962
+ const paraGap = 8;
5963
+ const logoW = 28;
5964
+ const logoH = 28;
5965
+ const logoY = padTop + 4;
5966
+ const repoTextX = padX + (logoBase64 ? logoW + 14 : 0);
5967
+ const repoTextY = padTop + 24;
5968
+ const labelY = repoTextY + 32;
5969
+ const headerBottom = labelY + 32;
5970
+ const rawLines = writeUp.split("\n").map((l) => l.trim());
5971
+ const blocks = [];
5972
+ let currentBlock = null;
5973
+ for (const raw of rawLines) {
5974
+ if (!raw) {
5975
+ if (currentBlock) {
5976
+ blocks.push(currentBlock);
5977
+ currentBlock = null;
5978
+ }
5979
+ continue;
5980
+ }
5981
+ const isBullet = raw.startsWith("\u2022");
5982
+ const text = isBullet ? raw.slice(1).trim() : raw;
5983
+ if (!currentBlock || currentBlock.type !== (isBullet ? "bullet" : "paragraph")) {
5984
+ if (currentBlock) blocks.push(currentBlock);
5985
+ currentBlock = { type: isBullet ? "bullet" : "paragraph", lines: [] };
5986
+ }
5987
+ const wrapW = isBullet ? contentW - bulletIndent : contentW;
5988
+ const wrapped = wrapText(text, wrapW, bodyFontSize);
5989
+ currentBlock.lines.push(...wrapped);
5990
+ }
5991
+ if (currentBlock) blocks.push(currentBlock);
5992
+ let bodyHeight = 0;
5993
+ const lineMeta = [];
5994
+ let y = headerBottom;
5995
+ for (let b = 0; b < blocks.length; b++) {
5996
+ const block = blocks[b];
5997
+ for (let i = 0; i < block.lines.length; i++) {
5998
+ const line = block.lines[i];
5999
+ lineMeta.push({
6000
+ text: line,
6001
+ y,
6002
+ isBullet: block.type === "bullet",
6003
+ isFirst: i === 0
6004
+ });
6005
+ y += bodyLineHeight;
6006
+ if (block.type === "bullet" && i < block.lines.length - 1) {
6007
+ y += paraGap;
6008
+ }
6009
+ }
6010
+ if (b < blocks.length - 1) {
6011
+ y += bulletGap;
6012
+ }
6013
+ }
6014
+ bodyHeight = y - headerBottom;
6015
+ const height = headerBottom + bodyHeight + padBottom;
6016
+ const logoEl = logoBase64 ? `<image x="${padX}" y="${logoY}" width="${logoW}" height="${logoH}" href="${logoBase64}"/>` : "";
6017
+ const bodySpans = lineMeta.map(({ text, y: ly, isBullet, isFirst }) => {
6018
+ const x = isBullet ? padX + bulletIndent : padX;
6019
+ const weight = isBullet ? 'font-weight="500"' : "";
6020
+ const fill = isBullet ? "#1f2937" : "#4b5563";
6021
+ return `<tspan x="${x}" y="${ly}" fill="${fill}" ${weight}>${escapeXml(text)}</tspan>`;
6022
+ }).join("");
6023
+ const bulletDots = lineMeta.filter((m) => m.isBullet && m.isFirst).map(({ y: ly }) => {
6024
+ const cy = ly - bodyFontSize * 0.35;
6025
+ return `<circle cx="${padX + 6}" cy="${cy}" r="3" fill="#f97316"/>`;
6026
+ }).join("");
6027
+ const today4 = escapeXml((/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
6028
+ return `<?xml version="1.0" encoding="UTF-8"?>
6029
+ <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
6030
+ <defs>
6031
+ <style>
6032
+ .font { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
6033
+ </style>
6034
+ </defs>
6035
+
6036
+ <!-- Background -->
6037
+ <rect width="${width}" height="${height}" fill="#ffffff"/>
6038
+
6039
+ <!-- Top accent bar -->
6040
+ <rect x="${padX}" y="${padTop}" width="36" height="3" fill="#f97316" rx="1.5"/>
6041
+
6042
+ <!-- Header -->
6043
+ <g class="font">
6044
+ ${logoEl}
6045
+ <text x="${repoTextX}" y="${repoTextY}" fill="#111827" font-size="${repoFontSize}" font-weight="600">${escapeXml(owner)}/${escapeXml(repo)}</text>
6046
+
6047
+ <text x="${padX}" y="${labelY}" fill="#9ca3af" font-size="${labelFontSize}" font-weight="500" letter-spacing="0.08em">CHANGELOG</text>
6048
+
6049
+ <!-- Version pill -->
6050
+ <rect x="${padX + 88}" y="${labelY - 14}" width="${Math.max(40, version.length * 7 + 16)}" height="22" fill="#fff7ed" rx="11"/>
6051
+ <text x="${padX + 88 + 10}" y="${labelY}" fill="#f97316" font-size="${labelFontSize}" font-weight="500">${escapeXml(version)}</text>
6052
+ </g>
6053
+
6054
+ <!-- Separator -->
6055
+ <line x1="${padX}" y1="${headerBottom - 10}" x2="${width - padX}" y2="${headerBottom - 10}" stroke="#f3f4f6" stroke-width="1"/>
6056
+
6057
+ <!-- Body -->
6058
+ <g class="font">
6059
+ ${bulletDots}
6060
+ <text font-size="${bodyFontSize}" line-height="${bodyLineHeight}">
6061
+ ${bodySpans}
6062
+ </text>
6063
+ </g>
6064
+
6065
+ <!-- Footer -->
6066
+ <g class="font" transform="translate(0, ${height - padBottom + 20})">
6067
+ <text x="${padX}" y="0" fill="#d1d5db" font-size="11">Generated with KimiFlare \xB7 ${today4}</text>
6068
+ </g>
6069
+ </svg>`;
6070
+ }
6071
+ var GITHUB_API_BASE2, TIMEOUT_MS4, CHANGELOG_SYSTEM_PROMPT, changelogImageTool;
6072
+ var init_changelog_image = __esm({
6073
+ "src/tools/changelog-image.ts"() {
6074
+ "use strict";
6075
+ init_client();
6076
+ GITHUB_API_BASE2 = "https://api.github.com";
6077
+ TIMEOUT_MS4 = 2e4;
6078
+ CHANGELOG_SYSTEM_PROMPT = `You are a senior technical product writer. Your job is to write release notes that make users excited about new features while being completely accurate and grounded only in the provided pull requests.
6079
+
6080
+ Rules:
6081
+ - Write 3\u20136 bullet points maximum. Highlight only the most significant, user-facing changes.
6082
+ - Focus on USER VALUE and IMPACT, not implementation details or internal refactors.
6083
+ - Use confident, clear, engaging language \u2014 like Apple product release notes.
6084
+ - Group related changes under themes when it makes sense.
6085
+ - Each bullet should be 1\u20132 sentences.
6086
+ - Include the PR number in square brackets at the end of each bullet, e.g. [PR #123]
6087
+ - Do NOT include changes that are purely internal (chores, refactors, dependency updates, version bumps) unless they have clear user-facing impact.
6088
+ - Do NOT hallucinate features not present in the PRs.
6089
+ - If there are no meaningful user-facing changes, say so briefly.
6090
+
6091
+ Format your response as plain text bullet points, one per line, starting with "\u2022 ". Do not use markdown headers or bold/italic.`;
6092
+ changelogImageTool = {
6093
+ name: "changelog_image",
6094
+ description: "Generate a beautiful changelog image for a GitHub repository. Fetches merged PRs from the last N days, uses an LLM to write a creative summary, then renders a shareable PNG with the project logo, version, and highlights.",
6095
+ parameters: {
6096
+ type: "object",
6097
+ properties: {
6098
+ owner: { type: "string", description: "Repository owner (user or organization)." },
6099
+ repo: { type: "string", description: "Repository name." },
6100
+ days: { type: "integer", description: "Number of days to look back for merged PRs. Default: 7.", minimum: 1, maximum: 90 },
6101
+ output: { type: "string", description: "Output file path for the PNG. Default: ./changelog-<repo>-<version>.png" }
6102
+ },
6103
+ required: ["owner", "repo"],
6104
+ additionalProperties: false
6105
+ },
6106
+ needsPermission: false,
6107
+ render: (args) => ({ title: `Changelog image for ${args.owner ?? ""}/${args.repo ?? ""}` }),
6108
+ async run(args, ctx) {
6109
+ const token = getToken2(ctx);
6110
+ const days = args.days ?? 7;
6111
+ const tasks = [
6112
+ { id: "1", title: "Fetch merged PRs from GitHub", status: "pending" },
6113
+ { id: "2", title: "Determine latest version", status: "pending" },
6114
+ { id: "3", title: "Summarize changes with LLM", status: "pending" },
6115
+ { id: "4", title: "Render changelog image", status: "pending" },
6116
+ { id: "5", title: "Save PNG to file", status: "pending" }
6117
+ ];
6118
+ const setTask = (id, status) => {
6119
+ const t = tasks.find((x) => x.id === id);
6120
+ if (t) t.status = status;
6121
+ ctx.onTasks?.(tasks.map((x) => ({ ...x })));
6122
+ };
6123
+ setTask("1", "in_progress");
6124
+ const since = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
6125
+ const prs = await githubFetch2(
6126
+ `/repos/${args.owner}/${args.repo}/pulls?state=closed&sort=updated&direction=desc&per_page=100`,
6127
+ token
6128
+ );
6129
+ const merged = prs.filter((p) => p.merged_at && p.merged_at >= since).sort((a, b) => (b.merged_at ?? "").localeCompare(a.merged_at ?? ""));
6130
+ if (merged.length === 0) {
6131
+ setTask("1", "completed");
6132
+ return {
6133
+ content: `No merged PRs in ${args.owner}/${args.repo} within the last ${days} day(s).`,
6134
+ rawBytes: 0,
6135
+ reducedBytes: 0
6136
+ };
6137
+ }
6138
+ setTask("1", "completed");
6139
+ setTask("2", "in_progress");
6140
+ let version = "latest";
6141
+ try {
6142
+ const releases = await githubFetch2(
6143
+ `/repos/${args.owner}/${args.repo}/releases?per_page=1`,
6144
+ token
6145
+ );
6146
+ if (releases.length > 0) {
6147
+ version = releases[0].tag_name;
6148
+ }
6149
+ } catch {
6150
+ try {
6151
+ const tags = await githubFetch2(
6152
+ `/repos/${args.owner}/${args.repo}/tags?per_page=1`,
6153
+ token
6154
+ );
6155
+ if (tags.length > 0) {
6156
+ version = tags[0].name;
6157
+ }
6158
+ } catch {
6159
+ }
6160
+ }
6161
+ setTask("2", "completed");
6162
+ setTask("3", "in_progress");
6163
+ const writeUp = await summarizeWithLlm(merged, ctx);
6164
+ setTask("3", "completed");
6165
+ setTask("4", "in_progress");
6166
+ const logoBase64 = await loadLogoBase64();
6167
+ const svg = buildChangelogSvg({ owner: args.owner, repo: args.repo, version, writeUp, logoBase64 });
6168
+ const resvg = new Resvg(svg, {
6169
+ fitTo: { mode: "zoom", value: 2 },
6170
+ font: {
6171
+ defaultFontFamily: "system-ui"
6172
+ }
6173
+ });
6174
+ const pngData = resvg.render();
6175
+ const pngBuffer = pngData.asPng();
6176
+ setTask("4", "completed");
6177
+ setTask("5", "in_progress");
6178
+ const outputPath = args.output ?? `./changelog-${args.repo}-${version.replace(/[^a-zA-Z0-9._-]/g, "_")}.png`;
6179
+ await writeFile5(outputPath, pngBuffer);
6180
+ setTask("5", "completed");
6181
+ const periodLabel = days === 1 ? "past day" : `past ${days} days`;
6182
+ const content = `\u2713 Changelog image saved to ${outputPath}
6183
+ ${merged.length} PR${merged.length === 1 ? "" : "s"} from the ${periodLabel} \xB7 ${version} \xB7 ${resvg.width}\xD7${resvg.height}`;
6184
+ const bytes = Buffer.byteLength(content, "utf8");
6185
+ return { content, rawBytes: bytes, reducedBytes: bytes };
6186
+ }
6187
+ };
6188
+ }
6189
+ });
6190
+
5775
6191
  // src/tools/browser.ts
5776
6192
  import { mkdir as mkdir5 } from "fs/promises";
5777
6193
  import { tmpdir as tmpdir2 } from "os";
5778
- import { join as join11, dirname as dirname5 } from "path";
6194
+ import { join as join12, dirname as dirname5 } from "path";
5779
6195
  async function autoScroll(page) {
5780
6196
  await page.evaluate(async () => {
5781
6197
  await new Promise((resolve5) => {
@@ -5797,11 +6213,11 @@ async function autoScroll(page) {
5797
6213
  });
5798
6214
  });
5799
6215
  }
5800
- var TIMEOUT_MS4, NAV_TIMEOUT_MS, browserFetchTool;
6216
+ var TIMEOUT_MS5, NAV_TIMEOUT_MS, browserFetchTool;
5801
6217
  var init_browser = __esm({
5802
6218
  "src/tools/browser.ts"() {
5803
6219
  "use strict";
5804
- TIMEOUT_MS4 = 3e4;
6220
+ TIMEOUT_MS5 = 3e4;
5805
6221
  NAV_TIMEOUT_MS = 2e4;
5806
6222
  browserFetchTool = {
5807
6223
  name: "browser_fetch",
@@ -5844,14 +6260,14 @@ var init_browser = __esm({
5844
6260
  const page = await browser.newPage();
5845
6261
  await page.goto(args.url, { waitUntil: "networkidle", timeout: NAV_TIMEOUT_MS });
5846
6262
  if (args.wait_for) {
5847
- await page.waitForSelector(args.wait_for, { timeout: TIMEOUT_MS4 });
6263
+ await page.waitForSelector(args.wait_for, { timeout: TIMEOUT_MS5 });
5848
6264
  }
5849
6265
  if (args.scroll) {
5850
6266
  await autoScroll(page);
5851
6267
  }
5852
6268
  let screenshotPath;
5853
6269
  if (args.screenshot) {
5854
- screenshotPath = join11(tmpdir2(), `kimiflare-browser-${Date.now()}.png`);
6270
+ screenshotPath = join12(tmpdir2(), `kimiflare-browser-${Date.now()}.png`);
5855
6271
  await mkdir5(dirname5(screenshotPath), { recursive: true });
5856
6272
  await page.screenshot({ path: screenshotPath, fullPage: true });
5857
6273
  }
@@ -6976,6 +7392,7 @@ var init_executor = __esm({
6976
7392
  init_web_fetch();
6977
7393
  init_web_search();
6978
7394
  init_github();
7395
+ init_changelog_image();
6979
7396
  init_browser();
6980
7397
  init_tasks();
6981
7398
  init_memory();
@@ -6996,6 +7413,9 @@ var init_executor = __esm({
6996
7413
  githubReadPrTool,
6997
7414
  githubReadIssueTool,
6998
7415
  githubReadCodeTool,
7416
+ githubListMergedPrsTool,
7417
+ githubListReleasesTool,
7418
+ changelogImageTool,
6999
7419
  browserFetchTool,
7000
7420
  tasksSetTool,
7001
7421
  memoryRememberTool,
@@ -7215,20 +7635,20 @@ var init_executor = __esm({
7215
7635
  });
7216
7636
 
7217
7637
  // src/util/update-check.ts
7218
- import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
7638
+ import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir6 } from "fs/promises";
7219
7639
  import { homedir as homedir6 } from "os";
7220
- import { join as join12, dirname as dirname6 } from "path";
7640
+ import { join as join13, dirname as dirname6 } from "path";
7221
7641
  import { fileURLToPath as fileURLToPath2 } from "url";
7222
7642
  function cachePath() {
7223
- const xdg = process.env.XDG_CONFIG_HOME || join12(homedir6(), ".config");
7224
- return join12(xdg, "kimiflare", "update-check.json");
7643
+ const xdg = process.env.XDG_CONFIG_HOME || join13(homedir6(), ".config");
7644
+ return join13(xdg, "kimiflare", "update-check.json");
7225
7645
  }
7226
7646
  async function findPackageJson(startDir) {
7227
7647
  let dir = startDir;
7228
7648
  while (true) {
7229
- const candidate = join12(dir, "package.json");
7649
+ const candidate = join13(dir, "package.json");
7230
7650
  try {
7231
- const raw = await readFile7(candidate, "utf8");
7651
+ const raw = await readFile8(candidate, "utf8");
7232
7652
  const parsed = JSON.parse(raw);
7233
7653
  if (parsed.name === "kimiflare" && parsed.version) {
7234
7654
  return { path: candidate, version: parsed.version };
@@ -7248,7 +7668,7 @@ async function readLocalVersion() {
7248
7668
  }
7249
7669
  async function readCache() {
7250
7670
  try {
7251
- const raw = await readFile7(cachePath(), "utf8");
7671
+ const raw = await readFile8(cachePath(), "utf8");
7252
7672
  const parsed = JSON.parse(raw);
7253
7673
  if (Date.now() - parsed.checkedAt < CACHE_TTL_MS) {
7254
7674
  return parsed;
@@ -7260,7 +7680,7 @@ async function readCache() {
7260
7680
  async function writeCache(entry) {
7261
7681
  const p = cachePath();
7262
7682
  await mkdir6(dirname6(p), { recursive: true });
7263
- await writeFile6(p, JSON.stringify(entry), "utf8");
7683
+ await writeFile7(p, JSON.stringify(entry), "utf8");
7264
7684
  }
7265
7685
  async function fetchLatestVersion() {
7266
7686
  try {
@@ -7291,8 +7711,8 @@ function isNewer(local, remote) {
7291
7711
  async function readOptionalDepVersion(name) {
7292
7712
  try {
7293
7713
  const here = dirname6(fileURLToPath2(import.meta.url));
7294
- const candidate = join12(here, "..", "..", "node_modules", name, "package.json");
7295
- const raw = await readFile7(candidate, "utf8");
7714
+ const candidate = join13(here, "..", "..", "node_modules", name, "package.json");
7715
+ const raw = await readFile8(candidate, "utf8");
7296
7716
  const parsed = JSON.parse(raw);
7297
7717
  return parsed.version ?? null;
7298
7718
  } catch {
@@ -7352,23 +7772,23 @@ var init_update_check = __esm({
7352
7772
  });
7353
7773
 
7354
7774
  // src/remote/session-store.ts
7355
- import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, readdir as readdir3 } from "fs/promises";
7775
+ import { readFile as readFile9, writeFile as writeFile8, mkdir as mkdir7, readdir as readdir3 } from "fs/promises";
7356
7776
  import { homedir as homedir7 } from "os";
7357
- import { join as join13 } from "path";
7777
+ import { join as join14 } from "path";
7358
7778
  function remoteDir() {
7359
- const xdg = process.env.XDG_DATA_HOME || join13(homedir7(), ".config");
7360
- return join13(xdg, "kimiflare", "remote");
7779
+ const xdg = process.env.XDG_DATA_HOME || join14(homedir7(), ".config");
7780
+ return join14(xdg, "kimiflare", "remote");
7361
7781
  }
7362
7782
  async function saveRemoteSession(session) {
7363
7783
  const dir = remoteDir();
7364
7784
  await mkdir7(dir, { recursive: true });
7365
- const path = join13(dir, `${session.sessionId}.json`);
7366
- await writeFile7(path, JSON.stringify(session, null, 2) + "\n", "utf8");
7785
+ const path = join14(dir, `${session.sessionId}.json`);
7786
+ await writeFile8(path, JSON.stringify(session, null, 2) + "\n", "utf8");
7367
7787
  }
7368
7788
  async function loadRemoteSession(sessionId) {
7369
7789
  try {
7370
- const path = join13(remoteDir(), `${sessionId}.json`);
7371
- const raw = await readFile8(path, "utf8");
7790
+ const path = join14(remoteDir(), `${sessionId}.json`);
7791
+ const raw = await readFile9(path, "utf8");
7372
7792
  return JSON.parse(raw);
7373
7793
  } catch {
7374
7794
  return null;
@@ -7382,7 +7802,7 @@ async function listRemoteSessions() {
7382
7802
  for (const file of files) {
7383
7803
  if (!file.endsWith(".json")) continue;
7384
7804
  try {
7385
- const raw = await readFile8(join13(dir, file), "utf8");
7805
+ const raw = await readFile9(join14(dir, file), "utf8");
7386
7806
  sessions.push(JSON.parse(raw));
7387
7807
  } catch {
7388
7808
  }
@@ -7404,7 +7824,7 @@ var init_session_store = __esm({
7404
7824
 
7405
7825
  // src/remote/deploy.ts
7406
7826
  import { execSync } from "child_process";
7407
- import { join as join14, dirname as dirname7 } from "path";
7827
+ import { join as join15, dirname as dirname7 } from "path";
7408
7828
  import { fileURLToPath as fileURLToPath3 } from "url";
7409
7829
  import { randomBytes } from "crypto";
7410
7830
  function generateSecret() {
@@ -7439,7 +7859,7 @@ async function* deployForTui() {
7439
7859
  yield { message: "Docker OK" };
7440
7860
  yield { message: "Building remote agent bundle..." };
7441
7861
  try {
7442
- runCapture("npm run build:remote-agent", join14(REMOTE_DIR, ".."));
7862
+ runCapture("npm run build:remote-agent", join15(REMOTE_DIR, ".."));
7443
7863
  yield { message: "Agent bundle built" };
7444
7864
  } catch (err) {
7445
7865
  yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
@@ -7569,8 +7989,8 @@ var init_deploy = __esm({
7569
7989
  "use strict";
7570
7990
  init_config();
7571
7991
  __dirname2 = dirname7(fileURLToPath3(import.meta.url));
7572
- REMOTE_DIR = join14(__dirname2, "..", "..", "..", "remote");
7573
- WORKER_DIR = join14(REMOTE_DIR, "worker");
7992
+ REMOTE_DIR = join15(__dirname2, "..", "..", "..", "remote");
7993
+ WORKER_DIR = join15(REMOTE_DIR, "worker");
7574
7994
  }
7575
7995
  });
7576
7996
 
@@ -8084,12 +8504,12 @@ var init_heuristic = __esm({
8084
8504
  });
8085
8505
 
8086
8506
  // src/cost-attribution/classify-from-session.ts
8087
- import { readFile as readFile9 } from "fs/promises";
8088
- import { join as join15 } from "path";
8507
+ import { readFile as readFile10 } from "fs/promises";
8508
+ import { join as join16 } from "path";
8089
8509
  import { homedir as homedir8 } from "os";
8090
8510
  function sessionsDir() {
8091
- const xdg = process.env.XDG_DATA_HOME || join15(homedir8(), ".local", "share");
8092
- return join15(xdg, "kimiflare", "sessions");
8511
+ const xdg = process.env.XDG_DATA_HOME || join16(homedir8(), ".local", "share");
8512
+ return join16(xdg, "kimiflare", "sessions");
8093
8513
  }
8094
8514
  function parseToolCalls(calls) {
8095
8515
  return calls.map((c) => {
@@ -8103,7 +8523,7 @@ function parseToolCalls(calls) {
8103
8523
  }
8104
8524
  async function classifyFromSessionFile(sessionId) {
8105
8525
  try {
8106
- const raw = await readFile9(join15(sessionsDir(), `${sessionId}.json`), "utf8");
8526
+ const raw = await readFile10(join16(sessionsDir(), `${sessionId}.json`), "utf8");
8107
8527
  const session = JSON.parse(raw);
8108
8528
  const messages = session.messages ?? [];
8109
8529
  const turns = [];
@@ -8136,15 +8556,15 @@ var cli_exports = {};
8136
8556
  __export(cli_exports, {
8137
8557
  runCostCommand: () => runCostCommand
8138
8558
  });
8139
- import { readFile as readFile10 } from "fs/promises";
8140
- import { join as join16 } from "path";
8559
+ import { readFile as readFile11 } from "fs/promises";
8560
+ import { join as join17 } from "path";
8141
8561
  import { homedir as homedir9 } from "os";
8142
8562
  function usageDir() {
8143
- const xdg = process.env.XDG_DATA_HOME || join16(homedir9(), ".local", "share");
8144
- return join16(xdg, "kimiflare");
8563
+ const xdg = process.env.XDG_DATA_HOME || join17(homedir9(), ".local", "share");
8564
+ return join17(xdg, "kimiflare");
8145
8565
  }
8146
8566
  function usagePath() {
8147
- return join16(usageDir(), "usage.json");
8567
+ return join17(usageDir(), "usage.json");
8148
8568
  }
8149
8569
  function today() {
8150
8570
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -8156,7 +8576,7 @@ function daysAgo(n) {
8156
8576
  }
8157
8577
  async function loadLog() {
8158
8578
  try {
8159
- const raw = await readFile10(usagePath(), "utf8");
8579
+ const raw = await readFile11(usagePath(), "utf8");
8160
8580
  return JSON.parse(raw);
8161
8581
  } catch {
8162
8582
  return { version: 1, days: [], sessions: [] };
@@ -8255,12 +8675,12 @@ __export(sessions_exports, {
8255
8675
  saveSession: () => saveSession,
8256
8676
  sessionsDir: () => sessionsDir2
8257
8677
  });
8258
- import { readFile as readFile11, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, stat as stat4 } from "fs/promises";
8678
+ import { readFile as readFile12, writeFile as writeFile9, mkdir as mkdir8, readdir as readdir4, stat as stat4 } from "fs/promises";
8259
8679
  import { homedir as homedir10 } from "os";
8260
- import { join as join17 } from "path";
8680
+ import { join as join18 } from "path";
8261
8681
  function sessionsDir2() {
8262
- const xdg = process.env.XDG_DATA_HOME || join17(homedir10(), ".local", "share");
8263
- return join17(xdg, "kimiflare", "sessions");
8682
+ const xdg = process.env.XDG_DATA_HOME || join18(homedir10(), ".local", "share");
8683
+ return join18(xdg, "kimiflare", "sessions");
8264
8684
  }
8265
8685
  function sanitize(text) {
8266
8686
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -8280,8 +8700,8 @@ function makeSessionId(firstPrompt) {
8280
8700
  async function saveSession(file) {
8281
8701
  const dir = sessionsDir2();
8282
8702
  await mkdir8(dir, { recursive: true });
8283
- const path = join17(dir, `${file.id}.json`);
8284
- await writeFile8(path, JSON.stringify(file, null, 2), "utf8");
8703
+ const path = join18(dir, `${file.id}.json`);
8704
+ await writeFile9(path, JSON.stringify(file, null, 2), "utf8");
8285
8705
  return path;
8286
8706
  }
8287
8707
  async function pruneSessions() {
@@ -8300,9 +8720,9 @@ async function listSessions(limit = 30, cwd) {
8300
8720
  const summaries = [];
8301
8721
  for (const name of entries) {
8302
8722
  if (!name.endsWith(".json")) continue;
8303
- const path = join17(dir, name);
8723
+ const path = join18(dir, name);
8304
8724
  try {
8305
- const [s, raw] = await Promise.all([stat4(path), readFile11(path, "utf8")]);
8725
+ const [s, raw] = await Promise.all([stat4(path), readFile12(path, "utf8")]);
8306
8726
  const parsed = JSON.parse(raw);
8307
8727
  if (cwd && parsed.cwd !== cwd) continue;
8308
8728
  const firstUser = parsed.messages.find((m) => m.role === "user");
@@ -8324,7 +8744,7 @@ async function listSessions(limit = 30, cwd) {
8324
8744
  return summaries.slice(0, limit);
8325
8745
  }
8326
8746
  async function loadSession(filePath) {
8327
- const raw = await readFile11(filePath, "utf8");
8747
+ const raw = await readFile12(filePath, "utf8");
8328
8748
  return JSON.parse(raw);
8329
8749
  }
8330
8750
  async function addCheckpoint(filePath, checkpoint) {
@@ -10599,20 +11019,20 @@ var init_pricing = __esm({
10599
11019
  });
10600
11020
 
10601
11021
  // src/usage-tracker.ts
10602
- import { readFile as readFile12, writeFile as writeFile9, mkdir as mkdir9 } from "fs/promises";
11022
+ import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
10603
11023
  import { homedir as homedir11 } from "os";
10604
- import { join as join19 } from "path";
11024
+ import { join as join20 } from "path";
10605
11025
  import { EventEmitter as EventEmitter2 } from "events";
10606
11026
  import { randomUUID } from "crypto";
10607
11027
  function usageDir2() {
10608
- const xdg = process.env.XDG_DATA_HOME || join19(homedir11(), ".local", "share");
10609
- return join19(xdg, "kimiflare");
11028
+ const xdg = process.env.XDG_DATA_HOME || join20(homedir11(), ".local", "share");
11029
+ return join20(xdg, "kimiflare");
10610
11030
  }
10611
11031
  function usagePath2() {
10612
- return join19(usageDir2(), "usage.json");
11032
+ return join20(usageDir2(), "usage.json");
10613
11033
  }
10614
11034
  function historyPath() {
10615
- return join19(usageDir2(), "history.jsonl");
11035
+ return join20(usageDir2(), "history.jsonl");
10616
11036
  }
10617
11037
  function today2() {
10618
11038
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -10623,7 +11043,7 @@ function cutoffDate(daysBack) {
10623
11043
  }
10624
11044
  async function loadLog2() {
10625
11045
  try {
10626
- const raw = await readFile12(usagePath2(), "utf8");
11046
+ const raw = await readFile13(usagePath2(), "utf8");
10627
11047
  const parsed = JSON.parse(raw);
10628
11048
  if (parsed.version === LOG_VERSION2) return parsed;
10629
11049
  } catch {
@@ -10632,7 +11052,7 @@ async function loadLog2() {
10632
11052
  }
10633
11053
  async function saveLog(log2) {
10634
11054
  await mkdir9(usageDir2(), { recursive: true });
10635
- await writeFile9(usagePath2(), JSON.stringify(log2, null, 2), "utf8");
11055
+ await writeFile10(usagePath2(), JSON.stringify(log2, null, 2), "utf8");
10636
11056
  }
10637
11057
  function withLock(fn) {
10638
11058
  const next = writeChain.then(fn, fn);
@@ -10641,7 +11061,7 @@ function withLock(fn) {
10641
11061
  }
10642
11062
  async function loadHistory() {
10643
11063
  try {
10644
- const raw = await readFile12(historyPath(), "utf8");
11064
+ const raw = await readFile13(historyPath(), "utf8");
10645
11065
  const lines = raw.split("\n").filter((l) => l.trim());
10646
11066
  const entries = [];
10647
11067
  for (const line of lines) {
@@ -10666,7 +11086,7 @@ async function upsertHistoryDay(day) {
10666
11086
  }
10667
11087
  const lines = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
10668
11088
  await mkdir9(usageDir2(), { recursive: true });
10669
- await writeFile9(historyPath(), lines, "utf8");
11089
+ await writeFile10(historyPath(), lines, "utf8");
10670
11090
  }
10671
11091
  function getOrCreateDay(log2, date) {
10672
11092
  let day = log2.days.find((d) => d.date === date);
@@ -11075,14 +11495,14 @@ var init_types2 = __esm({
11075
11495
  // src/hooks/settings.ts
11076
11496
  import { existsSync as existsSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync3, writeFileSync } from "fs";
11077
11497
  import { homedir as homedir12 } from "os";
11078
- import { join as join20, dirname as dirname9 } from "path";
11498
+ import { join as join21, dirname as dirname9 } from "path";
11079
11499
  import { createHash } from "crypto";
11080
11500
  function globalSettingsPath() {
11081
- const xdg = process.env.XDG_CONFIG_HOME || join20(homedir12(), ".config");
11082
- return join20(xdg, "kimiflare", "settings.json");
11501
+ const xdg = process.env.XDG_CONFIG_HOME || join21(homedir12(), ".config");
11502
+ return join21(xdg, "kimiflare", "settings.json");
11083
11503
  }
11084
11504
  function projectSettingsPath(cwd) {
11085
- return join20(cwd, ".kimiflare", "settings.json");
11505
+ return join21(cwd, ".kimiflare", "settings.json");
11086
11506
  }
11087
11507
  function readSettingsFile(path) {
11088
11508
  if (!existsSync2(path)) return null;
@@ -11407,7 +11827,7 @@ var init_manager3 = __esm({
11407
11827
  // src/sdk/session.ts
11408
11828
  import { resolve as resolve3 } from "path";
11409
11829
  import { homedir as homedir13 } from "os";
11410
- import { join as join21 } from "path";
11830
+ import { join as join22 } from "path";
11411
11831
  import { existsSync as existsSync3 } from "fs";
11412
11832
  async function createAgentSession(opts2) {
11413
11833
  const config2 = await resolveSdkConfig(opts2);
@@ -11422,7 +11842,7 @@ async function createAgentSession(opts2) {
11422
11842
  let memoryManager = null;
11423
11843
  const memoryEnabled = opts2.memoryEnabled ?? config2.memoryEnabled ?? false;
11424
11844
  if (memoryEnabled) {
11425
- const dbPath = config2.memoryDbPath ?? join21(homedir13(), ".local", "share", "kimiflare", "memory.db");
11845
+ const dbPath = config2.memoryDbPath ?? join22(homedir13(), ".local", "share", "kimiflare", "memory.db");
11426
11846
  memoryManager = new MemoryManager({
11427
11847
  dbPath,
11428
11848
  accountId: config2.accountId,
@@ -11453,7 +11873,7 @@ async function createAgentSession(opts2) {
11453
11873
  }
11454
11874
  let sessionFile;
11455
11875
  if (opts2.sessionId) {
11456
- const filePath = join21(sessionsDir2(), `${opts2.sessionId}.json`);
11876
+ const filePath = join22(sessionsDir2(), `${opts2.sessionId}.json`);
11457
11877
  try {
11458
11878
  sessionFile = await loadSession(filePath);
11459
11879
  } catch {
@@ -11612,8 +12032,8 @@ var init_session = __esm({
11612
12032
  const parts = [{ type: "text", text }];
11613
12033
  for (const img of options.images) {
11614
12034
  if ("path" in img) {
11615
- const { readFile: readFile22 } = await import("fs/promises");
11616
- const data = await readFile22(img.path, "base64");
12035
+ const { readFile: readFile23 } = await import("fs/promises");
12036
+ const data = await readFile23(img.path, "base64");
11617
12037
  const mimeType = img.path.endsWith(".png") ? "image/png" : img.path.endsWith(".jpg") || img.path.endsWith(".jpeg") ? "image/jpeg" : "image/webp";
11618
12038
  parts.push({ type: "image_url", image_url: { url: `data:${mimeType};base64,${data}` } });
11619
12039
  } else {
@@ -11859,14 +12279,14 @@ var init_session = __esm({
11859
12279
  this.lspManager?.notifyChange(path, content);
11860
12280
  } else {
11861
12281
  void import("fs/promises").then(
11862
- ({ readFile: readFile22 }) => readFile22(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
12282
+ ({ readFile: readFile23 }) => readFile23(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
11863
12283
  })
11864
12284
  ).catch(() => {
11865
12285
  });
11866
12286
  }
11867
12287
  }
11868
12288
  });
11869
- if (existsSync3(join21(this.cwd, "KIMI.md"))) {
12289
+ if (existsSync3(join22(this.cwd, "KIMI.md"))) {
11870
12290
  this.messages[0] = {
11871
12291
  role: "system",
11872
12292
  content: buildSystemPrompt({
@@ -12141,9 +12561,9 @@ var init_repo_info = __esm({
12141
12561
  });
12142
12562
 
12143
12563
  // src/agent/supervisor.ts
12144
- import { readdir as readdir5, readFile as readFile13, stat as stat5 } from "fs/promises";
12564
+ import { readdir as readdir5, readFile as readFile14, stat as stat5 } from "fs/promises";
12145
12565
  import { createHash as createHash2 } from "crypto";
12146
- import { resolve as resolve4, join as join22 } from "path";
12566
+ import { resolve as resolve4, join as join23 } from "path";
12147
12567
  async function preReadFilesForWorkers(files, repoRoot, maxChars = DEFAULT_PRE_READ_MAX_CHARS) {
12148
12568
  const results = [];
12149
12569
  const filesRead = [];
@@ -12154,7 +12574,7 @@ async function preReadFilesForWorkers(files, repoRoot, maxChars = DEFAULT_PRE_RE
12154
12574
  try {
12155
12575
  const s = await stat5(path);
12156
12576
  if (!s.isFile()) continue;
12157
- const raw = await readFile13(path, "utf8");
12577
+ const raw = await readFile14(path, "utf8");
12158
12578
  const remaining = maxChars - chars;
12159
12579
  const content = raw.length > remaining ? raw.slice(0, remaining) + "\n\u2026 (truncated)" : raw;
12160
12580
  results.push(`--- ${file} ---
@@ -12178,7 +12598,7 @@ ${results.join("\n\n")}`,
12178
12598
  }
12179
12599
  function getPreReadFilesFromMemory(cfg, repoRoot, limit = 10) {
12180
12600
  if (!cfg.memoryEnabled) return [];
12181
- const dbPath = cfg.memoryDbPath ?? join22(repoRoot, ".kimiflare", "memory.db");
12601
+ const dbPath = cfg.memoryDbPath ?? join23(repoRoot, ".kimiflare", "memory.db");
12182
12602
  try {
12183
12603
  const db = openMemoryDb(dbPath);
12184
12604
  const files = getTopRelatedFiles(db, repoRoot, limit);
@@ -13440,9 +13860,9 @@ var init_emit_mode = __esm({
13440
13860
 
13441
13861
  // src/remote/deploy-commute.ts
13442
13862
  import { spawn as spawn4 } from "child_process";
13443
- import { mkdtemp, readFile as readFile14, writeFile as writeFile10, rm } from "fs/promises";
13863
+ import { mkdtemp, readFile as readFile15, writeFile as writeFile11, rm } from "fs/promises";
13444
13864
  import { tmpdir as tmpdir3 } from "os";
13445
- import { join as join23 } from "path";
13865
+ import { join as join24 } from "path";
13446
13866
  import { randomBytes as randomBytes2 } from "crypto";
13447
13867
  async function cfApiFetch(accountId, apiToken, path, init) {
13448
13868
  const url = `${CF_API}/accounts/${encodeURIComponent(accountId)}${path}`;
@@ -13673,8 +14093,8 @@ ${wranglerInstall.stderr.slice(-600)}`,
13673
14093
  ok: true
13674
14094
  };
13675
14095
  yield { message: "Prerequisites ready", ok: true };
13676
- const tmpRoot = await mkdtemp(join23(tmpdir3(), "kimiflare-commute-"));
13677
- const repoDir = join23(tmpRoot, "kimiflare-commute");
14096
+ const tmpRoot = await mkdtemp(join24(tmpdir3(), "kimiflare-commute-"));
14097
+ const repoDir = join24(tmpRoot, "kimiflare-commute");
13678
14098
  yield { message: `Fetching worker source from GitHub (${COMMUTE_REPO})\u2026` };
13679
14099
  const clone = await runCmd("git", ["clone", "--depth", "1", "--branch", COMMUTE_BRANCH, COMMUTE_REPO, repoDir], { timeoutMs: 6e4 });
13680
14100
  if (clone.code !== 0) {
@@ -13683,8 +14103,8 @@ ${(clone.stderr || clone.stdout).slice(0, 400)}`, error: true };
13683
14103
  throw new Error("clone failed");
13684
14104
  }
13685
14105
  yield { message: "Source fetched from GitHub", ok: true };
13686
- const workerDir = join23(repoDir, "remote", "worker");
13687
- const wranglerToml = join23(workerDir, "wrangler.toml");
14106
+ const workerDir = join24(repoDir, "remote", "worker");
14107
+ const wranglerToml = join24(workerDir, "wrangler.toml");
13688
14108
  yield { message: "Installing Worker dependencies (npm install)\u2026" };
13689
14109
  const install = await runCmd("npm", ["install", "--no-audit", "--no-fund", "--loglevel=error"], {
13690
14110
  cwd: workerDir,
@@ -13773,7 +14193,7 @@ ${(install.stderr || install.stdout).slice(-1200).trim()}`,
13773
14193
  ok: true
13774
14194
  };
13775
14195
  yield { message: "Patching wrangler.toml\u2026" };
13776
- let toml = await readFile14(wranglerToml, "utf8");
14196
+ let toml = await readFile15(wranglerToml, "utf8");
13777
14197
  toml = toml.replace(/^name\s*=\s*"[^"]+"/m, `name = "${workerName}"`);
13778
14198
  toml = toml.replace(
13779
14199
  /(\[\[kv_namespaces\]\][\s\S]*?binding\s*=\s*"OAUTH_KV"[\s\S]*?id\s*=\s*")[^"]+(")/,
@@ -13806,7 +14226,7 @@ enabled = false
13806
14226
  invocation_logs = true
13807
14227
  `;
13808
14228
  }
13809
- await writeFile10(wranglerToml, toml, "utf8");
14229
+ await writeFile11(wranglerToml, toml, "utf8");
13810
14230
  yield {
13811
14231
  message: workerExists ? "wrangler.toml patched (name, KV id, [[artifacts]] stripped)" : "wrangler.toml patched (name, KV id, [[artifacts]] stripped, DO migrations added)",
13812
14232
  ok: true
@@ -13999,128 +14419,449 @@ var init_deploy_commute = __esm({
13999
14419
  }
14000
14420
  });
14001
14421
 
14002
- // src/commands/builtins.ts
14003
- var BUILTIN_COMMANDS, BUILTIN_COMMAND_NAMES;
14004
- var init_builtins = __esm({
14005
- "src/commands/builtins.ts"() {
14006
- "use strict";
14007
- BUILTIN_COMMANDS = [
14008
- { name: "help", description: "Show keybindings and command list", source: "builtin" },
14009
- { name: "model", argHint: "[list|<id>]", description: "Pick model (no args opens picker)", source: "builtin" },
14010
- { name: "mode", argHint: "edit|plan|auto|multi-agent-experimental", description: "Switch agent mode", source: "builtin" },
14011
- { name: "multi-agent", argHint: "[enable|disable|status|setup]", description: "Configure multi-agent (endpoint, auto-implement, set up)", source: "builtin" },
14012
- { name: "theme", argHint: "[<name>]", description: "Switch color theme", source: "builtin" },
14013
- { name: "ui", argHint: "ink|camouflage", description: "Switch UI engine (takes effect on next launch)", source: "builtin" },
14014
- { name: "plan", description: "Switch to plan mode", source: "builtin" },
14015
- { name: "auto", description: "Switch to auto mode", source: "builtin" },
14016
- { name: "edit", description: "Switch to edit mode", source: "builtin" },
14017
- { name: "reasoning", description: "Toggle reasoning visibility", source: "builtin" },
14018
- { name: "memory", argHint: "[on|off|clear|search ...]", description: "Manage memory", source: "builtin" },
14019
- { name: "cost", argHint: "[on|off]", description: "Show cost report or toggle attribution", source: "builtin" },
14020
- { name: "gateway", argHint: "[status|off|<id>|cache-ttl|skip-cache|...]", description: "Manage AI Gateway", source: "builtin" },
14021
- { name: "mcp", argHint: "[list|reload]", description: "Manage MCP servers", source: "builtin" },
14022
- { name: "lsp", argHint: "[config|list|reload|scope]", description: "Manage language servers", source: "builtin" },
14023
- { name: "hooks", argHint: "[list|recommended|enable <id>|disable <id>|path|reload]", description: "Manage lifecycle hooks", source: "builtin" },
14024
- { name: "skills", argHint: "[list|add|edit|delete|enable|disable]", description: "Manage skills", source: "builtin" },
14025
- { name: "command", argHint: "[create|edit|delete|list]", description: "Manage custom slash commands", source: "builtin" },
14026
- { name: "resume", description: "Pick a past conversation to resume", source: "builtin" },
14027
- { name: "checkpoint", argHint: "[label]", description: "Save current point in session", source: "builtin" },
14028
- { name: "checkpoints", description: "List checkpoints in current session", source: "builtin" },
14029
- { name: "compact", description: "Summarize old turns to free context", source: "builtin" },
14030
- { name: "clear", description: "Clear current conversation", source: "builtin" },
14031
- { name: "fresh", description: "Reset session and start fresh with the last plan", source: "builtin" },
14032
- { name: "init", description: "Scan repo and write KIMI.md", source: "builtin" },
14033
- { name: "remote", argHint: "<prompt>", description: "Run a remote session on Cloudflare", source: "builtin" },
14034
- { name: "update", description: "Check for updates", source: "builtin" },
14035
- { name: "hello", description: "Send a voice note to the creator", source: "builtin" },
14036
- { name: "report", argHint: "[send] [note]", description: "Report the last API error with diagnostic logs", source: "builtin" },
14037
- { name: "shell", argHint: "[auto|bash|cmd|powershell|<path>]", description: "Show or set shell for bash tool", source: "builtin" },
14038
- { name: "logout", description: "Clear stored credentials", source: "builtin" },
14039
- { name: "exit", description: "Exit kimiflare", source: "builtin" }
14040
- ];
14041
- BUILTIN_COMMAND_NAMES = new Set(
14042
- BUILTIN_COMMANDS.map((c) => c.name.toLowerCase())
14422
+ // src/util/image.ts
14423
+ import { readFile as readFile16 } from "fs/promises";
14424
+ import { basename as basename3 } from "path";
14425
+ async function encodeImageFile(filePath) {
14426
+ const buf = await readFile16(filePath);
14427
+ if (buf.byteLength > MAX_IMAGE_BYTES) {
14428
+ throw new Error(
14429
+ `image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
14043
14430
  );
14044
14431
  }
14045
- });
14046
-
14047
- // src/agent/llm-summarize.ts
14048
- function indexOfNthUserFromEnd(messages, n) {
14049
- let seen = 0;
14050
- for (let i = messages.length - 1; i >= 0; i--) {
14051
- if (messages[i].role === "user") {
14052
- seen++;
14053
- if (seen === n) return i;
14054
- }
14055
- }
14056
- return -1;
14057
- }
14058
- async function summarizeMessagesViaLlm(opts2) {
14059
- const keep = opts2.keepLastTurns ?? 4;
14060
- const messages = opts2.messages;
14061
- let prefixEnd = 0;
14062
- while (prefixEnd < messages.length && messages[prefixEnd].role === "system") {
14063
- prefixEnd++;
14064
- }
14065
- const prefix = messages.slice(0, prefixEnd);
14066
- if (prefix.length === 0) {
14067
- return { summary: "", newMessages: messages, replacedCount: 0 };
14068
- }
14069
- const cutoffUserIdx = indexOfNthUserFromEnd(messages, keep);
14070
- const firstKeepIdx = cutoffUserIdx >= 0 ? cutoffUserIdx : messages.length;
14071
- const toSummarize = messages.slice(prefixEnd, firstKeepIdx);
14072
- const toKeep = messages.slice(firstKeepIdx);
14073
- if (toSummarize.length === 0) {
14074
- return { summary: "", newMessages: messages, replacedCount: 0 };
14075
- }
14076
- const transcript = toSummarize.map((m) => {
14077
- const contentStr = typeof m.content === "string" ? m.content : m.content?.map((p) => p.type === "text" ? p.text : "[image]").join(" ") ?? "";
14078
- if (m.role === "tool") {
14079
- const snippet = contentStr.slice(0, 500);
14080
- return `[tool ${m.name ?? ""}] ${snippet}`;
14081
- }
14082
- if (m.role === "assistant") {
14083
- const calls = m.tool_calls ? ` (tool_calls: ${m.tool_calls.map((c) => c.function.name).join(", ")})` : "";
14084
- return `[assistant]${calls} ${contentStr}`;
14085
- }
14086
- return `[${m.role}] ${contentStr}`;
14087
- }).join("\n");
14088
- let summary = "";
14089
- const events = runKimi({
14090
- accountId: opts2.accountId,
14091
- apiToken: opts2.apiToken,
14092
- model: opts2.model,
14093
- messages: [
14094
- { role: "system", content: SUMMARY_SYSTEM },
14095
- { role: "user", content: `Summarize this session so it can be replaced by your summary:
14096
-
14097
- ${transcript}` }
14098
- ],
14099
- signal: opts2.signal,
14100
- temperature: 0.1,
14101
- reasoningEffort: "low",
14102
- gateway: opts2.gateway,
14103
- idleTimeoutMs: 6e4
14104
- });
14105
- for await (const ev of events) {
14106
- if (ev.type === "text") summary += ev.delta;
14107
- }
14108
- const summaryMsg = {
14109
- role: "user",
14110
- content: `[compacted summary of earlier turns]
14111
- ${summary.trim()}`
14112
- };
14432
+ const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
14433
+ const mime = EXT_TO_MIME[ext] ?? "image/jpeg";
14434
+ const b64 = buf.toString("base64");
14113
14435
  return {
14114
- summary: summary.trim(),
14115
- newMessages: [...prefix, summaryMsg, ...toKeep],
14116
- replacedCount: toSummarize.length
14436
+ filename: basename3(filePath),
14437
+ mime,
14438
+ dataUrl: `data:${mime};base64,${b64}`
14117
14439
  };
14118
14440
  }
14119
- var SUMMARY_SYSTEM;
14120
- var init_llm_summarize = __esm({
14121
- "src/agent/llm-summarize.ts"() {
14122
- "use strict";
14123
- init_client();
14441
+ function isImagePath(path) {
14442
+ const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
14443
+ return ext in EXT_TO_MIME;
14444
+ }
14445
+ var MAX_IMAGE_BYTES, EXT_TO_MIME;
14446
+ var init_image = __esm({
14447
+ "src/util/image.ts"() {
14448
+ "use strict";
14449
+ MAX_IMAGE_BYTES = 5 * 1024 * 1024;
14450
+ EXT_TO_MIME = {
14451
+ ".png": "image/png",
14452
+ ".jpg": "image/jpeg",
14453
+ ".jpeg": "image/jpeg",
14454
+ ".gif": "image/gif",
14455
+ ".webp": "image/webp",
14456
+ ".bmp": "image/bmp"
14457
+ };
14458
+ }
14459
+ });
14460
+
14461
+ // src/ui/app-helpers.ts
14462
+ var app_helpers_exports = {};
14463
+ __export(app_helpers_exports, {
14464
+ AUTO_COMPACT_THRESHOLD: () => AUTO_COMPACT_THRESHOLD,
14465
+ CONTEXT_LIMIT: () => CONTEXT_LIMIT,
14466
+ DEFAULT_AUTO_FRESH_SUGGESTION_TURNS: () => DEFAULT_AUTO_FRESH_SUGGESTION_TURNS,
14467
+ FEEDBACK_WORKER_URL: () => FEEDBACK_WORKER_URL,
14468
+ MAX_EVENTS: () => MAX_EVENTS,
14469
+ MAX_IMAGES_PER_MESSAGE: () => MAX_IMAGES_PER_MESSAGE,
14470
+ buildFilePickerIgnoreList: () => buildFilePickerIgnoreList,
14471
+ capEvents: () => capEvents,
14472
+ compactEventsVisual: () => compactEventsVisual,
14473
+ detectGitBranch: () => detectGitBranch,
14474
+ detectGitHubRepo: () => detectGitHubRepo,
14475
+ findImagePaths: () => findImagePaths,
14476
+ formatTokens: () => formatTokens,
14477
+ gatewayFromConfig: () => gatewayFromConfig,
14478
+ gatewayUsageLookupFromConfig: () => gatewayUsageLookupFromConfig,
14479
+ makePrefixMessages: () => makePrefixMessages,
14480
+ mkAssistantId: () => mkAssistantId,
14481
+ mkKey: () => mkKey,
14482
+ openBrowser: () => openBrowser,
14483
+ rebuildSystemPromptForMode: () => rebuildSystemPromptForMode,
14484
+ trackRecentFile: () => trackRecentFile
14485
+ });
14486
+ import { execSync as execSync3, spawn as spawn5 } from "child_process";
14487
+ import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
14488
+ import { join as join25 } from "path";
14489
+ import { platform as platform3 } from "os";
14490
+ function buildFilePickerIgnoreList(cwd) {
14491
+ const hardcoded = [
14492
+ // Dependencies
14493
+ "**/node_modules/**",
14494
+ "**/vendor/**",
14495
+ "**/.bundle/**",
14496
+ "**/bower_components/**",
14497
+ // Version control
14498
+ "**/.git/**",
14499
+ "**/.svn/**",
14500
+ "**/.hg/**",
14501
+ // Build / output directories
14502
+ "**/dist/**",
14503
+ "**/build/**",
14504
+ "**/out/**",
14505
+ "**/public/**",
14506
+ "**/.next/**",
14507
+ "**/.nuxt/**",
14508
+ "**/.svelte-kit/**",
14509
+ "**/.vercel/**",
14510
+ "**/.netlify/**",
14511
+ "**/target/**",
14512
+ "**/bin/**",
14513
+ "**/obj/**",
14514
+ "**/Debug/**",
14515
+ "**/Release/**",
14516
+ "**/.gradle/**",
14517
+ // Caches
14518
+ "**/.cache/**",
14519
+ "**/.parcel-cache/**",
14520
+ "**/.turbo/**",
14521
+ "**/.eslintcache",
14522
+ "**/.stylelintcache",
14523
+ "**/.rpt2_cache/**",
14524
+ "**/.rts2_cache/**",
14525
+ // Temporary
14526
+ "**/tmp/**",
14527
+ "**/temp/**",
14528
+ "**/*.tmp",
14529
+ // Coverage
14530
+ "**/coverage/**",
14531
+ "**/.nyc_output/**",
14532
+ // OS files
14533
+ "**/.DS_Store",
14534
+ "**/Thumbs.db",
14535
+ // Logs
14536
+ "**/*.log",
14537
+ "**/logs/**",
14538
+ // Lock files (auto-generated, usually huge)
14539
+ "**/package-lock.json",
14540
+ "**/yarn.lock",
14541
+ "**/pnpm-lock.yaml",
14542
+ "**/bun.lockb",
14543
+ "**/Cargo.lock",
14544
+ "**/Gemfile.lock",
14545
+ "**/composer.lock",
14546
+ "**/Pipfile.lock",
14547
+ "**/poetry.lock",
14548
+ "**/go.sum",
14549
+ // Minified / source maps
14550
+ "**/*.min.js",
14551
+ "**/*.min.css",
14552
+ "**/*.map",
14553
+ // kimiflare internal
14554
+ "**/.kimiflare/**",
14555
+ // IDE (usually not relevant to mention)
14556
+ "**/.idea/**"
14557
+ ];
14558
+ const gitignorePatterns = [];
14559
+ try {
14560
+ const gitignorePath = join25(cwd, ".gitignore");
14561
+ const stats = statSync4(gitignorePath);
14562
+ if (stats.size > MAX_GITIGNORE_SIZE) {
14563
+ return hardcoded;
14564
+ }
14565
+ const content = readFileSync4(gitignorePath, "utf-8");
14566
+ for (const line of content.split(/\r?\n/)) {
14567
+ const trimmed = line.trim();
14568
+ if (!trimmed || trimmed.startsWith("#")) continue;
14569
+ if (trimmed.startsWith("!")) continue;
14570
+ let pattern = trimmed;
14571
+ const isAnchored = pattern.startsWith("/");
14572
+ const isDir = pattern.endsWith("/");
14573
+ if (isAnchored) pattern = pattern.slice(1);
14574
+ if (isDir) pattern = pattern.slice(0, -1);
14575
+ if (!pattern) continue;
14576
+ if (isAnchored) {
14577
+ gitignorePatterns.push(isDir ? pattern + "/**" : pattern);
14578
+ } else {
14579
+ gitignorePatterns.push(isDir ? "**/" + pattern + "/**" : "**/" + pattern);
14580
+ }
14581
+ }
14582
+ } catch {
14583
+ }
14584
+ return [...hardcoded, ...gitignorePatterns];
14585
+ }
14586
+ function gatewayFromConfig(cfg) {
14587
+ if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
14588
+ if (!cfg.aiGatewayId) return void 0;
14589
+ return {
14590
+ id: cfg.aiGatewayId,
14591
+ cacheTtl: cfg.aiGatewayCacheTtl,
14592
+ skipCache: cfg.aiGatewaySkipCache,
14593
+ collectLogPayload: cfg.aiGatewayCollectLogPayload,
14594
+ metadata: cfg.aiGatewayMetadata
14595
+ };
14596
+ }
14597
+ function gatewayUsageLookupFromConfig(cfg, meta) {
14598
+ if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
14599
+ if (!cfg.aiGatewayId || !meta) return void 0;
14600
+ return {
14601
+ accountId: cfg.accountId,
14602
+ apiToken: cfg.apiToken,
14603
+ gatewayId: cfg.aiGatewayId,
14604
+ meta
14605
+ };
14606
+ }
14607
+ function openBrowser(url) {
14608
+ const cmd = platform3() === "darwin" ? "open" : platform3() === "win32" ? "start" : "xdg-open";
14609
+ const child = spawn5(cmd, [url], { detached: true, stdio: "ignore" });
14610
+ child.unref();
14611
+ }
14612
+ function detectGitHubRepo(cachedRepo) {
14613
+ if (cachedRepo) {
14614
+ const parts = cachedRepo.split("/");
14615
+ if (parts.length === 2) return { owner: parts[0], name: parts[1] };
14616
+ }
14617
+ try {
14618
+ const remoteUrl = execSync3("git remote get-url origin", { cwd: process.cwd(), encoding: "utf8" }).trim().replace(/\/+$/, "");
14619
+ const httpsMatch = remoteUrl.match(/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
14620
+ if (httpsMatch) return { owner: httpsMatch[1], name: httpsMatch[2] };
14621
+ const sshMatch = remoteUrl.match(/github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
14622
+ if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
14623
+ } catch {
14624
+ }
14625
+ return null;
14626
+ }
14627
+ function detectGitBranch() {
14628
+ try {
14629
+ return execSync3("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim() || null;
14630
+ } catch {
14631
+ return null;
14632
+ }
14633
+ }
14634
+ function formatTokens(n) {
14635
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
14636
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
14637
+ return String(n);
14638
+ }
14639
+ function trackRecentFile(ref, path, max = 10) {
14640
+ ref.current.set(path, Date.now());
14641
+ if (ref.current.size > max) {
14642
+ let oldest = null;
14643
+ let oldestTime = Infinity;
14644
+ for (const [p, t] of ref.current) {
14645
+ if (t < oldestTime) {
14646
+ oldestTime = t;
14647
+ oldest = p;
14648
+ }
14649
+ }
14650
+ if (oldest) ref.current.delete(oldest);
14651
+ }
14652
+ }
14653
+ function capEvents(prev) {
14654
+ if (prev.length <= MAX_EVENTS) return prev;
14655
+ return prev.slice(prev.length - MAX_EVENTS);
14656
+ }
14657
+ function compactEventsVisual(prev, keepLastTurns) {
14658
+ let seen = 0;
14659
+ let cutoff = -1;
14660
+ for (let i = prev.length - 1; i >= 0; i--) {
14661
+ if (prev[i].kind === "user") {
14662
+ seen++;
14663
+ if (seen === keepLastTurns + 1) {
14664
+ cutoff = i;
14665
+ break;
14666
+ }
14667
+ }
14668
+ }
14669
+ if (cutoff <= 0) return prev;
14670
+ const kept = prev.slice(cutoff);
14671
+ return [
14672
+ { kind: "info", key: mkKey(), text: `\xB7\xB7\xB7 ${cutoff} earlier messages compacted \xB7\xB7\xB7` },
14673
+ ...kept
14674
+ ];
14675
+ }
14676
+ function makePrefixMessages(cacheStable, model, mode, tools) {
14677
+ if (cacheStable) {
14678
+ return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
14679
+ }
14680
+ return [
14681
+ {
14682
+ role: "system",
14683
+ content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
14684
+ }
14685
+ ];
14686
+ }
14687
+ function rebuildSystemPromptForMode(messages, cacheStable, model, mode, tools) {
14688
+ if (cacheStable) {
14689
+ const rebuilt = buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
14690
+ messages[0] = rebuilt[0];
14691
+ if (rebuilt[1]) {
14692
+ messages[1] = rebuilt[1];
14693
+ }
14694
+ } else {
14695
+ messages[0] = {
14696
+ role: "system",
14697
+ content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
14698
+ };
14699
+ }
14700
+ }
14701
+ function findImagePaths(text) {
14702
+ const paths = [];
14703
+ const quotedRegex = /"([^"]+)"|'([^']+)'/g;
14704
+ let match;
14705
+ while ((match = quotedRegex.exec(text)) !== null) {
14706
+ const path = match[1] ?? match[2];
14707
+ if (path && isImagePath(path) && existsSync4(path)) {
14708
+ paths.push(path);
14709
+ }
14710
+ }
14711
+ const remaining = text.replace(/"[^"]+"|'[^']+'/g, "");
14712
+ const ESCAPED_SPACE = "\0";
14713
+ const processed = remaining.replace(/\\ /g, ESCAPED_SPACE);
14714
+ for (const token of processed.split(/\s+/)) {
14715
+ const clean = token.replace(new RegExp(ESCAPED_SPACE, "g"), " ").replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
14716
+ if (clean && isImagePath(clean) && existsSync4(clean) && !paths.includes(clean)) {
14717
+ paths.push(clean);
14718
+ }
14719
+ }
14720
+ return paths;
14721
+ }
14722
+ var MAX_GITIGNORE_SIZE, CONTEXT_LIMIT, AUTO_COMPACT_THRESHOLD, MAX_EVENTS, DEFAULT_AUTO_FRESH_SUGGESTION_TURNS, MAX_IMAGES_PER_MESSAGE, FEEDBACK_WORKER_URL, nextKey, mkKey, nextAssistantId, mkAssistantId;
14723
+ var init_app_helpers = __esm({
14724
+ "src/ui/app-helpers.ts"() {
14725
+ "use strict";
14726
+ init_system_prompt();
14727
+ init_image();
14728
+ MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
14729
+ CONTEXT_LIMIT = 262e3;
14730
+ AUTO_COMPACT_THRESHOLD = 0.8;
14731
+ MAX_EVENTS = 500;
14732
+ DEFAULT_AUTO_FRESH_SUGGESTION_TURNS = 30;
14733
+ MAX_IMAGES_PER_MESSAGE = 10;
14734
+ FEEDBACK_WORKER_URL = "https://hello.kimiflare.com";
14735
+ nextKey = 1;
14736
+ mkKey = () => `evt_${nextKey++}`;
14737
+ nextAssistantId = 1;
14738
+ mkAssistantId = () => nextAssistantId++;
14739
+ }
14740
+ });
14741
+
14742
+ // src/commands/builtins.ts
14743
+ var BUILTIN_COMMANDS, BUILTIN_COMMAND_NAMES;
14744
+ var init_builtins = __esm({
14745
+ "src/commands/builtins.ts"() {
14746
+ "use strict";
14747
+ BUILTIN_COMMANDS = [
14748
+ { name: "help", description: "Show keybindings and command list", source: "builtin" },
14749
+ { name: "model", argHint: "[list|<id>]", description: "Pick model (no args opens picker)", source: "builtin" },
14750
+ { name: "mode", argHint: "edit|plan|auto|multi-agent-experimental", description: "Switch agent mode", source: "builtin" },
14751
+ { name: "multi-agent", argHint: "[enable|disable|status|setup]", description: "Configure multi-agent (endpoint, auto-implement, set up)", source: "builtin" },
14752
+ { name: "theme", argHint: "[<name>]", description: "Switch color theme", source: "builtin" },
14753
+ { name: "ui", argHint: "ink|camouflage", description: "Switch UI engine (takes effect on next launch)", source: "builtin" },
14754
+ { name: "plan", description: "Switch to plan mode", source: "builtin" },
14755
+ { name: "auto", description: "Switch to auto mode", source: "builtin" },
14756
+ { name: "edit", description: "Switch to edit mode", source: "builtin" },
14757
+ { name: "reasoning", description: "Toggle reasoning visibility", source: "builtin" },
14758
+ { name: "memory", argHint: "[on|off|clear|search ...]", description: "Manage memory", source: "builtin" },
14759
+ { name: "cost", argHint: "[on|off]", description: "Show cost report or toggle attribution", source: "builtin" },
14760
+ { name: "gateway", argHint: "[status|off|<id>|cache-ttl|skip-cache|...]", description: "Manage AI Gateway", source: "builtin" },
14761
+ { name: "mcp", argHint: "[list|reload]", description: "Manage MCP servers", source: "builtin" },
14762
+ { name: "lsp", argHint: "[config|list|reload|scope]", description: "Manage language servers", source: "builtin" },
14763
+ { name: "hooks", argHint: "[list|recommended|enable <id>|disable <id>|path|reload]", description: "Manage lifecycle hooks", source: "builtin" },
14764
+ { name: "skills", argHint: "[list|add|edit|delete|enable|disable]", description: "Manage skills", source: "builtin" },
14765
+ { name: "command", argHint: "[create|edit|delete|list]", description: "Manage custom slash commands", source: "builtin" },
14766
+ { name: "resume", description: "Pick a past conversation to resume", source: "builtin" },
14767
+ { name: "checkpoint", argHint: "[label]", description: "Save current point in session", source: "builtin" },
14768
+ { name: "checkpoints", description: "List checkpoints in current session", source: "builtin" },
14769
+ { name: "compact", description: "Summarize old turns to free context", source: "builtin" },
14770
+ { name: "clear", description: "Clear current conversation", source: "builtin" },
14771
+ { name: "fresh", description: "Start a fresh session with a summarized plan or continuation context", source: "builtin" },
14772
+ { name: "init", description: "Scan repo and write KIMI.md", source: "builtin" },
14773
+ { name: "remote", argHint: "<prompt>", description: "Run a remote session on Cloudflare", source: "builtin" },
14774
+ { name: "update", description: "Check for updates", source: "builtin" },
14775
+ { name: "hello", description: "Send a voice note to the creator", source: "builtin" },
14776
+ { name: "report", argHint: "[send] [note]", description: "Report the last API error with diagnostic logs", source: "builtin" },
14777
+ { name: "shell", argHint: "[auto|bash|cmd|powershell|<path>]", description: "Show or set shell for bash tool", source: "builtin" },
14778
+ { name: "changelog-image", argHint: "[owner/repo] [days]", description: "Generate a changelog image from merged PRs", source: "builtin" },
14779
+ { name: "logout", description: "Clear stored credentials", source: "builtin" },
14780
+ { name: "exit", description: "Exit kimiflare", source: "builtin" }
14781
+ ];
14782
+ BUILTIN_COMMAND_NAMES = new Set(
14783
+ BUILTIN_COMMANDS.map((c) => c.name.toLowerCase())
14784
+ );
14785
+ }
14786
+ });
14787
+
14788
+ // src/agent/llm-summarize.ts
14789
+ function indexOfNthUserFromEnd(messages, n) {
14790
+ let seen = 0;
14791
+ for (let i = messages.length - 1; i >= 0; i--) {
14792
+ if (messages[i].role === "user") {
14793
+ seen++;
14794
+ if (seen === n) return i;
14795
+ }
14796
+ }
14797
+ return -1;
14798
+ }
14799
+ async function summarizeMessagesViaLlm(opts2) {
14800
+ const keep = opts2.keepLastTurns ?? 4;
14801
+ const messages = opts2.messages;
14802
+ let prefixEnd = 0;
14803
+ while (prefixEnd < messages.length && messages[prefixEnd].role === "system") {
14804
+ prefixEnd++;
14805
+ }
14806
+ const prefix = messages.slice(0, prefixEnd);
14807
+ if (prefix.length === 0) {
14808
+ return { summary: "", newMessages: messages, replacedCount: 0 };
14809
+ }
14810
+ const cutoffUserIdx = indexOfNthUserFromEnd(messages, keep);
14811
+ const firstKeepIdx = cutoffUserIdx >= 0 ? cutoffUserIdx : messages.length;
14812
+ const toSummarize = messages.slice(prefixEnd, firstKeepIdx);
14813
+ const toKeep = messages.slice(firstKeepIdx);
14814
+ if (toSummarize.length === 0) {
14815
+ return { summary: "", newMessages: messages, replacedCount: 0 };
14816
+ }
14817
+ const transcript = toSummarize.map((m) => {
14818
+ const contentStr = typeof m.content === "string" ? m.content : m.content?.map((p) => p.type === "text" ? p.text : "[image]").join(" ") ?? "";
14819
+ if (m.role === "tool") {
14820
+ const snippet = contentStr.slice(0, 500);
14821
+ return `[tool ${m.name ?? ""}] ${snippet}`;
14822
+ }
14823
+ if (m.role === "assistant") {
14824
+ const calls = m.tool_calls ? ` (tool_calls: ${m.tool_calls.map((c) => c.function.name).join(", ")})` : "";
14825
+ return `[assistant]${calls} ${contentStr}`;
14826
+ }
14827
+ return `[${m.role}] ${contentStr}`;
14828
+ }).join("\n");
14829
+ let summary = "";
14830
+ const events = runKimi({
14831
+ accountId: opts2.accountId,
14832
+ apiToken: opts2.apiToken,
14833
+ model: opts2.model,
14834
+ messages: [
14835
+ { role: "system", content: SUMMARY_SYSTEM },
14836
+ { role: "user", content: `Summarize this session so it can be replaced by your summary:
14837
+
14838
+ ${transcript}` }
14839
+ ],
14840
+ signal: opts2.signal,
14841
+ temperature: 0.1,
14842
+ reasoningEffort: "low",
14843
+ gateway: opts2.gateway,
14844
+ idleTimeoutMs: 6e4
14845
+ });
14846
+ for await (const ev of events) {
14847
+ if (ev.type === "text") summary += ev.delta;
14848
+ }
14849
+ const summaryMsg = {
14850
+ role: "user",
14851
+ content: `[compacted summary of earlier turns]
14852
+ ${summary.trim()}`
14853
+ };
14854
+ return {
14855
+ summary: summary.trim(),
14856
+ newMessages: [...prefix, summaryMsg, ...toKeep],
14857
+ replacedCount: toSummarize.length
14858
+ };
14859
+ }
14860
+ var SUMMARY_SYSTEM;
14861
+ var init_llm_summarize = __esm({
14862
+ "src/agent/llm-summarize.ts"() {
14863
+ "use strict";
14864
+ init_client();
14124
14865
  SUMMARY_SYSTEM = `You are summarizing a terminal coding session so it can fit back into a short context window. Produce a dense summary that captures:
14125
14866
  - The user's goal(s) and what they've asked for.
14126
14867
  - Files read or modified, with paths.
@@ -14156,6 +14897,155 @@ var init_distill = __esm({
14156
14897
  }
14157
14898
  });
14158
14899
 
14900
+ // src/agent/continuation-summary.ts
14901
+ import { execSync as execSync4 } from "child_process";
14902
+ function extractFirstUserGoal(messages) {
14903
+ const texts = [];
14904
+ for (const m of messages) {
14905
+ if (m.role !== "user") continue;
14906
+ let text = "";
14907
+ if (typeof m.content === "string") {
14908
+ text = m.content;
14909
+ } else if (Array.isArray(m.content)) {
14910
+ text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
14911
+ }
14912
+ text = text.trim();
14913
+ if (text.length > 5) {
14914
+ texts.push(text);
14915
+ if (texts.length >= 3) break;
14916
+ }
14917
+ }
14918
+ return texts.join("\n---\n");
14919
+ }
14920
+ function extractRecentAssistantMessages(messages, count = 3) {
14921
+ const texts = [];
14922
+ for (let i = messages.length - 1; i >= 0 && texts.length < count; i--) {
14923
+ const m = messages[i];
14924
+ if (m?.role !== "assistant") continue;
14925
+ let text = "";
14926
+ if (typeof m.content === "string") {
14927
+ text = m.content;
14928
+ } else if (Array.isArray(m.content)) {
14929
+ text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
14930
+ }
14931
+ text = text.trim();
14932
+ if (text.length > 10) {
14933
+ texts.unshift(text);
14934
+ }
14935
+ }
14936
+ return texts.join("\n---\n");
14937
+ }
14938
+ function gatherGitEvidence() {
14939
+ const pieces = [];
14940
+ try {
14941
+ const branch = execSync4("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim();
14942
+ if (branch) pieces.push(`Branch: ${branch}`);
14943
+ } catch {
14944
+ }
14945
+ try {
14946
+ const log2 = execSync4("git log --oneline -5", { cwd: process.cwd(), encoding: "utf8" }).trim();
14947
+ if (log2) pieces.push(`Recent commits:
14948
+ ${log2}`);
14949
+ } catch {
14950
+ }
14951
+ try {
14952
+ const status = execSync4("git status --short", { cwd: process.cwd(), encoding: "utf8" }).trim();
14953
+ if (status) pieces.push(`Working tree changes:
14954
+ ${status}`);
14955
+ } catch {
14956
+ }
14957
+ return pieces.join("\n\n");
14958
+ }
14959
+ async function gatherMemoryEvidence(manager, enabled, signal) {
14960
+ if (!enabled) return "";
14961
+ try {
14962
+ const cwd = process.cwd();
14963
+ const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 10 });
14964
+ const highSignal = results.filter(
14965
+ (r) => ["edit_event", "task", "instruction"].includes(r.memory.category)
14966
+ );
14967
+ if (highSignal.length === 0) return "";
14968
+ const synthesized = await manager.synthesizeRecalled(highSignal, signal);
14969
+ return synthesized ? `Recorded work log:
14970
+ ${synthesized}` : "";
14971
+ } catch {
14972
+ return "";
14973
+ }
14974
+ }
14975
+ async function runKimiText2(opts2) {
14976
+ const events = runKimi({
14977
+ accountId: opts2.accountId,
14978
+ apiToken: opts2.apiToken,
14979
+ model: opts2.model,
14980
+ messages: opts2.messages,
14981
+ temperature: 0.1,
14982
+ reasoningEffort: "low",
14983
+ gateway: opts2.gateway,
14984
+ signal: opts2.signal
14985
+ });
14986
+ let text = "";
14987
+ for await (const ev of events) {
14988
+ if (ev.type === "text") text += ev.delta;
14989
+ }
14990
+ return text.trim();
14991
+ }
14992
+ async function generateContinuationSummary(opts2) {
14993
+ const { messages, mode } = opts2;
14994
+ if (mode === "plan") {
14995
+ return distillSessionPlan(messages);
14996
+ }
14997
+ const goal = extractFirstUserGoal(messages);
14998
+ const recentAssistant = extractRecentAssistantMessages(messages);
14999
+ const gitEvidence = gatherGitEvidence();
15000
+ const memoryEvidence = opts2.memoryManager ? await gatherMemoryEvidence(opts2.memoryManager, opts2.memoryEnabled ?? false, opts2.signal) : "";
15001
+ const evidenceParts = [];
15002
+ if (goal) evidenceParts.push(`## Original goal(s)
15003
+ ${goal}`);
15004
+ if (recentAssistant) evidenceParts.push(`## Recent assistant messages
15005
+ ${recentAssistant}`);
15006
+ if (gitEvidence) evidenceParts.push(`## Git state
15007
+ ${gitEvidence}`);
15008
+ if (memoryEvidence) evidenceParts.push(`## ${memoryEvidence}`);
15009
+ if (evidenceParts.length === 0) {
15010
+ return null;
15011
+ }
15012
+ const userPrompt = evidenceParts.join("\n\n");
15013
+ const summary = await runKimiText2({
15014
+ accountId: opts2.accountId,
15015
+ apiToken: opts2.apiToken,
15016
+ model: opts2.model,
15017
+ gateway: opts2.gateway,
15018
+ signal: opts2.signal,
15019
+ messages: [
15020
+ { role: "system", content: HANDOFF_SYSTEM },
15021
+ { role: "user", content: userPrompt }
15022
+ ]
15023
+ });
15024
+ return summary || null;
15025
+ }
15026
+ var HANDOFF_SYSTEM;
15027
+ var init_continuation_summary = __esm({
15028
+ "src/agent/continuation-summary.ts"() {
15029
+ "use strict";
15030
+ init_client();
15031
+ init_distill();
15032
+ HANDOFF_SYSTEM = `You are a session-continuation engine. Given evidence from a coding session, produce a dense handoff document so a new agent can pick up exactly where this one left off.
15033
+
15034
+ Output format (use these exact headings):
15035
+ Goal: what the user originally asked for.
15036
+ Completed: files modified, tests added, key decisions, commits made.
15037
+ Remaining: what still needs to be done.
15038
+ Current state: any open errors, incomplete refactors, or pending work.
15039
+ Context: relevant file paths or architectural notes.
15040
+
15041
+ Rules:
15042
+ - Be terse but complete. A new agent with zero prior context must be able to continue.
15043
+ - Do not include chat-style pleasantries.
15044
+ - Do not speculate beyond the evidence provided.
15045
+ - Aim for ~300-600 tokens.`;
15046
+ }
15047
+ });
15048
+
14159
15049
  // src/ui/greetings.ts
14160
15050
  function pick(arr) {
14161
15051
  if (arr.length === 0) throw new Error("pick() called with empty array");
@@ -15021,24 +15911,24 @@ var init_theme = __esm({
15021
15911
  });
15022
15912
 
15023
15913
  // src/util/clipboard.ts
15024
- import { execSync as execSync3 } from "child_process";
15025
- import { platform as platform3 } from "os";
15914
+ import { execSync as execSync5 } from "child_process";
15915
+ import { platform as platform4 } from "os";
15026
15916
  function writeToClipboard(text) {
15027
- const os2 = platform3();
15917
+ const os2 = platform4();
15028
15918
  try {
15029
15919
  if (os2 === "darwin") {
15030
- execSync3("pbcopy", { input: text, timeout: 5e3 });
15920
+ execSync5("pbcopy", { input: text, timeout: 5e3 });
15031
15921
  return { success: true, message: "Copied to clipboard" };
15032
15922
  }
15033
15923
  if (os2 === "win32") {
15034
- execSync3("clip", { input: text, timeout: 5e3 });
15924
+ execSync5("clip", { input: text, timeout: 5e3 });
15035
15925
  return { success: true, message: "Copied to clipboard" };
15036
15926
  }
15037
15927
  try {
15038
- execSync3("xclip -selection clipboard", { input: text, timeout: 5e3 });
15928
+ execSync5("xclip -selection clipboard", { input: text, timeout: 5e3 });
15039
15929
  return { success: true, message: "Copied to clipboard" };
15040
15930
  } catch {
15041
- execSync3("xsel --clipboard --input", { input: text, timeout: 5e3 });
15931
+ execSync5("xsel --clipboard --input", { input: text, timeout: 5e3 });
15042
15932
  return { success: true, message: "Copied to clipboard" };
15043
15933
  }
15044
15934
  } catch {
@@ -15359,8 +16249,8 @@ var init_frontmatter = __esm({
15359
16249
  });
15360
16250
 
15361
16251
  // src/skills/loader.ts
15362
- import { readFile as readFile15, readdir as readdir6, stat as stat6 } from "fs/promises";
15363
- import { join as join24, extname } from "path";
16252
+ import { readFile as readFile17, readdir as readdir6, stat as stat6 } from "fs/promises";
16253
+ import { join as join26, extname } from "path";
15364
16254
  function normalizeManifest(raw, filePath) {
15365
16255
  const name = typeof raw.name === "string" ? raw.name : "";
15366
16256
  const description = typeof raw.description === "string" ? raw.description : "";
@@ -15374,7 +16264,7 @@ function normalizeManifest(raw, filePath) {
15374
16264
  return { name, description, match, scope, priority, enabled };
15375
16265
  }
15376
16266
  async function loadSkillFile(filePath) {
15377
- const raw = await readFile15(filePath, "utf-8");
16267
+ const raw = await readFile17(filePath, "utf-8");
15378
16268
  const parsed = parseFrontmatter(raw);
15379
16269
  const manifest = normalizeManifest(parsed.data, filePath);
15380
16270
  const body = parsed.content.trim();
@@ -15396,7 +16286,7 @@ async function loadSkillsFromDir(dirPath) {
15396
16286
  const entries = await readdir6(dirPath);
15397
16287
  const files = [];
15398
16288
  for (const entry of entries) {
15399
- const full = join24(dirPath, entry);
16289
+ const full = join26(dirPath, entry);
15400
16290
  const s = await stat6(full);
15401
16291
  if (s.isFile() && extname(entry) === ".md") {
15402
16292
  files.push(full);
@@ -15424,12 +16314,12 @@ var init_loader = __esm({
15424
16314
  });
15425
16315
 
15426
16316
  // src/skills/manager.ts
15427
- import { mkdir as mkdir10, writeFile as writeFile11, unlink as unlink2, readFile as readFile16 } from "fs/promises";
15428
- import { join as join25 } from "path";
16317
+ import { mkdir as mkdir10, writeFile as writeFile12, unlink as unlink2, readFile as readFile18 } from "fs/promises";
16318
+ import { join as join27 } from "path";
15429
16319
  function getSkillDirs(cwd) {
15430
16320
  return {
15431
- projectDir: join25(cwd, ".kimiflare", "skills"),
15432
- globalDir: join25(process.env.HOME ?? "", ".config", "kimiflare", "skills")
16321
+ projectDir: join27(cwd, ".kimiflare", "skills"),
16322
+ globalDir: join27(process.env.HOME ?? "", ".config", "kimiflare", "skills")
15433
16323
  };
15434
16324
  }
15435
16325
  async function listAllSkills(cwd) {
@@ -15443,7 +16333,7 @@ async function listAllSkills(cwd) {
15443
16333
  async function createSkill(opts2) {
15444
16334
  const dirs = getSkillDirs(opts2.cwd);
15445
16335
  const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
15446
- const filepath = join25(dir, `${opts2.name}.md`);
16336
+ const filepath = join27(dir, `${opts2.name}.md`);
15447
16337
  const frontmatter = {
15448
16338
  name: opts2.name,
15449
16339
  enabled: true,
@@ -15465,7 +16355,7 @@ ${yaml}
15465
16355
  Add your instructions here.
15466
16356
  `;
15467
16357
  await mkdir10(dir, { recursive: true });
15468
- await writeFile11(filepath, content, "utf8");
16358
+ await writeFile12(filepath, content, "utf8");
15469
16359
  return { filepath };
15470
16360
  }
15471
16361
  async function deleteSkill(name, cwd) {
@@ -15479,7 +16369,7 @@ async function setSkillEnabled(name, enabled, cwd) {
15479
16369
  const all = await listAllSkills(cwd);
15480
16370
  const skill = all.project.find((s) => s.name === name) ?? all.global.find((s) => s.name === name);
15481
16371
  if (!skill) throw new Error(`skill "${name}" not found`);
15482
- const raw = await readFile16(skill.filePath, "utf-8");
16372
+ const raw = await readFile18(skill.filePath, "utf-8");
15483
16373
  const parsed = parseFrontmatter(raw);
15484
16374
  parsed.data.enabled = enabled;
15485
16375
  const yaml = Object.entries(parsed.data).map(([k, v]) => {
@@ -15491,7 +16381,7 @@ ${v.map((item) => ` - ${item}`).join("\n")}`;
15491
16381
  ${yaml}
15492
16382
  ---
15493
16383
  ${parsed.content}`;
15494
- await writeFile11(skill.filePath, content, "utf8");
16384
+ await writeFile12(skill.filePath, content, "utf8");
15495
16385
  return { filepath: skill.filePath };
15496
16386
  }
15497
16387
  async function findSkillFile(name, cwd) {
@@ -15564,13 +16454,13 @@ var init_frontmatter2 = __esm({
15564
16454
  // src/commands/loader.ts
15565
16455
  import { open, realpath as realpath2 } from "fs/promises";
15566
16456
  import { homedir as homedir14 } from "os";
15567
- import { join as join26, relative as relative5, sep as sep2 } from "path";
16457
+ import { join as join28, relative as relative5, sep as sep2 } from "path";
15568
16458
  function projectCommandsDir(cwd = process.cwd()) {
15569
- return join26(cwd, ".kimiflare", "commands");
16459
+ return join28(cwd, ".kimiflare", "commands");
15570
16460
  }
15571
16461
  function globalCommandsDir() {
15572
- const xdg = process.env.XDG_CONFIG_HOME || join26(homedir14(), ".config");
15573
- return join26(xdg, "kimiflare", "commands");
16462
+ const xdg = process.env.XDG_CONFIG_HOME || join28(homedir14(), ".config");
16463
+ return join28(xdg, "kimiflare", "commands");
15574
16464
  }
15575
16465
  async function loadCustomCommands(cwd = process.cwd()) {
15576
16466
  const warnings = [];
@@ -15721,7 +16611,7 @@ var init_loader2 = __esm({
15721
16611
  });
15722
16612
 
15723
16613
  // src/commands/save.ts
15724
- import { mkdir as mkdir11, writeFile as writeFile12, unlink as unlink3 } from "fs/promises";
16614
+ import { mkdir as mkdir11, writeFile as writeFile13, unlink as unlink3 } from "fs/promises";
15725
16615
  import { dirname as dirname11 } from "path";
15726
16616
  async function saveCustomCommand(opts2) {
15727
16617
  const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
@@ -15734,7 +16624,7 @@ async function saveCustomCommand(opts2) {
15734
16624
  const frontmatter = serializeFrontmatter(data);
15735
16625
  const content = frontmatter + opts2.template;
15736
16626
  await mkdir11(dirname11(filepath), { recursive: true });
15737
- await writeFile12(filepath, content, "utf8");
16627
+ await writeFile13(filepath, content, "utf8");
15738
16628
  return { filepath };
15739
16629
  }
15740
16630
  async function deleteCustomCommand(cmd) {
@@ -15844,13 +16734,13 @@ var init_worker_client = __esm({
15844
16734
  });
15845
16735
 
15846
16736
  // src/init/context-generator.ts
15847
- import { existsSync as existsSync4, statSync as statSync4 } from "fs";
15848
- import { join as join27 } from "path";
16737
+ import { existsSync as existsSync5, statSync as statSync5 } from "fs";
16738
+ import { join as join29 } from "path";
15849
16739
  function detectFlavor(cwd) {
15850
16740
  for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
15851
16741
  if (flavor === "generic") continue;
15852
16742
  for (const sig of signatures) {
15853
- const path = join27(cwd, sig);
16743
+ const path = join29(cwd, sig);
15854
16744
  if (sig.includes("*")) {
15855
16745
  try {
15856
16746
  const parts = sig.split("*");
@@ -15862,7 +16752,7 @@ function detectFlavor(cwd) {
15862
16752
  }
15863
16753
  } catch {
15864
16754
  }
15865
- } else if (existsSync4(path)) {
16755
+ } else if (existsSync5(path)) {
15866
16756
  return flavor;
15867
16757
  }
15868
16758
  }
@@ -15871,16 +16761,16 @@ function detectFlavor(cwd) {
15871
16761
  }
15872
16762
  function findFile(cwd, candidates) {
15873
16763
  for (const c of candidates) {
15874
- if (existsSync4(join27(cwd, c))) return c;
16764
+ if (existsSync5(join29(cwd, c))) return c;
15875
16765
  }
15876
16766
  return null;
15877
16767
  }
15878
16768
  function findSourceRoots(cwd) {
15879
16769
  const roots = [];
15880
16770
  for (const r of SOURCE_ROOT_CANDIDATES) {
15881
- const p = join27(cwd, r);
16771
+ const p = join29(cwd, r);
15882
16772
  try {
15883
- const s = statSync4(p);
16773
+ const s = statSync5(p);
15884
16774
  if (s.isDirectory()) roots.push(r);
15885
16775
  } catch {
15886
16776
  }
@@ -15889,9 +16779,9 @@ function findSourceRoots(cwd) {
15889
16779
  }
15890
16780
  function findCiConfig(cwd) {
15891
16781
  for (const c of CI_PATHS) {
15892
- if (existsSync4(join27(cwd, c))) {
16782
+ if (existsSync5(join29(cwd, c))) {
15893
16783
  try {
15894
- const s = statSync4(join27(cwd, c));
16784
+ const s = statSync5(join29(cwd, c));
15895
16785
  return s.isDirectory() ? c : c;
15896
16786
  } catch {
15897
16787
  }
@@ -16028,7 +16918,7 @@ function analyzeProject(cwd) {
16028
16918
  ciConfig: findCiConfig(cwd),
16029
16919
  readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
16030
16920
  sourceRoots: findSourceRoots(cwd),
16031
- hasGit: existsSync4(join27(cwd, ".git"))
16921
+ hasGit: existsSync5(join29(cwd, ".git"))
16032
16922
  };
16033
16923
  }
16034
16924
  function bashDiscoveryCommands(profile) {
@@ -16193,7 +17083,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
16193
17083
  }
16194
17084
  function buildInitPrompt(cwd) {
16195
17085
  const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
16196
- (n) => existsSync4(join27(cwd, n))
17086
+ (n) => existsSync5(join29(cwd, n))
16197
17087
  );
16198
17088
  const isRefresh = existingName !== void 0;
16199
17089
  const targetFilename = existingName ?? "KIMI.md";
@@ -16281,11 +17171,11 @@ __export(ui_mode_exports, {
16281
17171
  runCamouflageOnboarding: () => runCamouflageOnboarding,
16282
17172
  runUiMode: () => runUiMode
16283
17173
  });
16284
- import { execSync as execSync4, spawn as spawn5 } from "child_process";
17174
+ import { execSync as execSync6, spawn as spawn6 } from "child_process";
16285
17175
  import { appendFileSync, openSync } from "fs";
16286
17176
  import { readdir as readdir7, unlink as unlink4 } from "fs/promises";
16287
- import { join as join28, relative as relative6 } from "path";
16288
- import { platform as platform4 } from "os";
17177
+ import { join as join30, relative as relative6 } from "path";
17178
+ import { platform as platform5 } from "os";
16289
17179
  function kimiLog(payload) {
16290
17180
  if (!KIMI_LOG_PATH) return;
16291
17181
  try {
@@ -17067,6 +17957,14 @@ Executor opened PR: ${prUrl}` : plan });
17067
17957
  cam.send("StatusUpdate", {
17068
17958
  segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
17069
17959
  });
17960
+ rebuildSystemPromptForMode(
17961
+ messages,
17962
+ false,
17963
+ // Camouflage UI always uses single system message
17964
+ opts2.model,
17965
+ currentMode,
17966
+ ALL_TOOLS
17967
+ );
17070
17968
  messages.push({ role: "user", content: selected.plan });
17071
17969
  cam.send("UserMessageCreated", { text: selected.plan });
17072
17970
  cam.send("ShowToast", {
@@ -17129,7 +18027,7 @@ Executor opened PR: ${prUrl}` : plan });
17129
18027
  allow_cancel: true
17130
18028
  });
17131
18029
  if (pick3.cancelled || !pick3.value) return;
17132
- openBrowser(
18030
+ openBrowser2(
17133
18031
  `${URL2}/inbox?u=${encodeURIComponent(handle)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(pick3.value)}`
17134
18032
  );
17135
18033
  cam.send("ShowToast", { text: "opened in browser", kind: "success", ttl_ms: 1500 });
@@ -18246,7 +19144,7 @@ Executor opened PR: ${prUrl}` : plan });
18246
19144
  return true;
18247
19145
  }
18248
19146
  case "hello":
18249
- openBrowser("https://hello.kimiflare.com");
19147
+ openBrowser2("https://hello.kimiflare.com");
18250
19148
  cam.send("ShowToast", { text: "opened hello.kimiflare.com \u2014 leave the creator a voice note", kind: "info", ttl_ms: 3500 });
18251
19149
  return true;
18252
19150
  case "inbox":
@@ -18264,33 +19162,50 @@ Executor opened PR: ${prUrl}` : plan });
18264
19162
  cam.send("ShowToast", { text: "can't /fresh while model is running \u2014 press Esc to interrupt first", kind: "warn", ttl_ms: 2500 });
18265
19163
  return true;
18266
19164
  }
18267
- const plan = sessionPlan ?? distillSessionPlan(messages);
18268
- if (!plan) {
18269
- cam.send("ShowToast", { text: "No plan found to start fresh with.", kind: "error", ttl_ms: 2500 });
18270
- return true;
18271
- }
18272
- const clipResult = writeToClipboard(plan);
18273
- const systemMessages = messages.filter((m) => m.role === "system");
18274
- messages.length = 0;
18275
- messages.push(...systemMessages);
18276
- sessionCostUsd = 0;
18277
- promptTokens = 0;
18278
- cachedTokens = 0;
18279
- completionTokens = 0;
18280
- sessionPlan = null;
18281
- cam.send("TranscriptCleared", {});
18282
- cam.send("StatusUpdate", {
18283
- segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
18284
- });
18285
- messages.push({ role: "user", content: plan });
18286
- cam.send("ShowToast", {
18287
- text: clipResult.success ? "Plan copied to clipboard. Starting fresh session with plan only\u2026" : "Clipboard unavailable. Starting fresh session with plan only\u2026",
18288
- kind: "info",
18289
- ttl_ms: 3e3
18290
- });
18291
- if (!clipResult.success) {
18292
- cam.send("UserMessageCreated", { text: "--- Plan ---\n" + plan });
18293
- }
19165
+ void (async () => {
19166
+ const summary = await generateContinuationSummary({
19167
+ messages,
19168
+ mode: currentMode,
19169
+ accountId: opts2.accountId,
19170
+ apiToken: opts2.apiToken,
19171
+ model: opts2.plumbingModel ?? "@cf/moonshotai/kimi-k2.5",
19172
+ gateway: gatewayFromOpts2(opts2)
19173
+ });
19174
+ if (!summary) {
19175
+ cam.send("ShowToast", { text: "No plan or summary found to start fresh with.", kind: "error", ttl_ms: 2500 });
19176
+ return;
19177
+ }
19178
+ const clipResult = writeToClipboard(summary);
19179
+ const systemMessages = messages.filter((m) => m.role === "system");
19180
+ messages.length = 0;
19181
+ messages.push(...systemMessages);
19182
+ sessionCostUsd = 0;
19183
+ promptTokens = 0;
19184
+ cachedTokens = 0;
19185
+ completionTokens = 0;
19186
+ sessionPlan = null;
19187
+ cam.send("TranscriptCleared", {});
19188
+ cam.send("StatusUpdate", {
19189
+ segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
19190
+ });
19191
+ rebuildSystemPromptForMode(
19192
+ messages,
19193
+ false,
19194
+ // Camouflage UI always uses single system message
19195
+ opts2.model,
19196
+ currentMode,
19197
+ ALL_TOOLS
19198
+ );
19199
+ messages.push({ role: "user", content: summary });
19200
+ cam.send("ShowToast", {
19201
+ text: clipResult.success ? "Summary copied to clipboard. Starting fresh session with continuation context\u2026" : "Clipboard unavailable. Starting fresh session with continuation context\u2026",
19202
+ kind: "info",
19203
+ ttl_ms: 3e3
19204
+ });
19205
+ if (!clipResult.success) {
19206
+ cam.send("UserMessageCreated", { text: "--- Continuation Context ---\n" + summary });
19207
+ }
19208
+ })();
18294
19209
  return true;
18295
19210
  }
18296
19211
  case "logout": {
@@ -18355,7 +19270,7 @@ async function registerMentions(cam, recents) {
18355
19270
  for (const e of entries) {
18356
19271
  if (collected.length >= 200) return;
18357
19272
  if (e.name.startsWith(".") || SKIP.has(e.name)) continue;
18358
- const full = join28(dir, e.name);
19273
+ const full = join30(dir, e.name);
18359
19274
  if (e.isDirectory()) {
18360
19275
  await walk2(full, depth + 1);
18361
19276
  } else if (e.isFile()) {
@@ -18404,9 +19319,9 @@ function formatShortDate(iso) {
18404
19319
  return iso;
18405
19320
  }
18406
19321
  }
18407
- function openBrowser(url) {
18408
- const cmd = platform4() === "darwin" ? "open" : platform4() === "win32" ? "start" : "xdg-open";
18409
- const child = spawn5(cmd, [url], { detached: true, stdio: "ignore" });
19322
+ function openBrowser2(url) {
19323
+ const cmd = platform5() === "darwin" ? "open" : platform5() === "win32" ? "start" : "xdg-open";
19324
+ const child = spawn6(cmd, [url], { detached: true, stdio: "ignore" });
18410
19325
  child.unref();
18411
19326
  }
18412
19327
  function formatUsd(n) {
@@ -18425,7 +19340,7 @@ function formatElapsed2(secs) {
18425
19340
  }
18426
19341
  function tryGitBranch2() {
18427
19342
  try {
18428
- const out = execSync4("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
19343
+ const out = execSync6("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
18429
19344
  encoding: "utf8",
18430
19345
  timeout: 200
18431
19346
  }).trim();
@@ -18530,12 +19445,14 @@ var init_ui_mode = __esm({
18530
19445
  init_classify();
18531
19446
  init_deploy_commute();
18532
19447
  init_system_prompt();
19448
+ init_app_helpers();
18533
19449
  init_executor();
18534
19450
  init_errors();
18535
19451
  init_builtins();
18536
19452
  init_sessions();
18537
19453
  init_llm_summarize();
18538
19454
  init_distill();
19455
+ init_continuation_summary();
18539
19456
  init_greetings();
18540
19457
  init_theme();
18541
19458
  init_update_check();
@@ -21017,7 +21934,7 @@ var init_text_input = __esm({
21017
21934
  // src/ui/permission.tsx
21018
21935
  import { useState as useState4, useCallback } from "react";
21019
21936
  import { Box as Box7, Text as Text8, useInput as useInput2 } from "ink";
21020
- import { platform as platform5 } from "os";
21937
+ import { platform as platform6 } from "os";
21021
21938
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
21022
21939
  function formatSelection(label, shortcut) {
21023
21940
  return `${label} [${MOD_KEY}+${shortcut}]`;
@@ -21181,7 +22098,7 @@ var init_permission = __esm({
21181
22098
  { value: { decision: "deny", scope: "once" }, label: "Something else", key: 3 }
21182
22099
  ];
21183
22100
  DENY = { decision: "deny", scope: "once" };
21184
- MOD_KEY = platform5() === "darwin" ? "\u2325" : "Alt";
22101
+ MOD_KEY = platform6() === "darwin" ? "\u2325" : "Alt";
21185
22102
  }
21186
22103
  });
21187
22104
 
@@ -21619,7 +22536,7 @@ function TaskList({ tasks, startedAt, tokensDelta }) {
21619
22536
  const allDone = done === total;
21620
22537
  const header = active ? active.title : allDone ? `${total} tasks done` : `${done}/${total}`;
21621
22538
  const elapsed = startedAt ? formatElapsed5(now2 - startedAt) : null;
21622
- const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
22539
+ const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens2(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
21623
22540
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
21624
22541
  const hiddenPending = Math.max(0, tasks.length - visibleTasks.length);
21625
22542
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
@@ -21671,7 +22588,7 @@ function formatElapsed5(ms) {
21671
22588
  if (m === 0) return `${s}s`;
21672
22589
  return `${m}m ${s}s`;
21673
22590
  }
21674
- function formatTokens(n) {
22591
+ function formatTokens2(n) {
21675
22592
  if (n < 1e3) return String(n);
21676
22593
  return `${(n / 1e3).toFixed(1)}k`;
21677
22594
  }
@@ -23212,52 +24129,13 @@ function Welcome() {
23212
24129
  return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginBottom: 1, children: [
23213
24130
  /* @__PURE__ */ jsx20(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx20(Text19, { bold: true, color: theme.accent, children: headline }) }),
23214
24131
  /* @__PURE__ */ jsx20(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx20(Text19, { color: theme.info.color, dimColor: true, children: "Type / for commands" }) })
23215
- ] });
23216
- }
23217
- var init_welcome = __esm({
23218
- "src/ui/welcome.tsx"() {
23219
- "use strict";
23220
- init_theme_context();
23221
- init_greetings();
23222
- }
23223
- });
23224
-
23225
- // src/util/image.ts
23226
- import { readFile as readFile17 } from "fs/promises";
23227
- import { basename as basename4 } from "path";
23228
- async function encodeImageFile(filePath) {
23229
- const buf = await readFile17(filePath);
23230
- if (buf.byteLength > MAX_IMAGE_BYTES) {
23231
- throw new Error(
23232
- `image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
23233
- );
23234
- }
23235
- const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
23236
- const mime = EXT_TO_MIME[ext] ?? "image/jpeg";
23237
- const b64 = buf.toString("base64");
23238
- return {
23239
- filename: basename4(filePath),
23240
- mime,
23241
- dataUrl: `data:${mime};base64,${b64}`
23242
- };
23243
- }
23244
- function isImagePath(path) {
23245
- const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
23246
- return ext in EXT_TO_MIME;
23247
- }
23248
- var MAX_IMAGE_BYTES, EXT_TO_MIME;
23249
- var init_image = __esm({
23250
- "src/util/image.ts"() {
23251
- "use strict";
23252
- MAX_IMAGE_BYTES = 5 * 1024 * 1024;
23253
- EXT_TO_MIME = {
23254
- ".png": "image/png",
23255
- ".jpg": "image/jpeg",
23256
- ".jpeg": "image/jpeg",
23257
- ".gif": "image/gif",
23258
- ".webp": "image/webp",
23259
- ".bmp": "image/bmp"
23260
- };
24132
+ ] });
24133
+ }
24134
+ var init_welcome = __esm({
24135
+ "src/ui/welcome.tsx"() {
24136
+ "use strict";
24137
+ init_theme_context();
24138
+ init_greetings();
23261
24139
  }
23262
24140
  });
23263
24141
 
@@ -23493,11 +24371,11 @@ var init_wcag = __esm({
23493
24371
  });
23494
24372
 
23495
24373
  // src/ui/theme-loader.ts
23496
- import { readFile as readFile18, readdir as readdir8 } from "fs/promises";
23497
- import { join as join29 } from "path";
24374
+ import { readFile as readFile19, readdir as readdir8 } from "fs/promises";
24375
+ import { join as join31 } from "path";
23498
24376
  import { homedir as homedir15 } from "os";
23499
24377
  function projectThemesDir(cwd = process.cwd()) {
23500
- return join29(cwd, ".kimiflare", "themes");
24378
+ return join31(cwd, ".kimiflare", "themes");
23501
24379
  }
23502
24380
  function isHexColor(c) {
23503
24381
  return /^#[0-9a-fA-F]{6}$/.test(c);
@@ -23586,10 +24464,10 @@ async function loadThemesFromDir(dir, source) {
23586
24464
  return { themes, errors };
23587
24465
  }
23588
24466
  for (const file of files.filter((f) => f.endsWith(".json"))) {
23589
- const path = join29(dir, file);
24467
+ const path = join31(dir, file);
23590
24468
  let raw;
23591
24469
  try {
23592
- raw = await readFile18(path, "utf-8");
24470
+ raw = await readFile19(path, "utf-8");
23593
24471
  } catch (e) {
23594
24472
  errors.push(`${path}: ${e instanceof Error ? e.message : String(e)}`);
23595
24473
  continue;
@@ -23735,8 +24613,8 @@ var init_theme_loader = __esm({
23735
24613
  "use strict";
23736
24614
  init_wcag();
23737
24615
  init_theme();
23738
- USER_THEMES_DIR = join29(
23739
- process.env.XDG_CONFIG_HOME || join29(homedir15(), ".config"),
24616
+ USER_THEMES_DIR = join31(
24617
+ process.env.XDG_CONFIG_HOME || join31(homedir15(), ".config"),
23740
24618
  "kimiflare",
23741
24619
  "themes"
23742
24620
  );
@@ -24173,8 +25051,9 @@ function useModalHost() {
24173
25051
  const [showSkillsPicker, setShowSkillsPicker] = useState16(false);
24174
25052
  const [showShellPicker, setShowShellPicker] = useState16(false);
24175
25053
  const [showPlanCompletePicker, setShowPlanCompletePicker] = useState16(false);
25054
+ const [showChangelogImagePicker, setShowChangelogImagePicker] = useState16(false);
24176
25055
  const flags = useMemo4(() => {
24177
- const hasFullscreenModal = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || showThemePicker || showUiPicker || showModelPicker || showModePicker || keyEntryFor !== null || billingChooserFor !== null || unifiedProbeFor !== null || showRemoteDashboard || showInboxModal || showMultiAgentModal || showHooksDashboard || showHelpMenu || showMemoryPicker || showGatewayPicker || showSkillsPicker || showShellPicker;
25056
+ const hasFullscreenModal = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || showThemePicker || showUiPicker || showModelPicker || showModePicker || keyEntryFor !== null || billingChooserFor !== null || unifiedProbeFor !== null || showRemoteDashboard || showInboxModal || showMultiAgentModal || showHooksDashboard || showHelpMenu || showMemoryPicker || showGatewayPicker || showSkillsPicker || showShellPicker || showChangelogImagePicker;
24178
25057
  const hasOverlayModal = limitModal !== null || loopModal !== null || showPlanCompletePicker;
24179
25058
  return {
24180
25059
  hasFullscreenModal,
@@ -24204,6 +25083,7 @@ function useModalHost() {
24204
25083
  showSkillsPicker,
24205
25084
  showShellPicker,
24206
25085
  showPlanCompletePicker,
25086
+ showChangelogImagePicker,
24207
25087
  limitModal,
24208
25088
  loopModal
24209
25089
  ]);
@@ -24256,6 +25136,8 @@ function useModalHost() {
24256
25136
  setShowShellPicker,
24257
25137
  showPlanCompletePicker,
24258
25138
  setShowPlanCompletePicker,
25139
+ showChangelogImagePicker,
25140
+ setShowChangelogImagePicker,
24259
25141
  ...flags
24260
25142
  };
24261
25143
  }
@@ -24823,7 +25705,7 @@ var init_command_list = __esm({
24823
25705
  import { useState as useState18 } from "react";
24824
25706
  import { Box as Box25, Text as Text26 } from "ink";
24825
25707
  import SelectInput10 from "ink-select-input";
24826
- import { spawn as spawn6 } from "child_process";
25708
+ import { spawn as spawn7 } from "child_process";
24827
25709
  import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
24828
25710
  function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
24829
25711
  const theme = useTheme();
@@ -24837,7 +25719,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
24837
25719
  const runInstall = (command) => {
24838
25720
  setInstallState({ status: "running", output: "Installing..." });
24839
25721
  const { shell, args } = getShellCommand();
24840
- const child = spawn6(shell, [...args, command], {
25722
+ const child = spawn7(shell, [...args, command], {
24841
25723
  env: process.env
24842
25724
  });
24843
25725
  let stdout = "";
@@ -25863,7 +26745,7 @@ function formatSessionLine(s) {
25863
26745
  const ago = formatAgo(new Date(s.updatedAt));
25864
26746
  const prompt = s.prompt.slice(0, 30) + (s.prompt.length > 30 ? "\u2026" : "");
25865
26747
  const outcome = s.prUrl ? `PR ${s.prUrl.split("/").pop()}` : s.status;
25866
- const cost = s.tokensUsed && s.tokensBudget ? ` (${formatTokens2(s.tokensUsed)}/${formatTokens2(s.tokensBudget)})` : s.tokensUsed ? ` (${formatTokens2(s.tokensUsed)})` : "";
26748
+ const cost = s.tokensUsed && s.tokensBudget ? ` (${formatTokens3(s.tokensUsed)}/${formatTokens3(s.tokensBudget)})` : s.tokensUsed ? ` (${formatTokens3(s.tokensUsed)})` : "";
25867
26749
  return `${icon} ${prompt} \u2192 ${outcome} ${ago}${cost}`;
25868
26750
  }
25869
26751
  function formatAgo(date) {
@@ -25876,7 +26758,7 @@ function formatAgo(date) {
25876
26758
  if (minutes > 0) return `${minutes}m ago`;
25877
26759
  return "just now";
25878
26760
  }
25879
- function formatTokens2(n) {
26761
+ function formatTokens3(n) {
25880
26762
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
25881
26763
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
25882
26764
  return String(n);
@@ -25937,8 +26819,8 @@ function RemoteSessionDetail({
25937
26819
  ] }),
25938
26820
  session.tokensUsed !== void 0 && /* @__PURE__ */ jsxs33(Text34, { children: [
25939
26821
  "Tokens: ",
25940
- formatTokens2(session.tokensUsed),
25941
- session.tokensBudget ? ` / ${formatTokens2(session.tokensBudget)}` : ""
26822
+ formatTokens3(session.tokensUsed),
26823
+ session.tokensBudget ? ` / ${formatTokens3(session.tokensBudget)}` : ""
25942
26824
  ] }),
25943
26825
  /* @__PURE__ */ jsxs33(Text34, { children: [
25944
26826
  "Created: ",
@@ -25982,7 +26864,7 @@ function InboxModal({ onDone, onOpen }) {
25982
26864
  setError(null);
25983
26865
  try {
25984
26866
  const res = await fetch(
25985
- `${FEEDBACK_WORKER_URL}/inbox/check?u=${encodeURIComponent(u)}&s=${encodeURIComponent(s)}`
26867
+ `${FEEDBACK_WORKER_URL2}/inbox/check?u=${encodeURIComponent(u)}&s=${encodeURIComponent(s)}`
25986
26868
  );
25987
26869
  if (!res.ok) {
25988
26870
  throw new Error(`Server returned ${res.status}`);
@@ -26028,7 +26910,7 @@ function InboxModal({ onDone, onOpen }) {
26028
26910
  if (messages.length === 0) return;
26029
26911
  const msg = messages[selectedIndex];
26030
26912
  if (!msg) return;
26031
- const url = `${FEEDBACK_WORKER_URL}/inbox?u=${encodeURIComponent(twitter)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(msg.id)}`;
26913
+ const url = `${FEEDBACK_WORKER_URL2}/inbox?u=${encodeURIComponent(twitter)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(msg.id)}`;
26032
26914
  onOpen(url);
26033
26915
  onDone();
26034
26916
  }, [messages, selectedIndex, twitter, secret, onOpen, onDone]);
@@ -26102,280 +26984,38 @@ function InboxModal({ onDone, onOpen }) {
26102
26984
  return /* @__PURE__ */ jsxs34(
26103
26985
  Text35,
26104
26986
  {
26105
- color: isSelected ? theme.accent : theme.palette.foreground,
26106
- bold: isSelected,
26107
- dimColor: !isSelected && msg.seen,
26108
- children: [
26109
- isSelected ? "> " : " ",
26110
- marker,
26111
- dateStr,
26112
- msg.seen ? " (played)" : " (new)"
26113
- ]
26114
- },
26115
- msg.id
26116
- );
26117
- }) }),
26118
- /* @__PURE__ */ jsx36(Box34, { marginTop: 1, children: /* @__PURE__ */ jsx36(Text35, { color: theme.info.color, children: "\u2191\u2193 to select \xB7 Enter to open in browser" }) })
26119
- ] }) : /* @__PURE__ */ jsxs34(Text35, { color: theme.muted?.color ?? theme.palette.secondary, children: [
26120
- "No messages yet for @",
26121
- twitter,
26122
- " / ",
26123
- secret.replace(/./g, "*"),
26124
- "."
26125
- ] }),
26126
- /* @__PURE__ */ jsx36(Text35, { dimColor: true, children: "Press Esc to close." })
26127
- ] })
26128
- ] });
26129
- }
26130
- var FEEDBACK_WORKER_URL;
26131
- var init_inbox_modal = __esm({
26132
- "src/ui/inbox-modal.tsx"() {
26133
- "use strict";
26134
- init_text_input();
26135
- init_theme_context();
26136
- FEEDBACK_WORKER_URL = "https://hello.kimiflare.com";
26137
- }
26138
- });
26139
-
26140
- // src/ui/app-helpers.ts
26141
- import { execSync as execSync5, spawn as spawn7 } from "child_process";
26142
- import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
26143
- import { join as join30 } from "path";
26144
- import { platform as platform6 } from "os";
26145
- function buildFilePickerIgnoreList(cwd) {
26146
- const hardcoded = [
26147
- // Dependencies
26148
- "**/node_modules/**",
26149
- "**/vendor/**",
26150
- "**/.bundle/**",
26151
- "**/bower_components/**",
26152
- // Version control
26153
- "**/.git/**",
26154
- "**/.svn/**",
26155
- "**/.hg/**",
26156
- // Build / output directories
26157
- "**/dist/**",
26158
- "**/build/**",
26159
- "**/out/**",
26160
- "**/public/**",
26161
- "**/.next/**",
26162
- "**/.nuxt/**",
26163
- "**/.svelte-kit/**",
26164
- "**/.vercel/**",
26165
- "**/.netlify/**",
26166
- "**/target/**",
26167
- "**/bin/**",
26168
- "**/obj/**",
26169
- "**/Debug/**",
26170
- "**/Release/**",
26171
- "**/.gradle/**",
26172
- // Caches
26173
- "**/.cache/**",
26174
- "**/.parcel-cache/**",
26175
- "**/.turbo/**",
26176
- "**/.eslintcache",
26177
- "**/.stylelintcache",
26178
- "**/.rpt2_cache/**",
26179
- "**/.rts2_cache/**",
26180
- // Temporary
26181
- "**/tmp/**",
26182
- "**/temp/**",
26183
- "**/*.tmp",
26184
- // Coverage
26185
- "**/coverage/**",
26186
- "**/.nyc_output/**",
26187
- // OS files
26188
- "**/.DS_Store",
26189
- "**/Thumbs.db",
26190
- // Logs
26191
- "**/*.log",
26192
- "**/logs/**",
26193
- // Lock files (auto-generated, usually huge)
26194
- "**/package-lock.json",
26195
- "**/yarn.lock",
26196
- "**/pnpm-lock.yaml",
26197
- "**/bun.lockb",
26198
- "**/Cargo.lock",
26199
- "**/Gemfile.lock",
26200
- "**/composer.lock",
26201
- "**/Pipfile.lock",
26202
- "**/poetry.lock",
26203
- "**/go.sum",
26204
- // Minified / source maps
26205
- "**/*.min.js",
26206
- "**/*.min.css",
26207
- "**/*.map",
26208
- // kimiflare internal
26209
- "**/.kimiflare/**",
26210
- // IDE (usually not relevant to mention)
26211
- "**/.idea/**"
26212
- ];
26213
- const gitignorePatterns = [];
26214
- try {
26215
- const gitignorePath = join30(cwd, ".gitignore");
26216
- const stats = statSync5(gitignorePath);
26217
- if (stats.size > MAX_GITIGNORE_SIZE) {
26218
- return hardcoded;
26219
- }
26220
- const content = readFileSync4(gitignorePath, "utf-8");
26221
- for (const line of content.split(/\r?\n/)) {
26222
- const trimmed = line.trim();
26223
- if (!trimmed || trimmed.startsWith("#")) continue;
26224
- if (trimmed.startsWith("!")) continue;
26225
- let pattern = trimmed;
26226
- const isAnchored = pattern.startsWith("/");
26227
- const isDir = pattern.endsWith("/");
26228
- if (isAnchored) pattern = pattern.slice(1);
26229
- if (isDir) pattern = pattern.slice(0, -1);
26230
- if (!pattern) continue;
26231
- if (isAnchored) {
26232
- gitignorePatterns.push(isDir ? pattern + "/**" : pattern);
26233
- } else {
26234
- gitignorePatterns.push(isDir ? "**/" + pattern + "/**" : "**/" + pattern);
26235
- }
26236
- }
26237
- } catch {
26238
- }
26239
- return [...hardcoded, ...gitignorePatterns];
26240
- }
26241
- function gatewayFromConfig(cfg) {
26242
- if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
26243
- if (!cfg.aiGatewayId) return void 0;
26244
- return {
26245
- id: cfg.aiGatewayId,
26246
- cacheTtl: cfg.aiGatewayCacheTtl,
26247
- skipCache: cfg.aiGatewaySkipCache,
26248
- collectLogPayload: cfg.aiGatewayCollectLogPayload,
26249
- metadata: cfg.aiGatewayMetadata
26250
- };
26251
- }
26252
- function gatewayUsageLookupFromConfig(cfg, meta) {
26253
- if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
26254
- if (!cfg.aiGatewayId || !meta) return void 0;
26255
- return {
26256
- accountId: cfg.accountId,
26257
- apiToken: cfg.apiToken,
26258
- gatewayId: cfg.aiGatewayId,
26259
- meta
26260
- };
26261
- }
26262
- function openBrowser2(url) {
26263
- const cmd = platform6() === "darwin" ? "open" : platform6() === "win32" ? "start" : "xdg-open";
26264
- const child = spawn7(cmd, [url], { detached: true, stdio: "ignore" });
26265
- child.unref();
26266
- }
26267
- function detectGitHubRepo(cachedRepo) {
26268
- if (cachedRepo) {
26269
- const parts = cachedRepo.split("/");
26270
- if (parts.length === 2) return { owner: parts[0], name: parts[1] };
26271
- }
26272
- try {
26273
- const remoteUrl = execSync5("git remote get-url origin", { cwd: process.cwd(), encoding: "utf8" }).trim();
26274
- const httpsMatch = remoteUrl.match(/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
26275
- if (httpsMatch) return { owner: httpsMatch[1], name: httpsMatch[2] };
26276
- const sshMatch = remoteUrl.match(/github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
26277
- if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
26278
- } catch {
26279
- }
26280
- return null;
26281
- }
26282
- function detectGitBranch() {
26283
- try {
26284
- return execSync5("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim() || null;
26285
- } catch {
26286
- return null;
26287
- }
26288
- }
26289
- function formatTokens3(n) {
26290
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
26291
- if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
26292
- return String(n);
26293
- }
26294
- function trackRecentFile(ref, path, max = 10) {
26295
- ref.current.set(path, Date.now());
26296
- if (ref.current.size > max) {
26297
- let oldest = null;
26298
- let oldestTime = Infinity;
26299
- for (const [p, t] of ref.current) {
26300
- if (t < oldestTime) {
26301
- oldestTime = t;
26302
- oldest = p;
26303
- }
26304
- }
26305
- if (oldest) ref.current.delete(oldest);
26306
- }
26307
- }
26308
- function capEvents(prev) {
26309
- if (prev.length <= MAX_EVENTS) return prev;
26310
- return prev.slice(prev.length - MAX_EVENTS);
26311
- }
26312
- function compactEventsVisual(prev, keepLastTurns) {
26313
- let seen = 0;
26314
- let cutoff = -1;
26315
- for (let i = prev.length - 1; i >= 0; i--) {
26316
- if (prev[i].kind === "user") {
26317
- seen++;
26318
- if (seen === keepLastTurns + 1) {
26319
- cutoff = i;
26320
- break;
26321
- }
26322
- }
26323
- }
26324
- if (cutoff <= 0) return prev;
26325
- const kept = prev.slice(cutoff);
26326
- return [
26327
- { kind: "info", key: mkKey(), text: `\xB7\xB7\xB7 ${cutoff} earlier messages compacted \xB7\xB7\xB7` },
26328
- ...kept
26329
- ];
26330
- }
26331
- function makePrefixMessages(cacheStable, model, mode, tools) {
26332
- if (cacheStable) {
26333
- return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
26334
- }
26335
- return [
26336
- {
26337
- role: "system",
26338
- content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
26339
- }
26340
- ];
26341
- }
26342
- function findImagePaths(text) {
26343
- const paths = [];
26344
- const quotedRegex = /"([^"]+)"|'([^']+)'/g;
26345
- let match;
26346
- while ((match = quotedRegex.exec(text)) !== null) {
26347
- const path = match[1] ?? match[2];
26348
- if (path && isImagePath(path) && existsSync5(path)) {
26349
- paths.push(path);
26350
- }
26351
- }
26352
- const remaining = text.replace(/"[^"]+"|'[^']+'/g, "");
26353
- const ESCAPED_SPACE = "\0";
26354
- const processed = remaining.replace(/\\ /g, ESCAPED_SPACE);
26355
- for (const token of processed.split(/\s+/)) {
26356
- const clean = token.replace(new RegExp(ESCAPED_SPACE, "g"), " ").replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
26357
- if (clean && isImagePath(clean) && existsSync5(clean) && !paths.includes(clean)) {
26358
- paths.push(clean);
26359
- }
26360
- }
26361
- return paths;
26987
+ color: isSelected ? theme.accent : theme.palette.foreground,
26988
+ bold: isSelected,
26989
+ dimColor: !isSelected && msg.seen,
26990
+ children: [
26991
+ isSelected ? "> " : " ",
26992
+ marker,
26993
+ dateStr,
26994
+ msg.seen ? " (played)" : " (new)"
26995
+ ]
26996
+ },
26997
+ msg.id
26998
+ );
26999
+ }) }),
27000
+ /* @__PURE__ */ jsx36(Box34, { marginTop: 1, children: /* @__PURE__ */ jsx36(Text35, { color: theme.info.color, children: "\u2191\u2193 to select \xB7 Enter to open in browser" }) })
27001
+ ] }) : /* @__PURE__ */ jsxs34(Text35, { color: theme.muted?.color ?? theme.palette.secondary, children: [
27002
+ "No messages yet for @",
27003
+ twitter,
27004
+ " / ",
27005
+ secret.replace(/./g, "*"),
27006
+ "."
27007
+ ] }),
27008
+ /* @__PURE__ */ jsx36(Text35, { dimColor: true, children: "Press Esc to close." })
27009
+ ] })
27010
+ ] });
26362
27011
  }
26363
- var MAX_GITIGNORE_SIZE, CONTEXT_LIMIT, AUTO_COMPACT_THRESHOLD, MAX_EVENTS, MAX_IMAGES_PER_MESSAGE, FEEDBACK_WORKER_URL2, nextKey, mkKey, nextAssistantId, mkAssistantId;
26364
- var init_app_helpers = __esm({
26365
- "src/ui/app-helpers.ts"() {
27012
+ var FEEDBACK_WORKER_URL2;
27013
+ var init_inbox_modal = __esm({
27014
+ "src/ui/inbox-modal.tsx"() {
26366
27015
  "use strict";
26367
- init_system_prompt();
26368
- init_image();
26369
- MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
26370
- CONTEXT_LIMIT = 262e3;
26371
- AUTO_COMPACT_THRESHOLD = 0.8;
26372
- MAX_EVENTS = 500;
26373
- MAX_IMAGES_PER_MESSAGE = 10;
27016
+ init_text_input();
27017
+ init_theme_context();
26374
27018
  FEEDBACK_WORKER_URL2 = "https://hello.kimiflare.com";
26375
- nextKey = 1;
26376
- mkKey = () => `evt_${nextKey++}`;
26377
- nextAssistantId = 1;
26378
- mkAssistantId = () => nextAssistantId++;
26379
27019
  }
26380
27020
  });
26381
27021
 
@@ -26556,7 +27196,7 @@ function MultiAgentModal({ initial, onSave, onDone, remoteWorkerUrl, remoteAuthS
26556
27196
  if (deployFailed) {
26557
27197
  if (input === "o" || input === "O") {
26558
27198
  const url = deployLog.map((l) => l.match(/https:\/\/dash\.cloudflare\.com\/[^\s)]+/)?.[0]).find((u) => !!u) ?? "https://dash.cloudflare.com/profile/api-tokens";
26559
- openBrowser2(url);
27199
+ openBrowser(url);
26560
27200
  return;
26561
27201
  }
26562
27202
  if (input === "r" || input === "R") {
@@ -27661,10 +28301,71 @@ var init_help_menu = __esm({
27661
28301
  }
27662
28302
  });
27663
28303
 
28304
+ // src/ui/changelog-image-picker.tsx
28305
+ import { useState as useState26 } from "react";
28306
+ import { Box as Box39, Text as Text40, useInput as useInput20 } from "ink";
28307
+ import { jsx as jsx41, jsxs as jsxs39 } from "react/jsx-runtime";
28308
+ function ChangelogImagePicker({ owner, repo, onGenerate, onCancel }) {
28309
+ const theme = useTheme();
28310
+ const [selectedIndex, setSelectedIndex] = useState26(0);
28311
+ useInput20((_input, key) => {
28312
+ if (key.escape) {
28313
+ onCancel();
28314
+ return;
28315
+ }
28316
+ if (key.return) {
28317
+ const option = PERIODS[selectedIndex];
28318
+ if (option) {
28319
+ onGenerate(owner, repo, option.days);
28320
+ }
28321
+ return;
28322
+ }
28323
+ if (key.upArrow) {
28324
+ setSelectedIndex((i) => Math.max(0, i - 1));
28325
+ return;
28326
+ }
28327
+ if (key.downArrow) {
28328
+ setSelectedIndex((i) => Math.min(PERIODS.length - 1, i + 1));
28329
+ return;
28330
+ }
28331
+ });
28332
+ return /* @__PURE__ */ jsxs39(Box39, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
28333
+ /* @__PURE__ */ jsxs39(Text40, { color: theme.accent, bold: true, children: [
28334
+ "Changelog Image: ",
28335
+ owner,
28336
+ "/",
28337
+ repo
28338
+ ] }),
28339
+ /* @__PURE__ */ jsx41(Text40, { color: theme.info.color, dimColor: true, children: "Select a time period to summarize" }),
28340
+ /* @__PURE__ */ jsx41(Box39, { marginTop: 1, flexDirection: "column", children: PERIODS.map((period, i) => {
28341
+ const isSelected = i === selectedIndex;
28342
+ const marker = isSelected ? "\u25B8" : " ";
28343
+ return /* @__PURE__ */ jsx41(Box39, { children: /* @__PURE__ */ jsxs39(Text40, { color: isSelected ? theme.accent : theme.info.color, bold: isSelected, children: [
28344
+ marker,
28345
+ " ",
28346
+ period.label
28347
+ ] }) }, period.label);
28348
+ }) }),
28349
+ /* @__PURE__ */ jsx41(Box39, { marginTop: 1, children: /* @__PURE__ */ jsx41(Text40, { color: theme.info.color, dimColor: theme.info.dim, children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }) })
28350
+ ] });
28351
+ }
28352
+ var PERIODS;
28353
+ var init_changelog_image_picker = __esm({
28354
+ "src/ui/changelog-image-picker.tsx"() {
28355
+ "use strict";
28356
+ init_theme_context();
28357
+ PERIODS = [
28358
+ { label: "Past 24 hours", days: 1 },
28359
+ { label: "Past 7 days", days: 7 },
28360
+ { label: "Past 30 days", days: 30 }
28361
+ ];
28362
+ }
28363
+ });
28364
+
27664
28365
  // src/ui/modal-host.tsx
27665
- import { Box as Box39, Text as Text40 } from "ink";
28366
+ import { Box as Box40, Text as Text41 } from "ink";
27666
28367
  import SelectInput22 from "ink-select-input";
27667
- import { jsx as jsx41, jsxs as jsxs39 } from "react/jsx-runtime";
28368
+ import { jsx as jsx42, jsxs as jsxs40 } from "react/jsx-runtime";
27668
28369
  function ModalHost(props) {
27669
28370
  const {
27670
28371
  modals,
@@ -27697,7 +28398,7 @@ function ModalHost(props) {
27697
28398
  onInboxOpen
27698
28399
  } = props;
27699
28400
  if (modals.showRemoteDashboard) {
27700
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: selectedRemoteSession ? /* @__PURE__ */ jsx41(
28401
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: selectedRemoteSession ? /* @__PURE__ */ jsx42(
27701
28402
  RemoteSessionDetail,
27702
28403
  {
27703
28404
  session: selectedRemoteSession,
@@ -27706,7 +28407,7 @@ function ModalHost(props) {
27706
28407
  void onCancelRemoteSession(session);
27707
28408
  }
27708
28409
  }
27709
- ) : /* @__PURE__ */ jsx41(
28410
+ ) : /* @__PURE__ */ jsx42(
27710
28411
  RemoteDashboard,
27711
28412
  {
27712
28413
  onSelect: (session) => onSelectRemoteSession(session),
@@ -27715,7 +28416,7 @@ function ModalHost(props) {
27715
28416
  ) }) });
27716
28417
  }
27717
28418
  if (modals.showInboxModal) {
27718
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28419
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27719
28420
  InboxModal,
27720
28421
  {
27721
28422
  onDone: () => modals.setShowInboxModal(false),
@@ -27724,7 +28425,7 @@ function ModalHost(props) {
27724
28425
  ) }) });
27725
28426
  }
27726
28427
  if (modals.showMultiAgentModal) {
27727
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28428
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27728
28429
  MultiAgentModal,
27729
28430
  {
27730
28431
  initial: props.multiAgentSettings ?? {},
@@ -27737,7 +28438,7 @@ function ModalHost(props) {
27737
28438
  ) }) });
27738
28439
  }
27739
28440
  if (modals.showHooksDashboard) {
27740
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28441
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27741
28442
  HooksDashboard,
27742
28443
  {
27743
28444
  getConfigured: props.getConfiguredHooks,
@@ -27748,7 +28449,7 @@ function ModalHost(props) {
27748
28449
  ) }) });
27749
28450
  }
27750
28451
  if (modals.showHelpMenu) {
27751
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28452
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27752
28453
  HelpMenu,
27753
28454
  {
27754
28455
  customCommands: customCommands.map((c) => ({ name: c.name, description: c.description })),
@@ -27762,10 +28463,10 @@ function ModalHost(props) {
27762
28463
  ) }) });
27763
28464
  }
27764
28465
  if (modals.showShellPicker) {
27765
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ShellPicker, { current: props.currentShell, onPick: props.onPickShell }) }) });
28466
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(ShellPicker, { current: props.currentShell, onPick: props.onPickShell }) }) });
27766
28467
  }
27767
28468
  if (modals.showMemoryPicker) {
27768
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28469
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27769
28470
  MemoryPicker,
27770
28471
  {
27771
28472
  enabled: props.memoryEnabled,
@@ -27776,7 +28477,7 @@ function ModalHost(props) {
27776
28477
  ) }) });
27777
28478
  }
27778
28479
  if (modals.showGatewayPicker) {
27779
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28480
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27780
28481
  GatewayPicker,
27781
28482
  {
27782
28483
  gatewayId: props.gatewayId,
@@ -27789,10 +28490,10 @@ function ModalHost(props) {
27789
28490
  ) }) });
27790
28491
  }
27791
28492
  if (modals.showSkillsPicker) {
27792
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(SkillsPicker, { onAction: props.onSkillsAction, onDone: props.onSkillsDone }) }) });
28493
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(SkillsPicker, { onAction: props.onSkillsAction, onDone: props.onSkillsDone }) }) });
27793
28494
  }
27794
28495
  if (modals.showLspWizard) {
27795
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28496
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27796
28497
  LspWizard,
27797
28498
  {
27798
28499
  servers: lspServers,
@@ -27804,7 +28505,7 @@ function ModalHost(props) {
27804
28505
  ) }) });
27805
28506
  }
27806
28507
  if (modals.commandWizard) {
27807
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28508
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27808
28509
  CommandWizard,
27809
28510
  {
27810
28511
  mode: modals.commandWizard.mode,
@@ -27818,7 +28519,7 @@ function ModalHost(props) {
27818
28519
  }
27819
28520
  if (modals.commandPicker) {
27820
28521
  const pickerMode = modals.commandPicker.mode;
27821
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28522
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27822
28523
  CommandPicker,
27823
28524
  {
27824
28525
  commands: customCommands,
@@ -27837,14 +28538,14 @@ function ModalHost(props) {
27837
28538
  }
27838
28539
  if (modals.commandToDelete) {
27839
28540
  const cmd = modals.commandToDelete;
27840
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsxs39(Box39, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
27841
- /* @__PURE__ */ jsxs39(Text40, { color: theme.accent, bold: true, children: [
28541
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsxs40(Box40, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
28542
+ /* @__PURE__ */ jsxs40(Text41, { color: theme.accent, bold: true, children: [
27842
28543
  "Delete /",
27843
28544
  cmd.name,
27844
28545
  "?"
27845
28546
  ] }),
27846
- /* @__PURE__ */ jsx41(Text40, { color: theme.info.color, children: cmd.filepath }),
27847
- /* @__PURE__ */ jsx41(Box39, { marginTop: 1, children: /* @__PURE__ */ jsx41(
28547
+ /* @__PURE__ */ jsx42(Text41, { color: theme.info.color, children: cmd.filepath }),
28548
+ /* @__PURE__ */ jsx42(Box40, { marginTop: 1, children: /* @__PURE__ */ jsx42(
27848
28549
  SelectInput22,
27849
28550
  {
27850
28551
  items: [
@@ -27863,7 +28564,7 @@ function ModalHost(props) {
27863
28564
  ] }) });
27864
28565
  }
27865
28566
  if (modals.showCommandList) {
27866
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28567
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27867
28568
  CommandList,
27868
28569
  {
27869
28570
  commands: customCommands,
@@ -27872,24 +28573,24 @@ function ModalHost(props) {
27872
28573
  ) }) });
27873
28574
  }
27874
28575
  if (modals.showThemePicker) {
27875
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ThemePicker, { themes, onPick: onPickTheme }) }) });
28576
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(ThemePicker, { themes, onPick: onPickTheme }) }) });
27876
28577
  }
27877
28578
  if (modals.showUiPicker) {
27878
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(UiPicker, { current: currentUiEngine, onPick: onPickUi }) }) });
28579
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(UiPicker, { current: currentUiEngine, onPick: onPickUi }) }) });
27879
28580
  }
27880
28581
  if (modals.showModelPicker) {
27881
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ModelPicker, { current: currentModel, onPick: onPickModel }) }) });
28582
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(ModelPicker, { current: currentModel, onPick: onPickModel }) }) });
27882
28583
  }
27883
28584
  if (modals.showModePicker) {
27884
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ModePicker, { current: props.currentMode, onPick: props.onPickMode, multiAgentEnabled: props.multiAgentEnabled }) }) });
28585
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(ModePicker, { current: props.currentMode, onPick: props.onPickMode, multiAgentEnabled: props.multiAgentEnabled }) }) });
27885
28586
  }
27886
28587
  if (modals.billingChooserFor) {
27887
28588
  const model = modals.billingChooserFor;
27888
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(BillingChooser, { model, onPick: (choice) => onPickBilling(model, choice) }) }) });
28589
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(BillingChooser, { model, onPick: (choice) => onPickBilling(model, choice) }) }) });
27889
28590
  }
27890
28591
  if (modals.unifiedProbeFor) {
27891
28592
  const model = modals.unifiedProbeFor;
27892
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28593
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27893
28594
  UnifiedBillingStatus,
27894
28595
  {
27895
28596
  model,
@@ -27902,7 +28603,7 @@ function ModalHost(props) {
27902
28603
  }
27903
28604
  if (modals.keyEntryFor) {
27904
28605
  const model = modals.keyEntryFor;
27905
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
28606
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
27906
28607
  KeyEntryModal,
27907
28608
  {
27908
28609
  model,
@@ -27914,6 +28615,17 @@ function ModalHost(props) {
27914
28615
  }
27915
28616
  ) }) });
27916
28617
  }
28618
+ if (modals.showChangelogImagePicker && props.changelogImageRepo) {
28619
+ return /* @__PURE__ */ jsx42(ThemeProvider, { theme, children: /* @__PURE__ */ jsx42(Box40, { flexDirection: "column", children: /* @__PURE__ */ jsx42(
28620
+ ChangelogImagePicker,
28621
+ {
28622
+ owner: props.changelogImageRepo.owner,
28623
+ repo: props.changelogImageRepo.name,
28624
+ onGenerate: props.onChangelogImageGenerate,
28625
+ onCancel: props.onChangelogImageCancel
28626
+ }
28627
+ ) }) });
28628
+ }
27917
28629
  return null;
27918
28630
  }
27919
28631
  function ModalOverlay({
@@ -27923,7 +28635,7 @@ function ModalOverlay({
27923
28635
  }) {
27924
28636
  if (modals.limitModal) {
27925
28637
  const m = modals.limitModal;
27926
- return /* @__PURE__ */ jsx41(
28638
+ return /* @__PURE__ */ jsx42(
27927
28639
  LimitModal,
27928
28640
  {
27929
28641
  limit: m.limit,
@@ -27937,7 +28649,7 @@ function ModalOverlay({
27937
28649
  }
27938
28650
  if (modals.loopModal) {
27939
28651
  const m = modals.loopModal;
27940
- return /* @__PURE__ */ jsx41(
28652
+ return /* @__PURE__ */ jsx42(
27941
28653
  LimitModal,
27942
28654
  {
27943
28655
  limit: 50,
@@ -27982,13 +28694,14 @@ var init_modal_host = __esm({
27982
28694
  init_multi_agent_modal();
27983
28695
  init_hooks_dashboard();
27984
28696
  init_help_menu();
28697
+ init_changelog_image_picker();
27985
28698
  }
27986
28699
  });
27987
28700
 
27988
28701
  // src/ui/plan-complete-picker.tsx
27989
- import { Box as Box40, Text as Text41 } from "ink";
28702
+ import { Box as Box41, Text as Text42 } from "ink";
27990
28703
  import SelectInput23 from "ink-select-input";
27991
- import { jsx as jsx42, jsxs as jsxs40 } from "react/jsx-runtime";
28704
+ import { jsx as jsx43, jsxs as jsxs41 } from "react/jsx-runtime";
27992
28705
  function PlanCompletePicker({ onPick }) {
27993
28706
  const theme = useTheme();
27994
28707
  const items = [
@@ -27996,10 +28709,10 @@ function PlanCompletePicker({ onPick }) {
27996
28709
  { label: "\u25B8 Start building and ask for permission (edit mode)", value: "edit", key: "edit" },
27997
28710
  { label: "\u25B8 Continue planning / ask a question", value: "continue", key: "continue" }
27998
28711
  ];
27999
- return /* @__PURE__ */ jsxs40(Box40, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
28000
- /* @__PURE__ */ jsx42(Text41, { color: theme.accent, bold: true, children: "Plan complete \u2014 what next?" }),
28001
- /* @__PURE__ */ jsx42(Text41, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select, Esc to cancel." }),
28002
- /* @__PURE__ */ jsx42(Box40, { marginTop: 1, children: /* @__PURE__ */ jsx42(
28712
+ return /* @__PURE__ */ jsxs41(Box41, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
28713
+ /* @__PURE__ */ jsx43(Text42, { color: theme.accent, bold: true, children: "Plan complete \u2014 what next?" }),
28714
+ /* @__PURE__ */ jsx43(Text42, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select, Esc to cancel." }),
28715
+ /* @__PURE__ */ jsx43(Box41, { marginTop: 1, children: /* @__PURE__ */ jsx43(
28003
28716
  SelectInput23,
28004
28717
  {
28005
28718
  items,
@@ -28018,7 +28731,7 @@ var init_plan_complete_picker = __esm({
28018
28731
  });
28019
28732
 
28020
28733
  // src/ui/use-session-manager.ts
28021
- import { useCallback as useCallback8, useRef as useRef5, useState as useState26 } from "react";
28734
+ import { useCallback as useCallback8, useRef as useRef5, useState as useState27 } from "react";
28022
28735
  function extractFirstUserText(messages) {
28023
28736
  const firstUser = messages.find((m) => m.role === "user");
28024
28737
  if (!firstUser) return "session";
@@ -28035,9 +28748,9 @@ function useSessionManager(deps) {
28035
28748
  const sessionIdRef = useRef5(null);
28036
28749
  const sessionCreatedAtRef = useRef5(null);
28037
28750
  const sessionTitleRef = useRef5(null);
28038
- const [resumeSessions, setResumeSessions] = useState26(null);
28039
- const [checkpointSession, setCheckpointSession] = useState26(null);
28040
- const [checkpointList, setCheckpointList] = useState26([]);
28751
+ const [resumeSessions, setResumeSessions] = useState27(null);
28752
+ const [checkpointSession, setCheckpointSession] = useState27(null);
28753
+ const [checkpointList, setCheckpointList] = useState27([]);
28041
28754
  const depsRef = useRef5(deps);
28042
28755
  depsRef.current = deps;
28043
28756
  const ensureSessionId = useCallback8(() => {
@@ -28109,8 +28822,22 @@ function useSessionManager(deps) {
28109
28822
  } catch {
28110
28823
  }
28111
28824
  }
28112
- const msg = checkpointId ? `resumed session ${file.id} from checkpoint` : `resumed session ${file.id} (${file.messages.filter((m) => m.role !== "system").length} msgs)`;
28825
+ const nonSystemCount = file.messages.filter((m) => m.role !== "system").length;
28826
+ const msg = checkpointId ? `resumed session ${file.id} from checkpoint` : `resumed session ${file.id} (${nonSystemCount} msgs)`;
28113
28827
  d.setEvents([{ kind: "info", key: d.mkKey(), text: msg }]);
28828
+ if (!checkpointId) {
28829
+ const threshold = d.cfg?.autoFreshSuggestionTurns ?? DEFAULT_AUTO_FRESH_SUGGESTION_TURNS;
28830
+ if (threshold > 0 && nonSystemCount >= threshold && (d.mode === "auto" || d.mode === "edit" || d.mode === "multi-agent-experimental")) {
28831
+ d.setEvents((es) => [
28832
+ ...es,
28833
+ {
28834
+ kind: "info",
28835
+ key: d.mkKey(),
28836
+ text: `This session has ${nonSystemCount} turns. The model may slow down with accumulated context. Run /fresh to continue with a summarized state, or /compact to compress history.`
28837
+ }
28838
+ ]);
28839
+ }
28840
+ }
28114
28841
  const userMsgs = file.messages.filter((m) => m.role === "user" && m.content).map((m) => {
28115
28842
  if (!m.content) return "";
28116
28843
  if (typeof m.content === "string") return m.content;
@@ -28209,30 +28936,31 @@ var init_use_session_manager = __esm({
28209
28936
  init_session_state();
28210
28937
  init_usage_tracker();
28211
28938
  init_log_sink();
28939
+ init_app_helpers();
28212
28940
  }
28213
28941
  });
28214
28942
 
28215
28943
  // src/ui/use-turn-controller.ts
28216
- import { useCallback as useCallback9, useRef as useRef6, useState as useState27 } from "react";
28944
+ import { useCallback as useCallback9, useRef as useRef6, useState as useState28 } from "react";
28217
28945
  function useTurnController() {
28218
- const [busy, setBusy] = useState27(false);
28946
+ const [busy, setBusy] = useState28(false);
28219
28947
  const busyRef = useRef6(false);
28220
28948
  const isAbortingRef = useRef6(false);
28221
28949
  const lastEscapeAtRef = useRef6(0);
28222
28950
  const supervisorRef = useRef6(new TurnSupervisor());
28223
- const [turnPhase, setTurnPhase] = useState27("waiting");
28224
- const [turnStartedAt, setTurnStartedAt] = useState27(null);
28225
- const [currentToolName, setCurrentToolName] = useState27(null);
28226
- const [lastActivityAt, setLastActivityAt] = useState27(null);
28951
+ const [turnPhase, setTurnPhase] = useState28("waiting");
28952
+ const [turnStartedAt, setTurnStartedAt] = useState28(null);
28953
+ const [currentToolName, setCurrentToolName] = useState28(null);
28954
+ const [lastActivityAt, setLastActivityAt] = useState28(null);
28227
28955
  const turnCounterRef = useRef6(0);
28228
- const [showReasoning, setShowReasoning] = useState27(false);
28956
+ const [showReasoning, setShowReasoning] = useState28(false);
28229
28957
  const toggleReasoning = useCallback9(() => {
28230
28958
  setShowReasoning((s) => !s);
28231
28959
  }, []);
28232
- const [tasks, setTasks] = useState27([]);
28960
+ const [tasks, setTasks] = useState28([]);
28233
28961
  const tasksRef = useRef6([]);
28234
- const [tasksStartedAt, setTasksStartedAt] = useState27(null);
28235
- const [tasksStartTokens, setTasksStartTokens] = useState27(0);
28962
+ const [tasksStartedAt, setTasksStartedAt] = useState28(null);
28963
+ const [tasksStartTokens, setTasksStartTokens] = useState28(0);
28236
28964
  const beginTurn = useCallback9(() => {
28237
28965
  setBusy(true);
28238
28966
  busyRef.current = true;
@@ -28358,15 +29086,15 @@ var tui_report_exports = {};
28358
29086
  __export(tui_report_exports, {
28359
29087
  getCategoryReportText: () => getCategoryReportText
28360
29088
  });
28361
- import { readFile as readFile19 } from "fs/promises";
28362
- import { join as join31 } from "path";
29089
+ import { readFile as readFile20 } from "fs/promises";
29090
+ import { join as join32 } from "path";
28363
29091
  import { homedir as homedir16 } from "os";
28364
29092
  function usageDir3() {
28365
- const xdg = process.env.XDG_DATA_HOME || join31(homedir16(), ".local", "share");
28366
- return join31(xdg, "kimiflare");
29093
+ const xdg = process.env.XDG_DATA_HOME || join32(homedir16(), ".local", "share");
29094
+ return join32(xdg, "kimiflare");
28367
29095
  }
28368
29096
  function usagePath3() {
28369
- return join31(usageDir3(), "usage.json");
29097
+ return join32(usageDir3(), "usage.json");
28370
29098
  }
28371
29099
  function today3() {
28372
29100
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -28378,7 +29106,7 @@ function daysAgo2(n) {
28378
29106
  }
28379
29107
  async function loadLog3() {
28380
29108
  try {
28381
- const raw = await readFile19(usagePath3(), "utf8");
29109
+ const raw = await readFile20(usagePath3(), "utf8");
28382
29110
  return JSON.parse(raw);
28383
29111
  } catch {
28384
29112
  return { version: 1, days: [], sessions: [] };
@@ -28435,10 +29163,10 @@ var init_tui_report = __esm({
28435
29163
  });
28436
29164
 
28437
29165
  // src/ui/slash-commands.ts
28438
- import { join as join32 } from "path";
29166
+ import { join as join33 } from "path";
28439
29167
  import { unlink as unlink5 } from "fs/promises";
28440
29168
  import QRCode from "qrcode";
28441
- function executeFreshStart(ctx, planText) {
29169
+ function executeFreshStart(ctx, planText, overrideMode) {
28442
29170
  const oldSessionId = ctx.sessionIdRef.current;
28443
29171
  if (ctx.cacheStableRef.current && ctx.messagesRef.current.length >= 2) {
28444
29172
  ctx.messagesRef.current = [ctx.messagesRef.current[0], ctx.messagesRef.current[1]];
@@ -28464,7 +29192,15 @@ function executeFreshStart(ctx, planText) {
28464
29192
  ctx.clearTaskTracking();
28465
29193
  ctx.compactSuggestedRef.current = false;
28466
29194
  ctx.updateNudgedRef.current = false;
29195
+ ctx.freshSuggestedRef.current = false;
28467
29196
  ctx.sessionPlanRef.current = null;
29197
+ rebuildSystemPromptForMode(
29198
+ ctx.messagesRef.current,
29199
+ ctx.cacheStableRef.current,
29200
+ ctx.cfg?.model ?? "@cf/moonshotai/kimi-k2.6",
29201
+ overrideMode ?? ctx.mode,
29202
+ [...ALL_TOOLS, ...ctx.mcpToolsRef.current, ...ctx.lspToolsRef.current]
29203
+ );
28468
29204
  ctx.messagesRef.current.push({ role: "user", content: planText });
28469
29205
  const newSessionId = ctx.ensureSessionId();
28470
29206
  if (oldSessionId) {
@@ -28483,7 +29219,7 @@ function dispatchSlashCommand(ctx, cmd) {
28483
29219
  if (!handler) return false;
28484
29220
  return handler(ctx, rest, arg);
28485
29221
  }
28486
- var handleExit, handleClear, handleFresh, handleReasoning, handleCost, handleShell, handleModel, handleGateway, handleMode, handleMultiAgent, handleTheme, handleUi, handlePlan, handleAuto, handleEdit, handleSkills, handleMemory, handleResume, handleCheckpoint, handleCompact, handleInit, handleUpdate, handleMcp, handleLsp, handleHooks, handleHello, handleInbox, handleLogout, handleCommand, handleRemote, handleHelp, handlers;
29222
+ var handleExit, handleClear, handleFresh, handleReasoning, handleCost, handleShell, handleModel, handleGateway, handleMode, handleMultiAgent, handleTheme, handleUi, handlePlan, handleAuto, handleEdit, handleSkills, handleMemory, handleResume, handleCheckpoint, handleCompact, handleInit, handleUpdate, handleMcp, handleLsp, handleHooks, handleHello, handleInbox, handleLogout, handleCommand, handleRemote, handleHelp, handleChangelogImage, handlers;
28487
29223
  var init_slash_commands = __esm({
28488
29224
  "src/ui/slash-commands.ts"() {
28489
29225
  "use strict";
@@ -28497,6 +29233,7 @@ var init_slash_commands = __esm({
28497
29233
  init_manager4();
28498
29234
  init_sessions();
28499
29235
  init_session_state();
29236
+ init_executor();
28500
29237
  init_recommended();
28501
29238
  init_settings();
28502
29239
  init_types2();
@@ -28507,7 +29244,7 @@ var init_slash_commands = __esm({
28507
29244
  init_session_store();
28508
29245
  init_deploy();
28509
29246
  init_tui_auth();
28510
- init_distill();
29247
+ init_continuation_summary();
28511
29248
  init_clipboard();
28512
29249
  handleExit = (ctx) => {
28513
29250
  void ctx.lspManagerRef.current.stopAll().finally(() => ctx.exit());
@@ -28546,11 +29283,12 @@ var init_slash_commands = __esm({
28546
29283
  ctx.clearTaskTracking();
28547
29284
  ctx.compactSuggestedRef.current = false;
28548
29285
  ctx.updateNudgedRef.current = false;
29286
+ ctx.freshSuggestedRef.current = false;
28549
29287
  ctx.sessionPlanRef.current = null;
28550
29288
  return true;
28551
29289
  };
28552
- handleFresh = (ctx) => {
28553
- const { busy, mkKey: mkKey2, setEvents } = ctx;
29290
+ handleFresh = async (ctx) => {
29291
+ const { busy, mkKey: mkKey2, setEvents, cfg } = ctx;
28554
29292
  if (busy) {
28555
29293
  setEvents((e) => [
28556
29294
  ...e,
@@ -28558,27 +29296,42 @@ var init_slash_commands = __esm({
28558
29296
  ]);
28559
29297
  return true;
28560
29298
  }
28561
- const plan = ctx.sessionPlanRef.current ?? distillSessionPlan(ctx.messagesRef.current);
28562
- if (!plan) {
29299
+ const summary = await generateContinuationSummary({
29300
+ messages: ctx.messagesRef.current,
29301
+ mode: ctx.mode,
29302
+ accountId: cfg?.accountId ?? "",
29303
+ apiToken: cfg?.apiToken ?? "",
29304
+ model: cfg?.plumbingModel ?? "@cf/moonshotai/kimi-k2.5",
29305
+ gateway: cfg?.aiGatewayId ? {
29306
+ id: cfg.aiGatewayId,
29307
+ cacheTtl: cfg.aiGatewayCacheTtl,
29308
+ skipCache: cfg.aiGatewaySkipCache,
29309
+ collectLogPayload: cfg.aiGatewayCollectLogPayload,
29310
+ metadata: cfg.aiGatewayMetadata
29311
+ } : void 0,
29312
+ memoryManager: ctx.memoryManagerRef.current,
29313
+ memoryEnabled: cfg?.memoryEnabled
29314
+ });
29315
+ if (!summary) {
28563
29316
  setEvents((e) => [
28564
29317
  ...e,
28565
- { kind: "error", key: mkKey2(), text: "No plan found to start fresh with." }
29318
+ { kind: "error", key: mkKey2(), text: "No plan or summary found to start fresh with." }
28566
29319
  ]);
28567
29320
  return true;
28568
29321
  }
28569
- const clipResult = executeFreshStart(ctx, plan);
29322
+ const clipResult = executeFreshStart(ctx, summary);
28570
29323
  setEvents((e) => [
28571
29324
  ...e,
28572
29325
  {
28573
29326
  kind: "info",
28574
29327
  key: mkKey2(),
28575
- text: clipResult.success ? "Plan copied to clipboard. Starting fresh session with plan only\u2026" : "Clipboard unavailable. Starting fresh session with plan only\u2026"
29328
+ text: clipResult.success ? "Summary copied to clipboard. Starting fresh session with continuation context\u2026" : "Clipboard unavailable. Starting fresh session with continuation context\u2026"
28576
29329
  }
28577
29330
  ]);
28578
29331
  if (!clipResult.success) {
28579
29332
  setEvents((e) => [
28580
29333
  ...e,
28581
- { kind: "info", key: mkKey2(), text: "--- Plan ---\n" + plan }
29334
+ { kind: "info", key: mkKey2(), text: "--- Continuation Context ---\n" + summary }
28582
29335
  ]);
28583
29336
  }
28584
29337
  return true;
@@ -29249,7 +30002,7 @@ ${lines.join("\n")}` }]);
29249
30002
  void (async () => {
29250
30003
  try {
29251
30004
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
29252
- const file = await loadSession(join32(sessionsDir3(), `${currentId}.json`));
30005
+ const file = await loadSession(join33(sessionsDir3(), `${currentId}.json`));
29253
30006
  const cps = file.checkpoints ?? [];
29254
30007
  if (cps.length === 0) {
29255
30008
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
@@ -29294,7 +30047,7 @@ ${lines.join("\n")}` }]);
29294
30047
  try {
29295
30048
  ctx.ensureSessionId();
29296
30049
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
29297
- const filePath = join32(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
30050
+ const filePath = join33(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
29298
30051
  await addCheckpoint(filePath, cp);
29299
30052
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
29300
30053
  } catch (e) {
@@ -29555,8 +30308,8 @@ project: ${projectSettingsPath(cwd)}`
29555
30308
  handleHello = (ctx) => {
29556
30309
  const { setEvents, mkKey: mkKey2 } = ctx;
29557
30310
  const session = crypto.randomUUID();
29558
- const url = `${FEEDBACK_WORKER_URL2}/?s=${session}&v=${getAppVersion()}`;
29559
- openBrowser2(url);
30311
+ const url = `${FEEDBACK_WORKER_URL}/?s=${session}&v=${getAppVersion()}`;
30312
+ openBrowser(url);
29560
30313
  void (async () => {
29561
30314
  try {
29562
30315
  const qr = await QRCode.toString(url, { type: "terminal", small: true });
@@ -29709,7 +30462,7 @@ project: ${projectSettingsPath(cwd)}`
29709
30462
  setEvents((e) => [
29710
30463
  ...e,
29711
30464
  { kind: "info", key: mkKey2(), text: `Starting remote session for ${repo.owner}/${repo.name}...` },
29712
- { kind: "info", key: mkKey2(), text: `Budget: ${formatTokens3(budget)} tokens. TTL: ${ttl} min.` }
30465
+ { kind: "info", key: mkKey2(), text: `Budget: ${formatTokens(budget)} tokens. TTL: ${ttl} min.` }
29713
30466
  ]);
29714
30467
  try {
29715
30468
  const data = await startRemoteSession({
@@ -29781,6 +30534,115 @@ project: ${projectSettingsPath(cwd)}`
29781
30534
  ctx.setShowHelpMenu(true);
29782
30535
  return true;
29783
30536
  };
30537
+ handleChangelogImage = (ctx, rest) => {
30538
+ const { cfg, setEvents, mkKey: mkKey2, setShowChangelogImagePicker, setChangelogImageRepo, setTasks, setTasksStartedAt } = ctx;
30539
+ if (!cfg) {
30540
+ setEvents((e) => [...e, { kind: "error", key: mkKey2(), text: "Not configured yet." }]);
30541
+ return true;
30542
+ }
30543
+ let owner = cfg.githubRepo?.split("/")[0];
30544
+ let repo = cfg.githubRepo?.split("/")[1];
30545
+ let days = 7;
30546
+ if (rest.length > 0 && rest[0].includes("/")) {
30547
+ const parts = rest[0].split("/");
30548
+ owner = parts[0];
30549
+ repo = parts[1];
30550
+ }
30551
+ if (rest.length > 1) {
30552
+ const d = parseInt(rest[1], 10);
30553
+ if (!Number.isNaN(d)) days = d;
30554
+ }
30555
+ const runGeneration = (o, r, d) => {
30556
+ const asstId = mkAssistantId();
30557
+ setEvents((e) => [
30558
+ ...e,
30559
+ {
30560
+ kind: "assistant",
30561
+ key: `asst_${asstId}`,
30562
+ id: asstId,
30563
+ text: `Generating changelog image for ${o}/${r} (last ${d} day${d === 1 ? "" : "s"})\u2026`,
30564
+ reasoning: "",
30565
+ streaming: true
30566
+ }
30567
+ ]);
30568
+ const taskList = [
30569
+ { id: "fetch-prs", title: "Fetch merged PRs", status: "pending" },
30570
+ { id: "fetch-release", title: "Fetch latest release", status: "pending" },
30571
+ { id: "summarize", title: "Summarize with LLM", status: "pending" },
30572
+ { id: "render", title: "Render changelog image", status: "pending" },
30573
+ { id: "save", title: "Save PNG file", status: "pending" }
30574
+ ];
30575
+ setTasks(taskList);
30576
+ setTasksStartedAt(Date.now());
30577
+ const updateTask = (id, status) => {
30578
+ setTasks((prev) => prev.map((t) => t.id === id ? { ...t, status } : t));
30579
+ };
30580
+ void (async () => {
30581
+ try {
30582
+ const { changelogImageTool: changelogImageTool2 } = await Promise.resolve().then(() => (init_changelog_image(), changelog_image_exports));
30583
+ const { gatewayFromConfig: gatewayFromConfig2 } = await Promise.resolve().then(() => (init_app_helpers(), app_helpers_exports));
30584
+ updateTask("fetch-prs", "in_progress");
30585
+ updateTask("fetch-release", "in_progress");
30586
+ const result = await changelogImageTool2.run({ owner: o, repo: r, days: d }, {
30587
+ cwd: process.cwd(),
30588
+ githubToken: cfg.githubOAuthToken,
30589
+ accountId: cfg.accountId,
30590
+ apiToken: cfg.apiToken,
30591
+ model: cfg.model,
30592
+ gateway: gatewayFromConfig2(cfg)
30593
+ });
30594
+ updateTask("fetch-prs", "completed");
30595
+ updateTask("fetch-release", "completed");
30596
+ updateTask("summarize", "completed");
30597
+ updateTask("render", "completed");
30598
+ updateTask("save", "completed");
30599
+ const text = typeof result === "string" ? result : result.content;
30600
+ setEvents(
30601
+ (e) => e.map(
30602
+ (ev) => ev.kind === "assistant" && ev.id === asstId ? { ...ev, text, streaming: false } : ev
30603
+ )
30604
+ );
30605
+ } catch (err) {
30606
+ const msg = `changelog-image failed: ${err instanceof Error ? err.message : String(err)}`;
30607
+ setEvents(
30608
+ (e) => e.map(
30609
+ (ev) => ev.kind === "assistant" && ev.id === asstId ? { ...ev, text: msg, streaming: false } : ev
30610
+ )
30611
+ );
30612
+ } finally {
30613
+ setTasksStartedAt(null);
30614
+ }
30615
+ })();
30616
+ };
30617
+ const tryOpenPicker = (o, r) => {
30618
+ if (o && r) {
30619
+ if (rest.length === 0) {
30620
+ setChangelogImageRepo({ owner: o, name: r });
30621
+ setShowChangelogImagePicker(true);
30622
+ return;
30623
+ }
30624
+ runGeneration(o, r, days);
30625
+ return;
30626
+ }
30627
+ setEvents((e) => [
30628
+ ...e,
30629
+ {
30630
+ kind: "error",
30631
+ key: mkKey2(),
30632
+ text: "Usage: /changelog-image [owner/repo] [days]\nSet githubRepo in config or pass owner/repo explicitly."
30633
+ }
30634
+ ]);
30635
+ };
30636
+ if (owner && repo) {
30637
+ tryOpenPicker(owner, repo);
30638
+ return true;
30639
+ }
30640
+ void Promise.resolve().then(() => (init_app_helpers(), app_helpers_exports)).then(({ detectGitHubRepo: detectGitHubRepo3 }) => {
30641
+ const detected = detectGitHubRepo3();
30642
+ tryOpenPicker(detected?.owner, detected?.name);
30643
+ });
30644
+ return true;
30645
+ };
29784
30646
  handlers = {
29785
30647
  "/exit": handleExit,
29786
30648
  "/clear": handleClear,
@@ -29812,6 +30674,7 @@ project: ${projectSettingsPath(cwd)}`
29812
30674
  "/logout": handleLogout,
29813
30675
  "/command": handleCommand,
29814
30676
  "/remote": handleRemote,
30677
+ "/changelog-image": handleChangelogImage,
29815
30678
  "/help": handleHelp
29816
30679
  };
29817
30680
  }
@@ -29819,7 +30682,7 @@ project: ${projectSettingsPath(cwd)}`
29819
30682
 
29820
30683
  // src/init/run-init.ts
29821
30684
  import { existsSync as existsSync6 } from "fs";
29822
- import { join as join33 } from "path";
30685
+ import { join as join34 } from "path";
29823
30686
  async function runInit(deps) {
29824
30687
  const {
29825
30688
  cfg,
@@ -29916,7 +30779,7 @@ async function runInit(deps) {
29916
30779
  lspManagerRef.current.notifyChange(path, content);
29917
30780
  } else {
29918
30781
  void import("fs/promises").then(
29919
- ({ readFile: readFile22 }) => readFile22(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
30782
+ ({ readFile: readFile23 }) => readFile23(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
29920
30783
  })
29921
30784
  );
29922
30785
  }
@@ -30022,7 +30885,7 @@ async function runInit(deps) {
30022
30885
  }
30023
30886
  }
30024
30887
  });
30025
- if (existsSync6(join33(cwd, "KIMI.md"))) {
30888
+ if (existsSync6(join34(cwd, "KIMI.md"))) {
30026
30889
  if (cacheStableRef.current) {
30027
30890
  messagesRef.current[1] = {
30028
30891
  role: "system",
@@ -30116,8 +30979,8 @@ var init_run_init = __esm({
30116
30979
  });
30117
30980
 
30118
30981
  // src/skills/discovery.ts
30119
- import { readdir as readdir9, stat as stat7, readFile as readFile20 } from "fs/promises";
30120
- import { join as join34, extname as extname2 } from "path";
30982
+ import { readdir as readdir9, stat as stat7, readFile as readFile21 } from "fs/promises";
30983
+ import { join as join35, extname as extname2 } from "path";
30121
30984
  async function dirExists(path) {
30122
30985
  try {
30123
30986
  const s = await stat7(path);
@@ -30141,20 +31004,20 @@ async function scanSkillDir(dirPath, source) {
30141
31004
  for (const entry of entries) {
30142
31005
  if (!entry.isFile()) continue;
30143
31006
  if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
30144
- files.push({ filePath: join34(dirPath, entry.name), source });
31007
+ files.push({ filePath: join35(dirPath, entry.name), source });
30145
31008
  }
30146
31009
  return files;
30147
31010
  }
30148
31011
  async function discoverSkills(cwd) {
30149
- const agentsSkills = await scanSkillDir(join34(cwd, ".agents", "skills"), "agents");
30150
- const agentsMd = await fileExists(join34(cwd, "AGENTS.md")) ? [{ filePath: join34(cwd, "AGENTS.md"), source: "agents-md" }] : [];
30151
- const githubSkills = await scanSkillDir(join34(cwd, ".github", "skills"), "github");
30152
- const kimiflareSkills = await scanSkillDir(join34(cwd, ".kimiflare", "skills"), "kimiflare");
31012
+ const agentsSkills = await scanSkillDir(join35(cwd, ".agents", "skills"), "agents");
31013
+ const agentsMd = await fileExists(join35(cwd, "AGENTS.md")) ? [{ filePath: join35(cwd, "AGENTS.md"), source: "agents-md" }] : [];
31014
+ const githubSkills = await scanSkillDir(join35(cwd, ".github", "skills"), "github");
31015
+ const kimiflareSkills = await scanSkillDir(join35(cwd, ".kimiflare", "skills"), "kimiflare");
30153
31016
  const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
30154
31017
  return ordered;
30155
31018
  }
30156
31019
  async function readSkillFile(filePath) {
30157
- const bytes = await readFile20(filePath);
31020
+ const bytes = await readFile21(filePath);
30158
31021
  return { bytes, text: bytes.toString("utf-8") };
30159
31022
  }
30160
31023
  var SKILL_EXTENSIONS;
@@ -30359,16 +31222,16 @@ var init_skills = __esm({
30359
31222
  });
30360
31223
 
30361
31224
  // src/util/state.ts
30362
- import { readFile as readFile21, writeFile as writeFile13, mkdir as mkdir12 } from "fs/promises";
31225
+ import { readFile as readFile22, writeFile as writeFile14, mkdir as mkdir12 } from "fs/promises";
30363
31226
  import { homedir as homedir17 } from "os";
30364
- import { join as join35 } from "path";
31227
+ import { join as join36 } from "path";
30365
31228
  function statePath() {
30366
- const xdg = process.env.XDG_CONFIG_HOME || join35(homedir17(), ".config");
30367
- return join35(xdg, "kimiflare", "state.json");
31229
+ const xdg = process.env.XDG_CONFIG_HOME || join36(homedir17(), ".config");
31230
+ return join36(xdg, "kimiflare", "state.json");
30368
31231
  }
30369
31232
  async function readState() {
30370
31233
  try {
30371
- const raw = await readFile21(statePath(), "utf8");
31234
+ const raw = await readFile22(statePath(), "utf8");
30372
31235
  return JSON.parse(raw);
30373
31236
  } catch {
30374
31237
  return {};
@@ -30376,8 +31239,8 @@ async function readState() {
30376
31239
  }
30377
31240
  async function writeState(state) {
30378
31241
  const path = statePath();
30379
- await mkdir12(join35(path, ".."), { recursive: true });
30380
- await writeFile13(path, JSON.stringify(state, null, 2) + "\n", "utf8");
31242
+ await mkdir12(join36(path, ".."), { recursive: true });
31243
+ await writeFile14(path, JSON.stringify(state, null, 2) + "\n", "utf8");
30381
31244
  }
30382
31245
  async function markCreatorMessageSeen(version) {
30383
31246
  const state = await readState();
@@ -30396,7 +31259,7 @@ var init_state = __esm({
30396
31259
 
30397
31260
  // src/ui/run-startup-tasks.ts
30398
31261
  import { existsSync as existsSync7 } from "fs";
30399
- import { join as join36 } from "path";
31262
+ import { join as join37 } from "path";
30400
31263
  function runStartupTasks(deps) {
30401
31264
  const {
30402
31265
  cfg,
@@ -30447,7 +31310,7 @@ function runStartupTasks(deps) {
30447
31310
  }
30448
31311
  });
30449
31312
  if (cfg.memoryEnabled) {
30450
- const dbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
31313
+ const dbPath = cfg.memoryDbPath ?? join37(process.cwd(), ".kimiflare", "memory.db");
30451
31314
  const manager = new MemoryManager({
30452
31315
  dbPath,
30453
31316
  accountId: cfg.accountId,
@@ -30481,7 +31344,7 @@ function runStartupTasks(deps) {
30481
31344
  });
30482
31345
  const cwd = process.cwd();
30483
31346
  sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
30484
- if (existsSync7(join36(cwd, "KIMI.md"))) {
31347
+ if (existsSync7(join37(cwd, "KIMI.md"))) {
30485
31348
  const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
30486
31349
  const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
30487
31350
  if (driftCount >= 5) {
@@ -30492,7 +31355,7 @@ function runStartupTasks(deps) {
30492
31355
  memoryManagerRef.current?.close();
30493
31356
  memoryManagerRef.current = null;
30494
31357
  }
30495
- const skillDbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
31358
+ const skillDbPath = cfg.memoryDbPath ?? join37(process.cwd(), ".kimiflare", "memory.db");
30496
31359
  const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
30497
31360
  initSkillsSchema(skillDb);
30498
31361
  void indexSkills({
@@ -30958,11 +31821,11 @@ var app_exports = {};
30958
31821
  __export(app_exports, {
30959
31822
  renderApp: () => renderApp
30960
31823
  });
30961
- import React24, { useState as useState28, useRef as useRef7, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
30962
- import { Box as Box41, Text as Text42, useApp, useInput as useInput20, render } from "ink";
31824
+ import React25, { useState as useState29, useRef as useRef7, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
31825
+ import { Box as Box42, Text as Text43, useApp, useInput as useInput21, render } from "ink";
30963
31826
  import { existsSync as existsSync8 } from "fs";
30964
- import { join as join37 } from "path";
30965
- import { jsx as jsx43, jsxs as jsxs41 } from "react/jsx-runtime";
31827
+ import { join as join38 } from "path";
31828
+ import { jsx as jsx44, jsxs as jsxs42 } from "react/jsx-runtime";
30966
31829
  function App({
30967
31830
  initialCfg,
30968
31831
  initialUpdateResult,
@@ -30970,14 +31833,14 @@ function App({
30970
31833
  initialLspProjectPath
30971
31834
  }) {
30972
31835
  const { exit } = useApp();
30973
- const [cfg, setCfg] = useState28(initialCfg);
31836
+ const [cfg, setCfg] = useState29(initialCfg);
30974
31837
  const modelContextLimit = useMemo6(
30975
31838
  () => cfg ? getModelOrInfer(cfg.model).contextWindow : CONTEXT_LIMIT,
30976
31839
  [cfg?.model]
30977
31840
  );
30978
- const [lspScope, setLspScope] = useState28(initialLspScope);
30979
- const [lspProjectPath, setLspProjectPath] = useState28(initialLspProjectPath);
30980
- const [events, setRawEvents] = useState28([]);
31841
+ const [lspScope, setLspScope] = useState29(initialLspScope);
31842
+ const [lspProjectPath, setLspProjectPath] = useState29(initialLspProjectPath);
31843
+ const [events, setRawEvents] = useState29([]);
30981
31844
  const setEvents = useCallback10(
30982
31845
  (updater) => {
30983
31846
  setRawEvents((prev) => {
@@ -30987,9 +31850,9 @@ function App({
30987
31850
  },
30988
31851
  []
30989
31852
  );
30990
- const [input, setInput] = useState28("");
30991
- const [usage, setUsage] = useState28(null);
30992
- const [sessionUsage, setSessionUsage] = useState28(null);
31853
+ const [input, setInput] = useState29("");
31854
+ const [usage, setUsage] = useState29(null);
31855
+ const [sessionUsage, setSessionUsage] = useState29(null);
30993
31856
  useEffect11(() => {
30994
31857
  const handler = (sid) => {
30995
31858
  if (sessionIdRef.current && sid === sessionIdRef.current) {
@@ -31001,7 +31864,7 @@ function App({
31001
31864
  usageEvents.off("update", handler);
31002
31865
  };
31003
31866
  }, []);
31004
- const [gatewayMeta, setGatewayMeta] = useState28(null);
31867
+ const [gatewayMeta, setGatewayMeta] = useState29(null);
31005
31868
  const turn = useTurnController();
31006
31869
  const {
31007
31870
  busy,
@@ -31029,7 +31892,7 @@ function App({
31029
31892
  endTurn,
31030
31893
  clearTaskTracking
31031
31894
  } = turn;
31032
- const [planOptions, setPlanOptions] = useState28(null);
31895
+ const [planOptions, setPlanOptions] = useState29(null);
31033
31896
  const planOptionsRef = useRef7(null);
31034
31897
  const {
31035
31898
  pending: perm,
@@ -31096,39 +31959,42 @@ function App({
31096
31959
  setShowShellPicker,
31097
31960
  showPlanCompletePicker,
31098
31961
  setShowPlanCompletePicker,
31962
+ showChangelogImagePicker,
31963
+ setShowChangelogImagePicker,
31099
31964
  hasFullscreenModal,
31100
31965
  hasAnyModal
31101
31966
  } = modals;
31102
- const [queue2, setQueue] = useState28([]);
31103
- const [history, setHistory] = useState28([]);
31104
- const [historyIndex, setHistoryIndex] = useState28(-1);
31105
- const [draftInput, setDraftInput] = useState28("");
31106
- const [mode, setMode] = useState28("edit");
31967
+ const [queue2, setQueue] = useState29([]);
31968
+ const [history, setHistory] = useState29([]);
31969
+ const [historyIndex, setHistoryIndex] = useState29(-1);
31970
+ const [draftInput, setDraftInput] = useState29("");
31971
+ const [mode, setMode] = useState29("edit");
31107
31972
  useEffect11(() => {
31108
31973
  if (mode === "multi-agent-experimental" && cfg && !cfg.workerEndpoint && !cfg.remoteWorkerUrl && !showMultiAgentModal) {
31109
31974
  setShowMultiAgentModal(true);
31110
31975
  }
31111
31976
  }, [mode, cfg?.workerEndpoint, cfg?.remoteWorkerUrl]);
31112
- const [codeMode, setCodeMode] = useState28(false);
31977
+ const [codeMode, setCodeMode] = useState29(false);
31113
31978
  const filePickerEnabled = initialCfg?.filePicker ?? true;
31114
- const [effort, setEffort] = useState28(
31979
+ const [effort, setEffort] = useState29(
31115
31980
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
31116
31981
  );
31117
- const [selectedRemoteSession, setSelectedRemoteSession] = useState28(null);
31118
- const [verbose, setVerbose] = useState28(false);
31119
- const [hasUpdate, setHasUpdate] = useState28(initialUpdateResult?.hasUpdate ?? false);
31120
- const [latestVersion, setLatestVersion] = useState28(initialUpdateResult?.latestVersion ?? null);
31121
- const [theme, setTheme] = useState28(resolveTheme(initialCfg?.theme));
31122
- const [originalTheme, setOriginalTheme] = useState28(null);
31123
- const [skillsActive, setSkillsActive] = useState28(0);
31124
- const [memoryRecalled, setMemoryRecalled] = useState28(false);
31125
- const [intentTier, setIntentTier] = useState28(null);
31126
- const [kimiMdStale, setKimiMdStale] = useState28(false);
31127
- const [gitBranch, setGitBranch] = useState28(null);
31128
- const [lastSessionTopic, setLastSessionTopic] = useState28(null);
31129
- const [activeWorkers, setActiveWorkers] = useState28([]);
31130
- const [isSynthesizing, setIsSynthesizing] = useState28(false);
31131
- const [coordinatorNarration, setCoordinatorNarration] = useState28("");
31982
+ const [selectedRemoteSession, setSelectedRemoteSession] = useState29(null);
31983
+ const [verbose, setVerbose] = useState29(false);
31984
+ const [hasUpdate, setHasUpdate] = useState29(initialUpdateResult?.hasUpdate ?? false);
31985
+ const [latestVersion, setLatestVersion] = useState29(initialUpdateResult?.latestVersion ?? null);
31986
+ const [theme, setTheme] = useState29(resolveTheme(initialCfg?.theme));
31987
+ const [originalTheme, setOriginalTheme] = useState29(null);
31988
+ const [skillsActive, setSkillsActive] = useState29(0);
31989
+ const [memoryRecalled, setMemoryRecalled] = useState29(false);
31990
+ const [intentTier, setIntentTier] = useState29(null);
31991
+ const [kimiMdStale, setKimiMdStale] = useState29(false);
31992
+ const [gitBranch, setGitBranch] = useState29(null);
31993
+ const [lastSessionTopic, setLastSessionTopic] = useState29(null);
31994
+ const [activeWorkers, setActiveWorkers] = useState29([]);
31995
+ const [isSynthesizing, setIsSynthesizing] = useState29(false);
31996
+ const [coordinatorNarration, setCoordinatorNarration] = useState29("");
31997
+ const [changelogImageRepo, setChangelogImageRepo] = useState29(null);
31132
31998
  useEffect11(() => {
31133
31999
  setGitBranch(detectGitBranch());
31134
32000
  }, []);
@@ -31178,8 +32044,8 @@ ${wcagWarnings.join("\n")}` }
31178
32044
  cancelled = true;
31179
32045
  };
31180
32046
  }, []);
31181
- const [cursorOffset, setCursorOffset] = useState28(0);
31182
- const [customCommandsVersion, setCustomCommandsVersion] = useState28(0);
32047
+ const [cursorOffset, setCursorOffset] = useState29(0);
32048
+ const [customCommandsVersion, setCustomCommandsVersion] = useState29(0);
31183
32049
  const cacheStableRef = useRef7(initialCfg?.cacheStablePrompts !== false);
31184
32050
  const messagesRef = useRef7(
31185
32051
  makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
@@ -31203,6 +32069,7 @@ ${wcagWarnings.join("\n")}` }
31203
32069
  const compiledContextRef = useRef7(initialCfg?.compiledContext === true);
31204
32070
  const updateNudgedRef = useRef7(false);
31205
32071
  const compactSuggestedRef = useRef7(false);
32072
+ const freshSuggestedRef = useRef7(false);
31206
32073
  const mcpManagerRef = useRef7(new McpManager());
31207
32074
  const mcpToolsRef = useRef7([]);
31208
32075
  const mcpInitRef = useRef7(false);
@@ -31223,6 +32090,7 @@ ${wcagWarnings.join("\n")}` }
31223
32090
  const sessionPlanRef = useRef7(null);
31224
32091
  const sessionMgr = useSessionManager({
31225
32092
  cfg,
32093
+ mode,
31226
32094
  messagesRef,
31227
32095
  sessionStateRef,
31228
32096
  artifactStoreRef,
@@ -31258,7 +32126,7 @@ ${wcagWarnings.join("\n")}` }
31258
32126
  const customCommandsRef = useRef7([]);
31259
32127
  const recentFilesRef = useRef7(/* @__PURE__ */ new Map());
31260
32128
  const MAX_RECENT_FILES = 10;
31261
- const allSlashCommands = React24.useMemo(() => {
32129
+ const allSlashCommands = React25.useMemo(() => {
31262
32130
  const customs = customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({
31263
32131
  name: c.name,
31264
32132
  description: c.description ?? "",
@@ -31624,7 +32492,7 @@ ${wcagWarnings.join("\n")}` }
31624
32492
  [cfg]
31625
32493
  );
31626
32494
  const interruptDepsRef = useRef7(null);
31627
- useInput20((inputChar, key) => {
32495
+ useInput21((inputChar, key) => {
31628
32496
  if (key.ctrl && inputChar === "c") {
31629
32497
  logger.info("input:ctrl+c", {
31630
32498
  busy: busyRef.current,
@@ -31894,62 +32762,6 @@ ${wcagWarnings.join("\n")}` }
31894
32762
  },
31895
32763
  [mkKey, setShowUiPicker]
31896
32764
  );
31897
- const handlePlanCompletePick = useCallback10(
31898
- (picked) => {
31899
- setShowPlanCompletePicker(false);
31900
- if (!picked || picked === "continue") return;
31901
- const plan = sessionPlanRef.current ?? distillSessionPlan(messagesRef.current);
31902
- if (!plan) {
31903
- setEvents((e) => [
31904
- ...e,
31905
- { kind: "error", key: mkKey(), text: "No plan found to start fresh with." }
31906
- ]);
31907
- setMode(picked);
31908
- return;
31909
- }
31910
- const clipResult = writeToClipboard(plan);
31911
- if (cacheStableRef.current && messagesRef.current.length >= 2) {
31912
- messagesRef.current = [messagesRef.current[0], messagesRef.current[1]];
31913
- } else {
31914
- messagesRef.current = [messagesRef.current[0]];
31915
- }
31916
- resetSession();
31917
- executorRef.current.clearArtifacts();
31918
- if (flushTimeoutRef.current) {
31919
- clearTimeout(flushTimeoutRef.current);
31920
- flushTimeoutRef.current = null;
31921
- }
31922
- pendingTextRef.current.clear();
31923
- activeAsstIdRef.current = null;
31924
- pendingToolCallsRef.current.clear();
31925
- usageRef.current = null;
31926
- turnCounterRef.current = 0;
31927
- setEvents([]);
31928
- setUsage(null);
31929
- setSessionUsage(null);
31930
- gatewayMetaRef.current = null;
31931
- setGatewayMeta(null);
31932
- clearTaskTracking();
31933
- compactSuggestedRef.current = false;
31934
- updateNudgedRef.current = false;
31935
- sessionPlanRef.current = null;
31936
- setEvents((e) => [
31937
- ...e,
31938
- {
31939
- kind: "info",
31940
- key: mkKey(),
31941
- text: clipResult.success ? `Plan copied to clipboard. Starting fresh session in ${picked} mode with plan only\u2026` : `Clipboard unavailable. Starting fresh session in ${picked} mode with plan only\u2026`
31942
- }
31943
- ]);
31944
- if (!clipResult.success) {
31945
- setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "--- Plan ---\n" + plan }]);
31946
- }
31947
- setMode(picked);
31948
- modeRef.current = picked;
31949
- submitRef.current(plan);
31950
- },
31951
- [mkKey, setShowPlanCompletePicker, setMode, setEvents, setUsage, setSessionUsage, setGatewayMeta, clearTaskTracking, resetSession, submitRef]
31952
- );
31953
32765
  const handleModelPick = useCallback10(
31954
32766
  (picked) => {
31955
32767
  setShowModelPicker(false);
@@ -32124,6 +32936,10 @@ ${wcagWarnings.join("\n")}` }
32124
32936
  setShowGatewayPicker,
32125
32937
  setShowSkillsPicker,
32126
32938
  setShowShellPicker,
32939
+ setShowChangelogImagePicker,
32940
+ setChangelogImageRepo,
32941
+ setTasks: turn.setTasks,
32942
+ setTasksStartedAt: turn.setTasksStartedAt,
32127
32943
  lspScope,
32128
32944
  lspProjectPath,
32129
32945
  resetSession,
@@ -32154,6 +32970,7 @@ ${wcagWarnings.join("\n")}` }
32154
32970
  sessionIdRef,
32155
32971
  compactSuggestedRef,
32156
32972
  updateNudgedRef,
32973
+ freshSuggestedRef,
32157
32974
  memoryManagerRef,
32158
32975
  artifactStoreRef,
32159
32976
  sessionStateRef,
@@ -32188,12 +33005,16 @@ ${wcagWarnings.join("\n")}` }
32188
33005
  setShowGatewayPicker,
32189
33006
  setShowSkillsPicker,
32190
33007
  setShowShellPicker,
33008
+ setShowChangelogImagePicker,
33009
+ setChangelogImageRepo,
32191
33010
  setShowLspWizard,
32192
33011
  setShowRemoteDashboard,
32193
33012
  setShowCommandList,
32194
33013
  setCommandWizard,
32195
33014
  setCommandPicker,
32196
33015
  turn.setShowReasoning,
33016
+ turn.setTasks,
33017
+ turn.setTasksStartedAt,
32197
33018
  resetSession,
32198
33019
  clearTaskTracking,
32199
33020
  openResumePicker,
@@ -32203,8 +33024,39 @@ ${wcagWarnings.join("\n")}` }
32203
33024
  initLsp2,
32204
33025
  ensureSessionId
32205
33026
  ]);
33027
+ const handlePlanCompletePick = useCallback10(
33028
+ (picked) => {
33029
+ setShowPlanCompletePicker(false);
33030
+ if (!picked || picked === "continue") return;
33031
+ const plan = sessionPlanRef.current ?? distillSessionPlan(messagesRef.current);
33032
+ if (!plan) {
33033
+ setEvents((e) => [
33034
+ ...e,
33035
+ { kind: "error", key: mkKey(), text: "No plan found to start fresh with." }
33036
+ ]);
33037
+ setMode(picked);
33038
+ return;
33039
+ }
33040
+ const clipResult = executeFreshStart(buildSlashContext(), plan, picked);
33041
+ setEvents((e) => [
33042
+ ...e,
33043
+ {
33044
+ kind: "info",
33045
+ key: mkKey(),
33046
+ text: clipResult.success ? `Plan copied to clipboard. Starting fresh session in ${picked} mode with plan only\u2026` : `Clipboard unavailable. Starting fresh session in ${picked} mode with plan only\u2026`
33047
+ }
33048
+ ]);
33049
+ if (!clipResult.success) {
33050
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "--- Plan ---\n" + plan }]);
33051
+ }
33052
+ setMode(picked);
33053
+ modeRef.current = picked;
33054
+ submitRef.current(plan);
33055
+ },
33056
+ [mkKey, setShowPlanCompletePicker, setMode, setEvents, submitRef, buildSlashContext]
33057
+ );
32206
33058
  const handleSlash = useCallback10(
32207
- (cmd) => dispatchSlashCommand(buildSlashContext(), cmd),
33059
+ async (cmd) => dispatchSlashCommand(buildSlashContext(), cmd),
32208
33060
  [buildSlashContext]
32209
33061
  );
32210
33062
  const handleCommandSave2 = useCallback10(
@@ -32257,7 +33109,7 @@ ${wcagWarnings.join("\n")}` }
32257
33109
  if (trimmed.startsWith("/")) {
32258
33110
  const head = trimmed.split(/\s+/)[0].toLowerCase();
32259
33111
  const selfManaged = ["/compact", "/init"].includes(head);
32260
- if (handleSlash(trimmed)) {
33112
+ if (await handleSlash(trimmed)) {
32261
33113
  if (!selfManaged) {
32262
33114
  endTurn();
32263
33115
  }
@@ -32376,12 +33228,68 @@ ${wcagWarnings.join("\n")}` }
32376
33228
  }
32377
33229
  }
32378
33230
  turnCounterRef.current += 1;
32379
- if (turnCounterRef.current % 15 === 0 && existsSync8(join37(process.cwd(), "KIMI.md")) && !kimiMdStale) {
33231
+ if (turnCounterRef.current % 15 === 0 && existsSync8(join38(process.cwd(), "KIMI.md")) && !kimiMdStale) {
32380
33232
  setEvents((e) => [
32381
33233
  ...e,
32382
33234
  { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
32383
33235
  ]);
32384
33236
  }
33237
+ const autoFreshThreshold = cfg?.autoFreshSuggestionTurns ?? DEFAULT_AUTO_FRESH_SUGGESTION_TURNS;
33238
+ if (autoFreshThreshold > 0 && turnCounterRef.current >= autoFreshThreshold && (modeRef.current === "auto" || modeRef.current === "edit" || modeRef.current === "multi-agent-experimental") && !freshSuggestedRef.current) {
33239
+ freshSuggestedRef.current = true;
33240
+ if (cfg?.autoFreshEnabled) {
33241
+ void (async () => {
33242
+ try {
33243
+ const turnIndex = messagesRef.current.length;
33244
+ if (turnIndex > 0) {
33245
+ const cp = {
33246
+ id: `cp_prefresh_${Date.now()}`,
33247
+ label: "pre-fresh auto-save",
33248
+ turnIndex,
33249
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
33250
+ sessionState: compiledContextRef.current ? sessionStateRef.current : void 0,
33251
+ artifactStore: serializeArtifactStore(artifactStoreRef.current)
33252
+ };
33253
+ ensureSessionId();
33254
+ const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
33255
+ const filePath = join38(sessionsDir3(), `${sessionIdRef.current}.json`);
33256
+ await addCheckpoint(filePath, cp);
33257
+ }
33258
+ const summary = await generateContinuationSummary({
33259
+ messages: messagesRef.current,
33260
+ mode: modeRef.current,
33261
+ accountId: cfg.accountId,
33262
+ apiToken: cfg.apiToken,
33263
+ model: cfg.plumbingModel ?? "@cf/moonshotai/kimi-k2.5",
33264
+ gateway: gatewayFromConfig(cfg),
33265
+ memoryManager: memoryManagerRef.current,
33266
+ memoryEnabled: cfg.memoryEnabled
33267
+ });
33268
+ if (summary) {
33269
+ executeFreshStart(buildSlashContext(), summary);
33270
+ setEvents((e) => [
33271
+ ...e,
33272
+ { kind: "info", key: mkKey(), text: "Auto-fresh triggered: starting new session with continuation context\u2026" }
33273
+ ]);
33274
+ }
33275
+ } catch (e) {
33276
+ setEvents((es) => [
33277
+ ...es,
33278
+ { kind: "error", key: mkKey(), text: `Auto-fresh failed: ${e.message}` }
33279
+ ]);
33280
+ }
33281
+ })();
33282
+ } else {
33283
+ setEvents((e) => [
33284
+ ...e,
33285
+ {
33286
+ kind: "info",
33287
+ key: mkKey(),
33288
+ text: `Session has run for ${turnCounterRef.current} turns. The model may slow down with accumulated context. Run /fresh to continue with a summarized state, or /compact to compress history.`
33289
+ }
33290
+ ]);
33291
+ }
33292
+ }
32385
33293
  gatewayMetaRef.current = null;
32386
33294
  setGatewayMeta(null);
32387
33295
  setIntentTier(classification.tier);
@@ -32703,7 +33611,7 @@ ${conflicts.join("\n")}` }
32703
33611
  lspManagerRef.current.notifyChange(path, content2);
32704
33612
  } else {
32705
33613
  void import("fs/promises").then(
32706
- ({ readFile: readFile22 }) => readFile22(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
33614
+ ({ readFile: readFile23 }) => readFile23(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
32707
33615
  })
32708
33616
  );
32709
33617
  }
@@ -32921,7 +33829,7 @@ ${conflicts.join("\n")}` }
32921
33829
  });
32922
33830
  }, [usage, modelContextLimit, busy, runCompact2]);
32923
33831
  if (!cfg) {
32924
- return /* @__PURE__ */ jsx43(ThemeProvider, { theme, children: /* @__PURE__ */ jsx43(
33832
+ return /* @__PURE__ */ jsx44(ThemeProvider, { theme, children: /* @__PURE__ */ jsx44(
32925
33833
  Onboarding,
32926
33834
  {
32927
33835
  onCancel: () => exit(),
@@ -32936,7 +33844,7 @@ ${conflicts.join("\n")}` }
32936
33844
  ) });
32937
33845
  }
32938
33846
  if (planOptions !== null) {
32939
- return /* @__PURE__ */ jsx43(ThemeProvider, { theme, children: /* @__PURE__ */ jsx43(Box41, { flexDirection: "column", children: /* @__PURE__ */ jsx43(
33847
+ return /* @__PURE__ */ jsx44(ThemeProvider, { theme, children: /* @__PURE__ */ jsx44(Box42, { flexDirection: "column", children: /* @__PURE__ */ jsx44(
32940
33848
  PlanOptionsPicker,
32941
33849
  {
32942
33850
  options: planOptions,
@@ -32966,7 +33874,7 @@ ${conflicts.join("\n")}` }
32966
33874
  ) }) });
32967
33875
  }
32968
33876
  if (checkpointSession !== null) {
32969
- return /* @__PURE__ */ jsx43(ThemeProvider, { theme, children: /* @__PURE__ */ jsx43(Box41, { flexDirection: "column", children: /* @__PURE__ */ jsx43(
33877
+ return /* @__PURE__ */ jsx44(ThemeProvider, { theme, children: /* @__PURE__ */ jsx44(Box42, { flexDirection: "column", children: /* @__PURE__ */ jsx44(
32970
33878
  CheckpointPicker,
32971
33879
  {
32972
33880
  session: checkpointSession,
@@ -32976,10 +33884,10 @@ ${conflicts.join("\n")}` }
32976
33884
  ) }) });
32977
33885
  }
32978
33886
  if (resumeSessions !== null) {
32979
- return /* @__PURE__ */ jsx43(ThemeProvider, { theme, children: /* @__PURE__ */ jsx43(Box41, { flexDirection: "column", children: /* @__PURE__ */ jsx43(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick }) }) });
33887
+ return /* @__PURE__ */ jsx44(ThemeProvider, { theme, children: /* @__PURE__ */ jsx44(Box42, { flexDirection: "column", children: /* @__PURE__ */ jsx44(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick }) }) });
32980
33888
  }
32981
33889
  if (hasFullscreenModal) {
32982
- return /* @__PURE__ */ jsx43(
33890
+ return /* @__PURE__ */ jsx44(
32983
33891
  ModalHost,
32984
33892
  {
32985
33893
  modals,
@@ -32990,7 +33898,7 @@ ${conflicts.join("\n")}` }
32990
33898
  onCommandDelete: handleCommandDelete2,
32991
33899
  lspServers: cfg?.lspServers ?? {},
32992
33900
  lspScope,
32993
- hasProjectDir: existsSync8(join37(process.cwd(), ".kimiflare")),
33901
+ hasProjectDir: existsSync8(join38(process.cwd(), ".kimiflare")),
32994
33902
  onLspSave: handleLspSave2,
32995
33903
  themes: themeList(),
32996
33904
  onPickTheme: handleThemePick,
@@ -33018,7 +33926,7 @@ ${conflicts.join("\n")}` }
33018
33926
  selectedRemoteSession,
33019
33927
  onSelectRemoteSession: setSelectedRemoteSession,
33020
33928
  onCancelRemoteSession: handleRemoteCancel2,
33021
- onInboxOpen: openBrowser2,
33929
+ onInboxOpen: openBrowser,
33022
33930
  multiAgentSettings: cfg ? {
33023
33931
  multiAgentEnabled: cfg.multiAgentEnabled,
33024
33932
  workerEndpoint: cfg.workerEndpoint,
@@ -33118,14 +34026,80 @@ ${conflicts.join("\n")}` }
33118
34026
  }
33119
34027
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `Type /skills ${action} <name> to ${action} a skill.` }]);
33120
34028
  },
33121
- onSkillsDone: () => setShowSkillsPicker(false)
34029
+ onSkillsDone: () => setShowSkillsPicker(false),
34030
+ changelogImageRepo,
34031
+ onChangelogImageGenerate: (owner, repo, days) => {
34032
+ setShowChangelogImagePicker(false);
34033
+ const asstId = mkAssistantId();
34034
+ setEvents((e) => [
34035
+ ...e,
34036
+ {
34037
+ kind: "assistant",
34038
+ key: `asst_${asstId}`,
34039
+ id: asstId,
34040
+ text: `Generating changelog image for ${owner}/${repo} (last ${days} day${days === 1 ? "" : "s"})\u2026`,
34041
+ reasoning: "",
34042
+ streaming: true
34043
+ }
34044
+ ]);
34045
+ const taskList = [
34046
+ { id: "fetch-prs", title: "Fetch merged PRs", status: "pending" },
34047
+ { id: "fetch-release", title: "Fetch latest release", status: "pending" },
34048
+ { id: "summarize", title: "Summarize with LLM", status: "pending" },
34049
+ { id: "render", title: "Render changelog image", status: "pending" },
34050
+ { id: "save", title: "Save PNG file", status: "pending" }
34051
+ ];
34052
+ turn.setTasks(taskList);
34053
+ turn.setTasksStartedAt(Date.now());
34054
+ const updateTask = (id, status) => {
34055
+ turn.setTasks((prev) => prev.map((t) => t.id === id ? { ...t, status } : t));
34056
+ };
34057
+ setTimeout(() => {
34058
+ void (async () => {
34059
+ try {
34060
+ updateTask("fetch-prs", "in_progress");
34061
+ updateTask("fetch-release", "in_progress");
34062
+ const { changelogImageTool: changelogImageTool2 } = await Promise.resolve().then(() => (init_changelog_image(), changelog_image_exports));
34063
+ const result = await changelogImageTool2.run({ owner, repo, days }, {
34064
+ cwd: process.cwd(),
34065
+ githubToken: cfg?.githubOAuthToken,
34066
+ accountId: cfg?.accountId,
34067
+ apiToken: cfg?.apiToken,
34068
+ model: cfg?.model,
34069
+ gateway: gatewayFromConfig(cfg)
34070
+ });
34071
+ updateTask("fetch-prs", "completed");
34072
+ updateTask("fetch-release", "completed");
34073
+ updateTask("summarize", "completed");
34074
+ updateTask("render", "completed");
34075
+ updateTask("save", "completed");
34076
+ const text = typeof result === "string" ? result : result.content;
34077
+ setEvents(
34078
+ (e) => e.map(
34079
+ (ev) => ev.kind === "assistant" && ev.id === asstId ? { ...ev, text, streaming: false } : ev
34080
+ )
34081
+ );
34082
+ } catch (err) {
34083
+ const msg = `changelog-image failed: ${err instanceof Error ? err.message : String(err)}`;
34084
+ setEvents(
34085
+ (e) => e.map(
34086
+ (ev) => ev.kind === "assistant" && ev.id === asstId ? { ...ev, text: msg, streaming: false } : ev
34087
+ )
34088
+ );
34089
+ } finally {
34090
+ turn.setTasksStartedAt(null);
34091
+ }
34092
+ })();
34093
+ }, 0);
34094
+ },
34095
+ onChangelogImageCancel: () => setShowChangelogImagePicker(false)
33122
34096
  }
33123
34097
  );
33124
34098
  }
33125
34099
  const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
33126
- return /* @__PURE__ */ jsx43(ThemeProvider, { theme, children: /* @__PURE__ */ jsxs41(Box41, { flexDirection: "column", children: [
33127
- !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx43(Welcome, {}) : /* @__PURE__ */ jsx43(ChatView, { events, showReasoning, verbose, intentTier: intentTier ?? void 0 }),
33128
- perm ? /* @__PURE__ */ jsx43(
34100
+ return /* @__PURE__ */ jsx44(ThemeProvider, { theme, children: /* @__PURE__ */ jsxs42(Box42, { flexDirection: "column", children: [
34101
+ !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx44(Welcome, {}) : /* @__PURE__ */ jsx44(ChatView, { events, showReasoning, verbose, intentTier: intentTier ?? void 0 }),
34102
+ perm ? /* @__PURE__ */ jsx44(
33129
34103
  PermissionModal,
33130
34104
  {
33131
34105
  tool: perm.tool,
@@ -33135,7 +34109,7 @@ ${conflicts.join("\n")}` }
33135
34109
  submitRef.current(text);
33136
34110
  }
33137
34111
  }
33138
- ) : limitModal || loopModal ? /* @__PURE__ */ jsx43(
34112
+ ) : limitModal || loopModal ? /* @__PURE__ */ jsx44(
33139
34113
  ModalOverlay,
33140
34114
  {
33141
34115
  modals,
@@ -33146,9 +34120,9 @@ ${conflicts.join("\n")}` }
33146
34120
  loopResolveRef.current = null;
33147
34121
  }
33148
34122
  }
33149
- ) : showPlanCompletePicker ? /* @__PURE__ */ jsx43(PlanCompletePicker, { onPick: handlePlanCompletePick }) : /* @__PURE__ */ jsxs41(Box41, { flexDirection: "column", marginTop: 1, children: [
33150
- (activeWorkers.length > 0 || coordinatorNarration) && /* @__PURE__ */ jsx43(WorkerList, { workers: activeWorkers, isSynthesizing, narration: coordinatorNarration }),
33151
- tasks.length > 0 && /* @__PURE__ */ jsx43(
34123
+ ) : showPlanCompletePicker ? /* @__PURE__ */ jsx44(PlanCompletePicker, { onPick: handlePlanCompletePick }) : /* @__PURE__ */ jsxs42(Box42, { flexDirection: "column", marginTop: 1, children: [
34124
+ (activeWorkers.length > 0 || coordinatorNarration) && /* @__PURE__ */ jsx44(WorkerList, { workers: activeWorkers, isSynthesizing, narration: coordinatorNarration }),
34125
+ tasks.length > 0 && /* @__PURE__ */ jsx44(
33152
34126
  TaskList,
33153
34127
  {
33154
34128
  tasks,
@@ -33156,11 +34130,11 @@ ${conflicts.join("\n")}` }
33156
34130
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
33157
34131
  }
33158
34132
  ),
33159
- queue2.length > 0 && /* @__PURE__ */ jsx43(Box41, { flexDirection: "column", marginBottom: 1, children: queue2.map((q, i) => /* @__PURE__ */ jsxs41(Text42, { color: theme.info.color, dimColor: theme.info.dim, children: [
34133
+ queue2.length > 0 && /* @__PURE__ */ jsx44(Box42, { flexDirection: "column", marginBottom: 1, children: queue2.map((q, i) => /* @__PURE__ */ jsxs42(Text43, { color: theme.info.color, dimColor: theme.info.dim, children: [
33160
34134
  "\u23F3 ",
33161
34135
  q.display
33162
34136
  ] }, `queue_${i}`)) }),
33163
- /* @__PURE__ */ jsx43(
34137
+ /* @__PURE__ */ jsx44(
33164
34138
  StatusBar,
33165
34139
  {
33166
34140
  usage,
@@ -33182,7 +34156,7 @@ ${conflicts.join("\n")}` }
33182
34156
  intentTier: intentTier ?? void 0
33183
34157
  }
33184
34158
  ),
33185
- picker.active?.kind === "file" && /* @__PURE__ */ jsx43(
34159
+ picker.active?.kind === "file" && /* @__PURE__ */ jsx44(
33186
34160
  FilePicker,
33187
34161
  {
33188
34162
  items: picker.fileItems,
@@ -33191,7 +34165,7 @@ ${conflicts.join("\n")}` }
33191
34165
  recentFiles: new Set(recentFilesRef.current.keys())
33192
34166
  }
33193
34167
  ),
33194
- picker.active?.kind === "slash" && /* @__PURE__ */ jsx43(
34168
+ picker.active?.kind === "slash" && /* @__PURE__ */ jsx44(
33195
34169
  SlashPicker,
33196
34170
  {
33197
34171
  items: picker.slashItems,
@@ -33199,9 +34173,9 @@ ${conflicts.join("\n")}` }
33199
34173
  query: picker.query
33200
34174
  }
33201
34175
  ),
33202
- /* @__PURE__ */ jsxs41(Box41, { marginTop: 1, children: [
33203
- /* @__PURE__ */ jsx43(Text42, { color: theme.prompt ?? theme.accent, children: "\u203A " }),
33204
- /* @__PURE__ */ jsx43(
34176
+ /* @__PURE__ */ jsxs42(Box42, { marginTop: 1, children: [
34177
+ /* @__PURE__ */ jsx44(Text43, { color: theme.prompt ?? theme.accent, children: "\u203A " }),
34178
+ /* @__PURE__ */ jsx44(
33205
34179
  CustomTextInput,
33206
34180
  {
33207
34181
  value: input,
@@ -33258,7 +34232,7 @@ ${conflicts.join("\n")}` }
33258
34232
  }
33259
34233
  async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath = null) {
33260
34234
  const instance = render(
33261
- /* @__PURE__ */ jsx43(
34235
+ /* @__PURE__ */ jsx44(
33262
34236
  App,
33263
34237
  {
33264
34238
  initialCfg: cfg,
@@ -33337,12 +34311,12 @@ var init_app = __esm({
33337
34311
  init_use_turn_controller();
33338
34312
  init_input_handlers();
33339
34313
  init_slash_commands();
34314
+ init_continuation_summary();
33340
34315
  init_run_init();
33341
34316
  init_run_startup_tasks();
33342
34317
  init_manager_init();
33343
34318
  init_run_compact();
33344
34319
  init_distill();
33345
- init_clipboard();
33346
34320
  init_command_handlers();
33347
34321
  init_app_helpers();
33348
34322
  }