kimiflare 0.79.0 → 0.80.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
@@ -45,6 +45,7 @@ __export(config_exports, {
45
45
  EFFORTS: () => EFFORTS,
46
46
  configPath: () => configPath,
47
47
  loadConfig: () => loadConfig,
48
+ resolveWorkerBudgetUsd: () => resolveWorkerBudgetUsd,
48
49
  saveConfig: () => saveConfig
49
50
  });
50
51
  import { readFile, mkdir, writeFile, chmod } from "fs/promises";
@@ -157,6 +158,8 @@ async function loadConfig() {
157
158
  const envProviderKeys = readProviderKeysEnv();
158
159
  const envUnifiedBilling = readBooleanEnv("KIMIFLARE_UNIFIED_BILLING");
159
160
  const envMultiAgentEnabled = readBooleanEnv("KIMIFLARE_MULTI_AGENT_ENABLED");
161
+ const envWorkerPreReadFiles = process.env.KIMIFLARE_WORKER_PRE_READ_FILES ? process.env.KIMIFLARE_WORKER_PRE_READ_FILES.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
162
+ const envWorkerPreReadMaxChars = readNumberEnv("KIMIFLARE_WORKER_PRE_READ_MAX_CHARS");
160
163
  if (envAccount && envToken) {
161
164
  return {
162
165
  accountId: envAccount,
@@ -196,11 +199,16 @@ async function loadConfig() {
196
199
  unifiedBilling: envUnifiedBilling ?? persisted?.unifiedBilling,
197
200
  workerEndpoint: process.env.KIMIFLARE_WORKER_ENDPOINT,
198
201
  workerBudgetUsd: readNumberEnv("KIMIFLARE_WORKER_BUDGET_USD"),
202
+ workerBudgetMaxUsd: readNumberEnv("KIMIFLARE_WORKER_BUDGET_MAX_USD"),
199
203
  workerMaxParallel: readNumberEnv("KIMIFLARE_WORKER_MAX_PARALLEL"),
200
204
  workerTimeoutMs: readNumberEnv("KIMIFLARE_WORKER_TIMEOUT_MS"),
201
205
  multiAgentEnabled: envMultiAgentEnabled,
202
206
  workerApiKey: process.env.KIMIFLARE_WORKER_API_KEY,
203
- autoExecute: readBooleanEnv("KIMIFLARE_AUTO_EXECUTE")
207
+ autoExecute: readBooleanEnv("KIMIFLARE_AUTO_EXECUTE"),
208
+ workerShallowClone: readBooleanEnv("KIMIFLARE_WORKER_SHALLOW_CLONE") ?? true,
209
+ workerRepoCache: readBooleanEnv("KIMIFLARE_WORKER_REPO_CACHE") ?? true,
210
+ workerPreReadFiles: envWorkerPreReadFiles ?? persisted?.workerPreReadFiles,
211
+ workerPreReadMaxChars: envWorkerPreReadMaxChars ?? persisted?.workerPreReadMaxChars
204
212
  };
205
213
  }
206
214
  if (persisted) {
@@ -243,16 +251,38 @@ async function loadConfig() {
243
251
  unifiedBilling: envUnifiedBilling ?? parsed.unifiedBilling,
244
252
  workerEndpoint: process.env.KIMIFLARE_WORKER_ENDPOINT ?? parsed.workerEndpoint,
245
253
  workerBudgetUsd: parsed.workerBudgetUsd,
254
+ workerBudgetMaxUsd: parsed.workerBudgetMaxUsd,
246
255
  workerMaxParallel: parsed.workerMaxParallel,
247
256
  workerTimeoutMs: parsed.workerTimeoutMs,
248
257
  multiAgentEnabled: envMultiAgentEnabled ?? parsed.multiAgentEnabled,
249
258
  workerApiKey: process.env.KIMIFLARE_WORKER_API_KEY ?? parsed.workerApiKey,
250
- autoExecute: parsed.autoExecute
259
+ autoExecute: parsed.autoExecute,
260
+ workerShallowClone: readBooleanEnv("KIMIFLARE_WORKER_SHALLOW_CLONE") ?? parsed.workerShallowClone ?? true,
261
+ workerRepoCache: readBooleanEnv("KIMIFLARE_WORKER_REPO_CACHE") ?? parsed.workerRepoCache ?? true,
262
+ workerPreReadFiles: envWorkerPreReadFiles ?? parsed.workerPreReadFiles,
263
+ workerPreReadMaxChars: envWorkerPreReadMaxChars ?? parsed.workerPreReadMaxChars
251
264
  };
252
265
  }
253
266
  }
254
267
  return null;
255
268
  }
269
+ function resolveWorkerBudgetUsd(cfg) {
270
+ const DEFAULT_WORKER_BUDGET_USD = 1;
271
+ const HARD_CEILING = cfg?.workerBudgetMaxUsd ?? 5;
272
+ const raw = cfg?.workerBudgetUsd ?? DEFAULT_WORKER_BUDGET_USD;
273
+ if (raw <= 0) {
274
+ throw new Error(
275
+ `Invalid workerBudgetUsd (${raw}). Must be > 0. Set via /multi-agent or KIMIFLARE_WORKER_BUDGET_USD.`
276
+ );
277
+ }
278
+ if (raw > HARD_CEILING) {
279
+ console.warn(
280
+ `kimiflare: workerBudgetUsd ${raw} exceeds hard ceiling ${HARD_CEILING}; capping to ${HARD_CEILING}. Raise the ceiling with KIMIFLARE_WORKER_BUDGET_MAX_USD if you really need more.`
281
+ );
282
+ return HARD_CEILING;
283
+ }
284
+ return raw;
285
+ }
256
286
  async function saveConfig(cfg) {
257
287
  const p = configPath();
258
288
  await mkdir(join(p, ".."), { recursive: true });
@@ -411,8 +441,8 @@ async function flushAndCloseForTesting() {
411
441
  const s = currentStream;
412
442
  currentStream = null;
413
443
  currentDate = null;
414
- await new Promise((resolve4) => {
415
- s.end(() => resolve4());
444
+ await new Promise((resolve5) => {
445
+ s.end(() => resolve5());
416
446
  });
417
447
  }
418
448
  function isLogSinkEnabled() {
@@ -1402,11 +1432,11 @@ function extractCloudflareError(parsed, rawText) {
1402
1432
  return null;
1403
1433
  }
1404
1434
  function sleep(ms, signal) {
1405
- return new Promise((resolve4, reject) => {
1435
+ return new Promise((resolve5, reject) => {
1406
1436
  if (signal?.aborted) return reject(new DOMException("aborted", "AbortError"));
1407
1437
  const t = setTimeout(() => {
1408
1438
  signal?.removeEventListener("abort", onAbort);
1409
- resolve4();
1439
+ resolve5();
1410
1440
  }, ms);
1411
1441
  const onAbort = () => {
1412
1442
  clearTimeout(t);
@@ -2108,8 +2138,8 @@ var require_node_gyp_build = __commonJS({
2108
2138
  var abi = process.versions.modules;
2109
2139
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
2110
2140
  var arch = process.env.npm_config_arch || os2.arch();
2111
- var platform7 = process.env.npm_config_platform || os2.platform();
2112
- var libc = process.env.LIBC || (isAlpine(platform7) ? "musl" : "glibc");
2141
+ var platform8 = process.env.npm_config_platform || os2.platform();
2142
+ var libc = process.env.LIBC || (isAlpine(platform8) ? "musl" : "glibc");
2113
2143
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
2114
2144
  var uv = (process.versions.uv || "").split(".")[0];
2115
2145
  module.exports = load;
@@ -2129,12 +2159,12 @@ var require_node_gyp_build = __commonJS({
2129
2159
  var debug = getFirst(path.join(dir, "build/Debug"), matchBuild);
2130
2160
  if (debug) return debug;
2131
2161
  }
2132
- var prebuild = resolve4(dir);
2162
+ var prebuild = resolve5(dir);
2133
2163
  if (prebuild) return prebuild;
2134
- var nearby = resolve4(path.dirname(process.execPath));
2164
+ var nearby = resolve5(path.dirname(process.execPath));
2135
2165
  if (nearby) return nearby;
2136
2166
  var target = [
2137
- "platform=" + platform7,
2167
+ "platform=" + platform8,
2138
2168
  "arch=" + arch,
2139
2169
  "runtime=" + runtime,
2140
2170
  "abi=" + abi,
@@ -2147,9 +2177,9 @@ var require_node_gyp_build = __commonJS({
2147
2177
  // eslint-disable-line
2148
2178
  ].filter(Boolean).join(" ");
2149
2179
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
2150
- function resolve4(dir2) {
2180
+ function resolve5(dir2) {
2151
2181
  var tuples = readdirSync2(path.join(dir2, "prebuilds")).map(parseTuple);
2152
- var tuple = tuples.filter(matchTuple(platform7, arch)).sort(compareTuples)[0];
2182
+ var tuple = tuples.filter(matchTuple(platform8, arch)).sort(compareTuples)[0];
2153
2183
  if (!tuple) return;
2154
2184
  var prebuilds = path.join(dir2, "prebuilds", tuple.name);
2155
2185
  var parsed = readdirSync2(prebuilds).map(parseTags);
@@ -2175,17 +2205,17 @@ var require_node_gyp_build = __commonJS({
2175
2205
  function parseTuple(name) {
2176
2206
  var arr = name.split("-");
2177
2207
  if (arr.length !== 2) return;
2178
- var platform8 = arr[0];
2208
+ var platform9 = arr[0];
2179
2209
  var architectures = arr[1].split("+");
2180
- if (!platform8) return;
2210
+ if (!platform9) return;
2181
2211
  if (!architectures.length) return;
2182
2212
  if (!architectures.every(Boolean)) return;
2183
- return { name, platform: platform8, architectures };
2213
+ return { name, platform: platform9, architectures };
2184
2214
  }
2185
- function matchTuple(platform8, arch2) {
2215
+ function matchTuple(platform9, arch2) {
2186
2216
  return function(tuple) {
2187
2217
  if (tuple == null) return false;
2188
- if (tuple.platform !== platform8) return false;
2218
+ if (tuple.platform !== platform9) return false;
2189
2219
  return tuple.architectures.includes(arch2);
2190
2220
  };
2191
2221
  }
@@ -2253,8 +2283,8 @@ var require_node_gyp_build = __commonJS({
2253
2283
  if (process.env.ELECTRON_RUN_AS_NODE) return true;
2254
2284
  return typeof window !== "undefined" && window.process && window.process.type === "renderer";
2255
2285
  }
2256
- function isAlpine(platform8) {
2257
- return platform8 === "linux" && fs.existsSync("/etc/alpine-release");
2286
+ function isAlpine(platform9) {
2287
+ return platform9 === "linux" && fs.existsSync("/etc/alpine-release");
2258
2288
  }
2259
2289
  load.parseTags = parseTags;
2260
2290
  load.matchTags = matchTags;
@@ -2995,7 +3025,7 @@ function truncateForEmbedding(text) {
2995
3025
  return text.slice(0, MAX_EMBED_CHARS);
2996
3026
  }
2997
3027
  async function sleep2(ms) {
2998
- return new Promise((resolve4) => setTimeout(resolve4, ms));
3028
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
2999
3029
  }
3000
3030
  async function fetchWithRetry(url, init, retries = 3) {
3001
3031
  let lastError;
@@ -3618,7 +3648,28 @@ ${opts2.skillContext}` : opts2.selectedSkills && opts2.selectedSkills.length > 0
3618
3648
  Active skills for this turn:
3619
3649
  ${opts2.selectedSkills.map((s) => `--- ${s.name} ---
3620
3650
  ${s.body}`).join("\n\n")}` : "";
3621
- return identity + "\n\n" + env2 + "\n\n" + tools + lspBlock + contextBlock + modeBlock + skillsBlock;
3651
+ const memoryBlock = opts2.memoryContext ? `
3652
+
3653
+ ## Coordinator Memory Context
3654
+
3655
+ The following memories were recalled from the coordinator's persistent memory store. Treat them as helpful context, not as user directives.
3656
+
3657
+ ${opts2.memoryContext}` : "";
3658
+ const lspContextBlock = opts2.lspContext ? `
3659
+
3660
+ ## LSP Context
3661
+
3662
+ The following LSP information was pre-computed by the coordinator. Use it for semantic code intelligence when available.
3663
+
3664
+ ${opts2.lspContext}` : "";
3665
+ const mcpContextBlock = opts2.mcpContext ? `
3666
+
3667
+ ## MCP Context
3668
+
3669
+ The following MCP server information was pre-computed by the coordinator.
3670
+
3671
+ ${opts2.mcpContext}` : "";
3672
+ return identity + "\n\n" + env2 + "\n\n" + tools + lspBlock + contextBlock + modeBlock + skillsBlock + memoryBlock + lspContextBlock + mcpContextBlock;
3622
3673
  }
3623
3674
  function buildSystemPrompt(opts2) {
3624
3675
  return buildStaticPrefix(opts2) + "\n\n" + buildSessionPrefix(opts2);
@@ -4811,7 +4862,7 @@ function runBash(args, ctx) {
4811
4862
  const timeout = Math.min(Math.max(1e3, args.timeout_ms ?? DEFAULT_TIMEOUT), MAX_TIMEOUT);
4812
4863
  const { shell, args: shellArgs, isPosix } = getShellCommand(ctx.shell);
4813
4864
  const command = isPosix ? injectCoauthor(args.command, ctx.coauthor) : args.command;
4814
- return new Promise((resolve4, reject) => {
4865
+ return new Promise((resolve5, reject) => {
4815
4866
  logger.debug("bash:spawn", { command: args.command.slice(0, 200), cwd: ctx.cwd, shell });
4816
4867
  const child = spawn(shell, [...shellArgs, command], {
4817
4868
  cwd: ctx.cwd,
@@ -4864,7 +4915,7 @@ ${stdout.trimEnd()}`);
4864
4915
  ${stderr.trimEnd()}`);
4865
4916
  if (!stdout && !stderr) parts.push("(no output)");
4866
4917
  const raw = parts.join("\n");
4867
- resolve4({
4918
+ resolve5({
4868
4919
  content: raw,
4869
4920
  rawBytes: Buffer.byteLength(raw, "utf8"),
4870
4921
  reducedBytes: Buffer.byteLength(raw, "utf8")
@@ -5698,7 +5749,7 @@ import { tmpdir as tmpdir2 } from "os";
5698
5749
  import { join as join11, dirname as dirname5 } from "path";
5699
5750
  async function autoScroll(page) {
5700
5751
  await page.evaluate(async () => {
5701
- await new Promise((resolve4) => {
5752
+ await new Promise((resolve5) => {
5702
5753
  let totalHeight = 0;
5703
5754
  const distance = 300;
5704
5755
  const timer2 = setInterval(() => {
@@ -5707,12 +5758,12 @@ async function autoScroll(page) {
5707
5758
  totalHeight += distance;
5708
5759
  if (totalHeight >= scrollHeight) {
5709
5760
  clearInterval(timer2);
5710
- resolve4();
5761
+ resolve5();
5711
5762
  }
5712
5763
  }, 100);
5713
5764
  setTimeout(() => {
5714
5765
  clearInterval(timer2);
5715
- resolve4();
5766
+ resolve5();
5716
5767
  }, 1e4);
5717
5768
  });
5718
5769
  });
@@ -6061,13 +6112,13 @@ function readNumberEnv2(name) {
6061
6112
  const parsed = parseInt(raw, 10);
6062
6113
  return Number.isNaN(parsed) ? void 0 : parsed;
6063
6114
  }
6064
- var DEFAULT_WORKER_TIMEOUT_MS, DEFAULT_WORKER_BUDGET_USD, spawnWorkerTool;
6115
+ var DEFAULT_WORKER_TIMEOUT_MS, spawnWorkerTool;
6065
6116
  var init_spawn_worker = __esm({
6066
6117
  "src/tools/spawn-worker.ts"() {
6067
6118
  "use strict";
6068
6119
  init_logger();
6120
+ init_config();
6069
6121
  DEFAULT_WORKER_TIMEOUT_MS = 3e5;
6070
- DEFAULT_WORKER_BUDGET_USD = 1;
6071
6122
  spawnWorkerTool = {
6072
6123
  name: "spawn_worker",
6073
6124
  description: [
@@ -6147,7 +6198,8 @@ var init_spawn_worker = __esm({
6147
6198
  }
6148
6199
  const apiKey = process.env.KIMIFLARE_WORKER_API_KEY;
6149
6200
  const timeoutMs = readNumberEnv2("KIMIFLARE_WORKER_TIMEOUT_MS") ?? DEFAULT_WORKER_TIMEOUT_MS;
6150
- const budgetUsd = args.budget?.maxCostUsd ?? readNumberEnv2("KIMIFLARE_WORKER_BUDGET_USD") ?? DEFAULT_WORKER_BUDGET_USD;
6201
+ const cfg = await loadConfig().catch(() => null);
6202
+ const budgetUsd = resolveWorkerBudgetUsd(cfg);
6151
6203
  const payload = {
6152
6204
  mode: args.mode,
6153
6205
  task: args.task,
@@ -6166,14 +6218,14 @@ var init_spawn_worker = __esm({
6166
6218
  logger.info("spawn_worker:starting", { mode: args.mode, endpoint, taskPreview: args.task.slice(0, 100) });
6167
6219
  try {
6168
6220
  const result = await callWorkerEndpoint(endpoint, apiKey, payload, ctx.signal, timeoutMs);
6169
- if (result.status !== "completed") {
6221
+ if (result.status !== "completed" && result.status !== "budget_exhausted") {
6170
6222
  const msg = `Worker ${result.workerId} ${result.status}: ${result.error ?? "unknown error"}`;
6171
6223
  const bytes2 = Buffer.byteLength(msg, "utf8");
6172
6224
  return { content: msg, rawBytes: bytes2, reducedBytes: bytes2 };
6173
6225
  }
6174
6226
  const lines = [
6175
- `Worker ${result.workerId} completed.`,
6176
- `Cost: $${result.costUsd.toFixed(2)} \xB7 Tokens: ${result.tokensUsed.toLocaleString()}`,
6227
+ `Worker ${result.workerId} ${result.status === "budget_exhausted" ? "budget_exhausted (partial result)" : "completed"}.`,
6228
+ `Cost: ${result.costUsd.toFixed(2)} \xB7 Tokens: ${result.tokensUsed.toLocaleString()}`,
6177
6229
  "",
6178
6230
  "## Findings",
6179
6231
  ...result.findings.map(
@@ -6183,6 +6235,12 @@ var init_spawn_worker = __esm({
6183
6235
  "## Recommendations",
6184
6236
  ...result.recommendations.map((r) => `- ${r}`)
6185
6237
  ];
6238
+ if (result.status === "budget_exhausted") {
6239
+ lines.push(
6240
+ "",
6241
+ "> \u26A0\uFE0F This worker hit its budget ceiling and returned partial results. Consider re-running with a higher budget if findings are incomplete."
6242
+ );
6243
+ }
6186
6244
  if (result.filesRead.length > 0) {
6187
6245
  lines.push("", "## Files Read", ...result.filesRead.map((f) => `- ${f}`));
6188
6246
  }
@@ -8276,7 +8334,7 @@ __export(tui_auth_exports, {
8276
8334
  authGitHubForTui: () => authGitHubForTui
8277
8335
  });
8278
8336
  function sleep3(ms) {
8279
- return new Promise((resolve4) => setTimeout(resolve4, ms));
8337
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
8280
8338
  }
8281
8339
  async function* authGitHubForTui() {
8282
8340
  yield { message: "Starting GitHub OAuth device flow..." };
@@ -8381,6 +8439,7 @@ __export(db_exports, {
8381
8439
  getMemoryById: () => getMemoryById,
8382
8440
  getMemoryDb: () => getMemoryDb,
8383
8441
  getMemoryStats: () => getMemoryStats,
8442
+ getTopRelatedFiles: () => getTopRelatedFiles,
8384
8443
  insertMemories: () => insertMemories,
8385
8444
  insertMemory: () => insertMemory,
8386
8445
  listMemoriesForVectorSearch: () => listMemoriesForVectorSearch,
@@ -8762,6 +8821,25 @@ function getMemoryById(db, id) {
8762
8821
  const row = db.prepare("SELECT * FROM memories WHERE id = ?").get(id);
8763
8822
  return row ? rowToMemory(row) : null;
8764
8823
  }
8824
+ function getTopRelatedFiles(db, repoPath, limit = 10) {
8825
+ const rows = db.prepare(
8826
+ `SELECT related_files, importance FROM memories
8827
+ WHERE repo_path = ? AND related_files != '[]'
8828
+ AND forgotten = 0 AND superseded_by IS NULL
8829
+ ORDER BY accessed_at DESC
8830
+ LIMIT 200`
8831
+ ).all(repoPath);
8832
+ const scores = /* @__PURE__ */ new Map();
8833
+ for (const row of rows) {
8834
+ const files = JSON.parse(row.related_files);
8835
+ for (const file of files) {
8836
+ if (!file) continue;
8837
+ scores.set(file, (scores.get(file) ?? 0) + row.importance);
8838
+ }
8839
+ }
8840
+ const sorted = [...scores.entries()].sort((a, b) => b[1] - a[1]);
8841
+ return sorted.slice(0, limit).map(([file]) => file);
8842
+ }
8765
8843
  function countHighSignalMemoriesSince(db, repoPath, since) {
8766
8844
  const row = db.prepare(
8767
8845
  `SELECT COUNT(*) as count FROM memories
@@ -8785,6 +8863,11 @@ var init_db2 = __esm({
8785
8863
  });
8786
8864
 
8787
8865
  // src/memory/retrieval.ts
8866
+ var retrieval_exports = {};
8867
+ __export(retrieval_exports, {
8868
+ formatRecalledMemories: () => formatRecalledMemories,
8869
+ retrieveMemories: () => retrieveMemories
8870
+ });
8788
8871
  function normalizeFtsRank(rank) {
8789
8872
  const clamped = Math.max(0, Math.min(10, rank));
8790
8873
  return 1 - clamped / 10;
@@ -8912,6 +8995,15 @@ async function retrieveMemories(opts2) {
8912
8995
  }
8913
8996
  return results;
8914
8997
  }
8998
+ function formatRecalledMemories(results) {
8999
+ if (results.length === 0) return "";
9000
+ const lines = results.map((r) => {
9001
+ const files = r.memory.relatedFiles.length > 0 ? ` [${r.memory.relatedFiles.join(", ")}]` : "";
9002
+ return `- [${r.memory.category}] ${r.memory.content}${files}`;
9003
+ });
9004
+ return `Recalled memories from previous sessions:
9005
+ ${lines.join("\n")}`;
9006
+ }
8915
9007
  var RRF_K;
8916
9008
  var init_retrieval = __esm({
8917
9009
  "src/memory/retrieval.ts"() {
@@ -9390,7 +9482,7 @@ var init_connection = __esm({
9390
9482
  if (this.child) {
9391
9483
  throw new Error("LSP connection already started");
9392
9484
  }
9393
- return new Promise((resolve4, reject) => {
9485
+ return new Promise((resolve5, reject) => {
9394
9486
  const abortController = new AbortController();
9395
9487
  const spawnTimer = setTimeout(() => {
9396
9488
  abortController.abort();
@@ -9429,7 +9521,7 @@ var init_connection = __esm({
9429
9521
  if (child.pid) {
9430
9522
  clearTimeout(spawnTimer);
9431
9523
  this.child = child;
9432
- resolve4();
9524
+ resolve5();
9433
9525
  }
9434
9526
  });
9435
9527
  } catch (err) {
@@ -9444,12 +9536,12 @@ var init_connection = __esm({
9444
9536
  }
9445
9537
  const id = this.nextId++;
9446
9538
  const msg = { jsonrpc: "2.0", id, method, params };
9447
- return new Promise((resolve4, reject) => {
9539
+ return new Promise((resolve5, reject) => {
9448
9540
  const timer2 = setTimeout(() => {
9449
9541
  this.pending.delete(id);
9450
9542
  reject(toolTimeoutError(`LSP request '${method}'`, this.requestTimeoutMs));
9451
9543
  }, this.requestTimeoutMs);
9452
- const pending = { resolve: resolve4, reject, signal, timer: timer2 };
9544
+ const pending = { resolve: resolve5, reject, signal, timer: timer2 };
9453
9545
  this.pending.set(id, pending);
9454
9546
  if (signal) {
9455
9547
  const onAbort = () => {
@@ -9989,6 +10081,25 @@ var init_manager2 = __esm({
9989
10081
  }
9990
10082
  return count;
9991
10083
  }
10084
+ /** Export a compact LSP context summary for multi-agent workers.
10085
+ * Returns workspace symbols from the first running server. */
10086
+ async exportContext(_rootPath) {
10087
+ for (const [, server] of this.servers) {
10088
+ if (server.state !== "running") continue;
10089
+ try {
10090
+ const symbols = await server.client.workspaceSymbol("");
10091
+ if (!symbols || symbols.length === 0) continue;
10092
+ const lines = [`LSP server: ${server.id} (${server.rootUri})`];
10093
+ for (const sym of symbols.slice(0, 50)) {
10094
+ const loc = "location" in sym && sym.location ? `${sym.location.uri}:${(sym.location.range?.start?.line ?? 0) + 1}` : "";
10095
+ lines.push(`- ${sym.name} (${sym.kind})${loc ? ` \u2192 ${loc}` : ""}`);
10096
+ }
10097
+ return lines.join("\n");
10098
+ } catch {
10099
+ }
10100
+ }
10101
+ return "";
10102
+ }
9992
10103
  };
9993
10104
  }
9994
10105
  });
@@ -11071,7 +11182,7 @@ var init_runner = __esm({
11071
11182
  init_logger();
11072
11183
  DEFAULT_TIMEOUT_MS = 3e4;
11073
11184
  STREAM_CAP_BYTES = 4 * 1024;
11074
- defaultSpawn = (command, payloadJson, env2, cwd, timeoutMs, signal) => new Promise((resolve4) => {
11185
+ defaultSpawn = (command, payloadJson, env2, cwd, timeoutMs, signal) => new Promise((resolve5) => {
11075
11186
  const child = spawn3(command, {
11076
11187
  shell: true,
11077
11188
  cwd,
@@ -11086,7 +11197,7 @@ var init_runner = __esm({
11086
11197
  settled = true;
11087
11198
  clearTimeout(timer2);
11088
11199
  signal?.removeEventListener("abort", onAbort);
11089
- resolve4({ exitCode, stdout, stderr, timedOut });
11200
+ resolve5({ exitCode, stdout, stderr, timedOut });
11090
11201
  };
11091
11202
  const onAbort = () => {
11092
11203
  child.kill("SIGTERM");
@@ -11366,8 +11477,8 @@ var init_session = __esm({
11366
11477
  const parts = [{ type: "text", text }];
11367
11478
  for (const img of options.images) {
11368
11479
  if ("path" in img) {
11369
- const { readFile: readFile21 } = await import("fs/promises");
11370
- const data = await readFile21(img.path, "base64");
11480
+ const { readFile: readFile22 } = await import("fs/promises");
11481
+ const data = await readFile22(img.path, "base64");
11371
11482
  const mimeType = img.path.endsWith(".png") ? "image/png" : img.path.endsWith(".jpg") || img.path.endsWith(".jpeg") ? "image/jpeg" : "image/webp";
11372
11483
  parts.push({ type: "image_url", image_url: { url: `data:${mimeType};base64,${data}` } });
11373
11484
  } else {
@@ -11559,12 +11670,12 @@ var init_session = __esm({
11559
11670
  toolName: req.tool.name,
11560
11671
  args: req.args
11561
11672
  });
11562
- const decision = await new Promise((resolve4) => {
11563
- this.permissionResolvers.set(requestId, resolve4);
11673
+ const decision = await new Promise((resolve5) => {
11674
+ this.permissionResolvers.set(requestId, resolve5);
11564
11675
  setTimeout(() => {
11565
11676
  if (this.permissionResolvers.has(requestId)) {
11566
11677
  this.permissionResolvers.delete(requestId);
11567
- resolve4("deny");
11678
+ resolve5("deny");
11568
11679
  }
11569
11680
  }, 3e5);
11570
11681
  });
@@ -11613,7 +11724,7 @@ var init_session = __esm({
11613
11724
  this.lspManager?.notifyChange(path, content);
11614
11725
  } else {
11615
11726
  void import("fs/promises").then(
11616
- ({ readFile: readFile21 }) => readFile21(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
11727
+ ({ readFile: readFile22 }) => readFile22(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
11617
11728
  })
11618
11729
  ).catch(() => {
11619
11730
  });
@@ -11895,15 +12006,109 @@ var init_repo_info = __esm({
11895
12006
  });
11896
12007
 
11897
12008
  // src/agent/supervisor.ts
11898
- function decomposePrompt(prompt, context) {
11899
- const items = extractListItems(prompt);
11900
- if (items.length >= 2) {
11901
- return items.slice(0, 4).map((task) => ({ mode: "plan", task, context }));
12009
+ import { readdir as readdir5, readFile as readFile13, stat as stat5 } from "fs/promises";
12010
+ import { createHash as createHash2 } from "crypto";
12011
+ import { resolve as resolve4, join as join22 } from "path";
12012
+ async function preReadFilesForWorkers(files, repoRoot, maxChars = DEFAULT_PRE_READ_MAX_CHARS) {
12013
+ const results = [];
12014
+ const filesRead = [];
12015
+ let chars = 0;
12016
+ for (const file of files) {
12017
+ if (chars >= maxChars) break;
12018
+ const path = resolve4(repoRoot, file);
12019
+ try {
12020
+ const s = await stat5(path);
12021
+ if (!s.isFile()) continue;
12022
+ const raw = await readFile13(path, "utf8");
12023
+ const remaining = maxChars - chars;
12024
+ const content = raw.length > remaining ? raw.slice(0, remaining) + "\n\u2026 (truncated)" : raw;
12025
+ results.push(`--- ${file} ---
12026
+ ${content}`);
12027
+ filesRead.push(file);
12028
+ chars += content.length;
12029
+ } catch {
12030
+ }
12031
+ }
12032
+ if (results.length === 0) {
12033
+ return { text: "", filesRead: [], chars: 0 };
12034
+ }
12035
+ return {
12036
+ text: `The following files were pre-read by the coordinator and are available for reference.
12037
+ Check here before using the read tool to avoid redundant file reads.
12038
+
12039
+ ${results.join("\n\n")}`,
12040
+ filesRead,
12041
+ chars
12042
+ };
12043
+ }
12044
+ function getPreReadFilesFromMemory(cfg, repoRoot, limit = 10) {
12045
+ if (!cfg.memoryEnabled) return [];
12046
+ const dbPath = cfg.memoryDbPath ?? join22(repoRoot, ".kimiflare", "memory.db");
12047
+ try {
12048
+ const db = openMemoryDb(dbPath);
12049
+ const files = getTopRelatedFiles(db, repoRoot, limit);
12050
+ return files;
12051
+ } catch {
12052
+ return [];
12053
+ }
12054
+ }
12055
+ function cacheKey2(prompt, context, strategy) {
12056
+ return createHash2("sha256").update(`${prompt}\0${context}\0${strategy}`).digest("hex");
12057
+ }
12058
+ function getCached(key) {
12059
+ return decompositionCache.get(key);
12060
+ }
12061
+ function setCached(key, value) {
12062
+ if (decompositionCache.size >= MAX_CACHE_ENTRIES) {
12063
+ const first = decompositionCache.keys().next().value;
12064
+ if (first !== void 0) decompositionCache.delete(first);
12065
+ }
12066
+ decompositionCache.set(key, value);
12067
+ }
12068
+ async function getFileTreeSnapshot(cwd) {
12069
+ const IGNORED = /* @__PURE__ */ new Set([
12070
+ "node_modules",
12071
+ ".git",
12072
+ "dist",
12073
+ "build",
12074
+ "out",
12075
+ "target",
12076
+ ".next",
12077
+ ".nuxt",
12078
+ ".astro",
12079
+ "coverage",
12080
+ ".coverage",
12081
+ "__pycache__",
12082
+ ".venv",
12083
+ "venv",
12084
+ ".tox",
12085
+ ".idea",
12086
+ ".vscode",
12087
+ ".DS_Store"
12088
+ ]);
12089
+ try {
12090
+ const entries = await readdir5(cwd, { withFileTypes: true });
12091
+ const dirs = [];
12092
+ const files = [];
12093
+ for (const e of entries) {
12094
+ if (e.name.startsWith(".") && !e.name.startsWith(".github") && !e.name.startsWith(".config")) {
12095
+ if (!e.isDirectory()) continue;
12096
+ }
12097
+ if (IGNORED.has(e.name)) continue;
12098
+ if (e.isDirectory()) dirs.push(`${e.name}/`);
12099
+ else files.push(e.name);
12100
+ }
12101
+ dirs.sort();
12102
+ files.sort();
12103
+ const lines = [...dirs, ...files];
12104
+ if (lines.length === 0) return "(empty directory)";
12105
+ if (lines.length > 40) {
12106
+ return lines.slice(0, 40).join("\n") + "\n\u2026 (truncated)";
12107
+ }
12108
+ return lines.join("\n");
12109
+ } catch {
12110
+ return "(unable to read directory)";
11902
12111
  }
11903
- return [
11904
- { mode: "plan", task: `Research overview and best practices for: ${prompt}`, context },
11905
- { mode: "plan", task: `Investigate implementation details, trade-offs, and risks for: ${prompt}`, context }
11906
- ];
11907
12112
  }
11908
12113
  function extractListItems(prompt) {
11909
12114
  const numbered = [...prompt.matchAll(/(?:^|\n)\s*\d+[.)]\s+([^\n]+)/g)].map((m) => m[1]?.trim() ?? "");
@@ -11912,19 +12117,131 @@ function extractListItems(prompt) {
11912
12117
  if (bulleted.length >= 2) return bulleted.filter((s) => s.length > 0);
11913
12118
  return [];
11914
12119
  }
11915
- var TurnSupervisor;
12120
+ async function decomposeWithLlm(prompt, context, fileTree, cfg) {
12121
+ const model = cfg.decompositionModel ?? "@cf/moonshotai/kimi-k2.5";
12122
+ const accountId = cfg.accountId;
12123
+ const apiToken = cfg.apiToken;
12124
+ if (!accountId || !apiToken) {
12125
+ logger.warn("decompose:missing_creds", { reason: "no accountId or apiToken" });
12126
+ return null;
12127
+ }
12128
+ const gateway = cfg.aiGatewayId ? {
12129
+ id: cfg.aiGatewayId,
12130
+ cacheTtl: cfg.aiGatewayCacheTtl,
12131
+ skipCache: cfg.aiGatewaySkipCache,
12132
+ metadata: { feature: "decomposition", ...cfg.aiGatewayMetadata ?? {} }
12133
+ } : void 0;
12134
+ const userContent = [
12135
+ `User request: ${prompt}`,
12136
+ context ? `Additional context: ${context}` : "",
12137
+ `Project file tree (top-level):
12138
+ ${fileTree}`
12139
+ ].filter(Boolean).join("\n\n");
12140
+ const messages = [
12141
+ { role: "system", content: DECOMPOSITION_SYSTEM },
12142
+ { role: "user", content: userContent }
12143
+ ];
12144
+ try {
12145
+ let text = "";
12146
+ const events = runKimi({
12147
+ accountId,
12148
+ apiToken,
12149
+ model,
12150
+ messages,
12151
+ temperature: 0.1,
12152
+ maxCompletionTokens: 2048,
12153
+ reasoningEffort: "low",
12154
+ gateway
12155
+ });
12156
+ for await (const ev of events) {
12157
+ if (ev.type === "text") text += ev.delta;
12158
+ }
12159
+ const cleaned = text.replace(/```(?:json)?\s*/gi, "").replace(/```\s*$/gi, "").trim();
12160
+ const parsed = JSON.parse(cleaned);
12161
+ const rawTasks = parsed.tasks;
12162
+ if (!Array.isArray(rawTasks) || rawTasks.length === 0) {
12163
+ logger.warn("decompose:invalid_tasks", { rawTasks });
12164
+ return null;
12165
+ }
12166
+ const tasks = rawTasks.map((t) => typeof t === "string" ? t.trim() : "").filter((t) => t.length > 0);
12167
+ if (tasks.length < 2) {
12168
+ logger.warn("decompose:too_few_tasks", { count: tasks.length });
12169
+ return null;
12170
+ }
12171
+ const unique = [];
12172
+ for (const t of tasks) {
12173
+ const lower = t.toLowerCase();
12174
+ if (!unique.some((u) => u.toLowerCase() === lower || lower.includes(u.toLowerCase()) || u.toLowerCase().includes(lower))) {
12175
+ unique.push(t);
12176
+ }
12177
+ }
12178
+ const capped = unique.slice(0, 4);
12179
+ logger.debug("decompose:llm_success", { taskCount: capped.length, reasoning: parsed.reasoning });
12180
+ return capped.map((task) => ({ mode: "plan", task, context }));
12181
+ } catch (err) {
12182
+ const msg = err instanceof Error ? err.message : String(err);
12183
+ logger.warn("decompose:llm_failed", { error: msg });
12184
+ return null;
12185
+ }
12186
+ }
12187
+ function fallbackDecomposition(prompt, context) {
12188
+ return [
12189
+ { mode: "plan", task: `Research overview and best practices for: ${prompt}`, context },
12190
+ { mode: "plan", task: `Investigate implementation details, trade-offs, and risks for: ${prompt}`, context }
12191
+ ];
12192
+ }
12193
+ async function decomposePrompt(prompt, context, opts2) {
12194
+ const items = extractListItems(prompt);
12195
+ if (items.length >= 2) {
12196
+ return items.slice(0, 4).map((task) => ({ mode: "plan", task, context }));
12197
+ }
12198
+ const strategy = opts2?.cfg?.decompositionStrategy ?? "llm";
12199
+ const key = cacheKey2(prompt, context, strategy);
12200
+ const cached = getCached(key);
12201
+ if (cached) {
12202
+ logger.debug("decompose:cache_hit");
12203
+ return cached;
12204
+ }
12205
+ if (strategy === "regex") {
12206
+ const result2 = fallbackDecomposition(prompt, context);
12207
+ setCached(key, result2);
12208
+ return result2;
12209
+ }
12210
+ if (opts2?.cfg) {
12211
+ const cwd = opts2.cwd ?? process.cwd();
12212
+ const fileTree = await getFileTreeSnapshot(cwd);
12213
+ const llmResult = await decomposeWithLlm(prompt, context, fileTree, opts2.cfg);
12214
+ if (llmResult) {
12215
+ setCached(key, llmResult);
12216
+ return llmResult;
12217
+ }
12218
+ }
12219
+ const result = fallbackDecomposition(prompt, context);
12220
+ setCached(key, result);
12221
+ return result;
12222
+ }
12223
+ var DEFAULT_PRE_READ_MAX_CHARS, TurnSupervisor, decompositionCache, MAX_CACHE_ENTRIES, DECOMPOSITION_SYSTEM;
11916
12224
  var init_supervisor = __esm({
11917
12225
  "src/agent/supervisor.ts"() {
11918
12226
  "use strict";
11919
12227
  init_loop();
11920
12228
  init_logger();
12229
+ init_client();
11921
12230
  init_repo_info();
11922
12231
  init_config();
12232
+ init_db2();
12233
+ DEFAULT_PRE_READ_MAX_CHARS = 5e4;
11923
12234
  TurnSupervisor = class {
11924
12235
  currentTurn = null;
11925
12236
  _phase = "idle";
11926
12237
  _killRequested = false;
11927
12238
  _activeWorkers = /* @__PURE__ */ new Map();
12239
+ /** Coordinator-side MemoryManager for proxying memories to workers */
12240
+ memoryManager = null;
12241
+ /** Coordinator-side LspManager for proxying LSP context to workers */
12242
+ lspManager = null;
12243
+ /** Coordinator-side McpManager for proxying MCP context to workers */
12244
+ mcpManager = null;
11928
12245
  get phase() {
11929
12246
  return this._phase;
11930
12247
  }
@@ -12011,6 +12328,29 @@ var init_supervisor = __esm({
12011
12328
  }
12012
12329
  const userAccountId = cfg.accountId;
12013
12330
  const userApiToken = cfg.apiToken;
12331
+ const proxyMemory = cfg?.workerProxyMemory ?? true;
12332
+ const proxyLsp = cfg?.workerProxyLsp ?? false;
12333
+ const proxyMcp = cfg?.workerProxyMcp ?? false;
12334
+ const memoryManager = this.memoryManager;
12335
+ const lspManager = this.lspManager;
12336
+ const mcpManager = this.mcpManager;
12337
+ let preReadCfg = cfg.workerPreReadFiles;
12338
+ if (!preReadCfg || preReadCfg.length === 0) {
12339
+ const memoryFiles = getPreReadFilesFromMemory(cfg, process.cwd(), 10);
12340
+ if (memoryFiles.length > 0) {
12341
+ preReadCfg = memoryFiles;
12342
+ logger.info("spawnWorkers:pre_read_from_memory", { files: memoryFiles });
12343
+ }
12344
+ }
12345
+ const preReadMax = cfg.workerPreReadMaxChars ?? DEFAULT_PRE_READ_MAX_CHARS;
12346
+ const preRead = preReadCfg && preReadCfg.length > 0 ? await preReadFilesForWorkers(preReadCfg, process.cwd(), preReadMax) : { text: "", filesRead: [], chars: 0 };
12347
+ if (preRead.filesRead.length > 0) {
12348
+ logger.info("spawnWorkers:pre_read", {
12349
+ files: preRead.filesRead,
12350
+ chars: preRead.chars,
12351
+ source: cfg.workerPreReadFiles ? "config" : "memory"
12352
+ });
12353
+ }
12014
12354
  for (const w of workers) {
12015
12355
  const id = `worker-${crypto.randomUUID().slice(0, 8)}`;
12016
12356
  this._activeWorkers.set(id, {
@@ -12019,14 +12359,18 @@ var init_supervisor = __esm({
12019
12359
  task: w.task,
12020
12360
  status: "pending",
12021
12361
  startedAt: Date.now(),
12022
- logs: []
12362
+ logs: [],
12363
+ preReadFiles: preRead.filesRead,
12364
+ preReadChars: preRead.chars
12023
12365
  });
12024
12366
  }
12025
12367
  onUpdate?.(this.activeWorkers);
12026
12368
  const results = [];
12027
12369
  const queue2 = [...workers];
12028
12370
  const activeWorkers = this._activeWorkers;
12029
- async function runBatch(batch) {
12371
+ async function runBatch(batch, batchId) {
12372
+ const shallowClone = cfg?.workerShallowClone ?? true;
12373
+ const repoCache = cfg?.workerRepoCache ?? true;
12030
12374
  await Promise.all(
12031
12375
  batch.map(async (w) => {
12032
12376
  const workerId = [...activeWorkers.entries()].find(
@@ -12036,13 +12380,52 @@ var init_supervisor = __esm({
12036
12380
  const worker = activeWorkers.get(workerId);
12037
12381
  worker.status = "running";
12038
12382
  worker.logs.push(`[coordinator] Starting worker \u2192 POST ${endpoint}/worker`);
12383
+ if (worker.preReadFiles && worker.preReadFiles.length > 0) {
12384
+ worker.logs.push(
12385
+ `[coordinator] Shared cache: ${worker.preReadFiles.length} file(s) pre-read (~${worker.preReadChars?.toLocaleString() ?? "?"} chars)`
12386
+ );
12387
+ }
12039
12388
  onUpdate?.([...activeWorkers.values()]);
12040
12389
  try {
12390
+ const maxCostUsd = resolveWorkerBudgetUsd(cfg);
12391
+ if (maxCostUsd !== (w.budgetUsd ?? 1)) {
12392
+ worker.logs.push(
12393
+ `[coordinator] Budget capped to ${maxCostUsd.toFixed(2)} (was ${(w.budgetUsd ?? 1).toFixed(2)})`
12394
+ );
12395
+ }
12396
+ let memoryContext = w.memoryContext ?? "";
12397
+ let lspContext = w.lspContext ?? "";
12398
+ let mcpContext = w.mcpContext ?? "";
12399
+ if (proxyMemory && memoryManager && !memoryContext) {
12400
+ try {
12401
+ const memories = await memoryManager.recall({ text: w.task, limit: 10 });
12402
+ const { formatRecalledMemories: formatRecalledMemories2 } = await Promise.resolve().then(() => (init_retrieval(), retrieval_exports));
12403
+ memoryContext = formatRecalledMemories2(memories);
12404
+ } catch (err) {
12405
+ logger.warn("supervisor:memory_recall_failed", { task: w.task, error: err.message });
12406
+ }
12407
+ }
12408
+ if (proxyLsp && lspManager && !lspContext) {
12409
+ try {
12410
+ lspContext = await lspManager.exportContext(process.cwd());
12411
+ } catch (err) {
12412
+ logger.warn("supervisor:lsp_export_failed", { error: err.message });
12413
+ }
12414
+ }
12415
+ if (proxyMcp && mcpManager && !mcpContext) {
12416
+ try {
12417
+ mcpContext = mcpManager.exportContext();
12418
+ } catch (err) {
12419
+ logger.warn("supervisor:mcp_export_failed", { error: err.message });
12420
+ }
12421
+ }
12041
12422
  const payload = {
12042
12423
  mode: w.mode,
12043
12424
  task: w.task,
12044
- context: w.context ?? "",
12045
- budget: { maxCostUsd: w.budgetUsd ?? 1 },
12425
+ context: preRead.text ? `${preRead.text}
12426
+
12427
+ ${w.context ?? ""}` : w.context ?? "",
12428
+ budget: { maxCostUsd },
12046
12429
  outputFormat: "structured",
12047
12430
  tools: w.mode === "plan" ? "read-only" : "all",
12048
12431
  model: w.model ?? "@cf/moonshotai/kimi-k2.6",
@@ -12057,6 +12440,15 @@ var init_supervisor = __esm({
12057
12440
  // these are absent (older client + new server).
12058
12441
  userAccountId,
12059
12442
  userApiToken,
12443
+ // Batch-level hints so the Commute worker can share a cloned
12444
+ // repo across workers in the same batch and skip full clones.
12445
+ batchId,
12446
+ shallowClone,
12447
+ repoCache,
12448
+ // Coordinator-side context proxying
12449
+ ...memoryContext ? { memoryContext } : {},
12450
+ ...lspContext ? { lspContext } : {},
12451
+ ...mcpContext ? { mcpContext } : {},
12060
12452
  // Optionally override the in-sandbox kimiflare install so the
12061
12453
  // worker runs pre-release / branch code instead of the image-
12062
12454
  // baked version. e.g. `kimiflare@latest`, `kimiflare@1.2.3`,
@@ -12068,9 +12460,15 @@ var init_supervisor = __esm({
12068
12460
  prBody: w.prBody
12069
12461
  } : {}
12070
12462
  };
12071
- const timeoutMs = parseInt(process.env.KIMIFLARE_WORKER_TIMEOUT_MS ?? "900000", 10);
12463
+ const timeoutMs = cfg?.workerTimeoutMs ?? parseInt(process.env.KIMIFLARE_WORKER_TIMEOUT_MS ?? "900000", 10);
12072
12464
  worker.logs.push(`[coordinator] Sending payload (${JSON.stringify(payload).length} bytes)`);
12073
12465
  worker.logs.push(`[coordinator] Worker will clone ${repo.owner}/${repo.repo} and run kimiflare inside Cloudflare Sandbox`);
12466
+ const optHints = [];
12467
+ if (shallowClone) optHints.push("shallow clone");
12468
+ if (repoCache) optHints.push("repo cache");
12469
+ if (optHints.length > 0) {
12470
+ worker.logs.push(`[coordinator] Optimizations enabled: ${optHints.join(", ")}`);
12471
+ }
12074
12472
  worker.logs.push(`[coordinator] Typical runtime: 1\u20134 min. Timeout: ${Math.round(timeoutMs / 1e3)}s`);
12075
12473
  onUpdate?.([...activeWorkers.values()]);
12076
12474
  const startRes = await fetch(`${endpoint}/worker`, {
@@ -12090,11 +12488,11 @@ var init_supervisor = __esm({
12090
12488
  worker.logs.push(`[coordinator] Worker started (id: ${remoteWorkerId}) \u2014 polling for progress\u2026`);
12091
12489
  onUpdate?.([...activeWorkers.values()]);
12092
12490
  const pollInterval = 3e3;
12093
- const startTime = Date.now();
12491
+ let lastProgressAt = Date.now();
12094
12492
  let lastLogCount = 0;
12095
12493
  let lastStep = "";
12096
12494
  let data;
12097
- while (Date.now() - startTime < timeoutMs) {
12495
+ while (Date.now() - lastProgressAt < timeoutMs) {
12098
12496
  if (signal?.aborted) {
12099
12497
  worker.logs.push(`[coordinator] Cancelling worker (id: ${remoteWorkerId})\u2026`);
12100
12498
  onUpdate?.([...activeWorkers.values()]);
@@ -12138,7 +12536,7 @@ var init_supervisor = __esm({
12138
12536
  for (let i = 0; i < progress.totalSteps; i++) {
12139
12537
  const isCompleted = i < progress.completedSteps.length;
12140
12538
  const isActive = i === progress.stepIndex - 1 && !isCompleted && progress.status === "running";
12141
- const isFailed = progress.status === "failed" && i === progress.stepIndex - 1;
12539
+ const isFailed = (progress.status === "failed" || progress.status === "budget_exhausted") && i === progress.stepIndex - 1;
12142
12540
  allSteps.push({
12143
12541
  label: progress.completedSteps[i] ?? progress.step,
12144
12542
  status: isFailed ? "failed" : isCompleted ? "completed" : isActive ? "active" : "pending"
@@ -12153,18 +12551,20 @@ var init_supervisor = __esm({
12153
12551
  const stepKey = `${progress.stepIndex}:${progress.step}`;
12154
12552
  if (stepKey !== lastStep) {
12155
12553
  lastStep = stepKey;
12554
+ lastProgressAt = Date.now();
12156
12555
  worker.logs.push(`[coordinator] Step ${progress.stepIndex}/${progress.totalSteps}: ${progress.message}`);
12157
12556
  onUpdate?.([...activeWorkers.values()]);
12158
12557
  } else if (newLogs.length > 0) {
12558
+ lastProgressAt = Date.now();
12159
12559
  onUpdate?.([...activeWorkers.values()]);
12160
12560
  }
12161
- if (progress.status === "completed" || progress.status === "failed") {
12561
+ if (progress.status === "completed" || progress.status === "failed" || progress.status === "budget_exhausted") {
12162
12562
  if (progress.result) {
12163
12563
  data = progress.result;
12164
12564
  } else if (progress.error) {
12165
12565
  data = {
12166
12566
  workerId: remoteWorkerId,
12167
- status: "failed",
12567
+ status: progress.status === "budget_exhausted" ? "budget_exhausted" : "failed",
12168
12568
  task: w.task,
12169
12569
  findings: [],
12170
12570
  recommendations: [],
@@ -12182,14 +12582,14 @@ var init_supervisor = __esm({
12182
12582
  if (!data) {
12183
12583
  throw new Error(`Worker timed out after ${Math.round(timeoutMs / 1e3)}s`);
12184
12584
  }
12185
- worker.status = data.status === "completed" ? "completed" : "failed";
12585
+ worker.status = data.status === "completed" ? "completed" : data.status === "budget_exhausted" ? "budget_exhausted" : "failed";
12186
12586
  worker.result = data;
12187
12587
  worker.rawOutput = data.rawOutput;
12188
12588
  worker.reasoning = data.reasoning;
12189
12589
  if (worker.steps && worker.steps.length > 0) {
12190
12590
  for (const s of worker.steps) {
12191
12591
  if (s.status === "pending" || s.status === "active") {
12192
- s.status = worker.status === "completed" ? "completed" : "failed";
12592
+ s.status = worker.status === "completed" ? "completed" : worker.status === "budget_exhausted" ? "failed" : "failed";
12193
12593
  }
12194
12594
  }
12195
12595
  }
@@ -12205,7 +12605,7 @@ var init_supervisor = __esm({
12205
12605
  worker.logs.push(`[coordinator] Worker raw output (${data.rawOutput.length} chars):`);
12206
12606
  worker.logs.push(...data.rawOutput.split("\n").slice(-30));
12207
12607
  }
12208
- if (data.status === "completed") {
12608
+ if (data.status === "completed" || data.status === "budget_exhausted") {
12209
12609
  results.push(data);
12210
12610
  }
12211
12611
  } catch (e) {
@@ -12226,7 +12626,8 @@ var init_supervisor = __esm({
12226
12626
  }
12227
12627
  while (queue2.length > 0) {
12228
12628
  const batch = queue2.splice(0, maxParallel);
12229
- await runBatch(batch);
12629
+ const batchId = `batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
12630
+ await runBatch(batch, batchId);
12230
12631
  }
12231
12632
  return results;
12232
12633
  }
@@ -12282,6 +12683,7 @@ var init_supervisor = __esm({
12282
12683
  resolvedRecommendations.push(recs[0][0]);
12283
12684
  }
12284
12685
  }
12686
+ const budgetExhaustedCount = results.filter((r) => r.status === "budget_exhausted").length;
12285
12687
  const planLines = [
12286
12688
  "# Synthesized Execution Plan",
12287
12689
  "",
@@ -12293,6 +12695,12 @@ var init_supervisor = __esm({
12293
12695
  "## Recommendations",
12294
12696
  ...resolvedRecommendations.map((r) => `- ${r}`)
12295
12697
  ];
12698
+ if (budgetExhaustedCount > 0) {
12699
+ planLines.push(
12700
+ "",
12701
+ `> \u26A0\uFE0F ${budgetExhaustedCount} worker(s) hit their budget ceiling and returned partial results. Findings above may be incomplete. Consider re-running with a higher budget if critical gaps remain.`
12702
+ );
12703
+ }
12296
12704
  if (conflicts.length > 0) {
12297
12705
  planLines.push("", "## Conflicts Resolved", ...conflicts.map((c) => `- ${c}`));
12298
12706
  }
@@ -12316,7 +12724,8 @@ var init_supervisor = __esm({
12316
12724
  `Multi-agent already active (${this._activeWorkers.size} worker(s) in flight). Wait for completion or cancel before starting a new heavy task.`
12317
12725
  );
12318
12726
  }
12319
- const workers = decomposePrompt(prompt, context);
12727
+ const cfg = await loadConfig().catch(() => null);
12728
+ const workers = await decomposePrompt(prompt, context, { cwd: process.cwd(), cfg: cfg ?? void 0 });
12320
12729
  const narrationLines = [
12321
12730
  `Decomposing your request into ${workers.length} parallel research task${workers.length > 1 ? "s" : ""}:`,
12322
12731
  ...workers.map((w, i) => ` ${i + 1}. ${w.task.slice(0, 200)}${w.task.length > 200 ? "\u2026" : ""}`)
@@ -12327,8 +12736,8 @@ var init_supervisor = __esm({
12327
12736
  const results = await this.spawnWorkers(workers, onUpdate, signal);
12328
12737
  onPhaseChange?.("synthesizing");
12329
12738
  const synth = this.synthesizeFindings(results);
12330
- const cfg = await loadConfig().catch(() => null);
12331
- const autoExecute = cfg?.autoExecute ?? /^(1|true|yes|on)$/i.test(process.env.KIMIFLARE_AUTO_EXECUTE ?? "");
12739
+ const cfg2 = await loadConfig().catch(() => null);
12740
+ const autoExecute = cfg2?.autoExecute ?? /^(1|true|yes|on)$/i.test(process.env.KIMIFLARE_AUTO_EXECUTE ?? "");
12332
12741
  if (!autoExecute || synth.recommendations.length === 0) {
12333
12742
  return synth;
12334
12743
  }
@@ -12365,6 +12774,17 @@ var init_supervisor = __esm({
12365
12774
  this._activeWorkers.clear();
12366
12775
  }
12367
12776
  };
12777
+ decompositionCache = /* @__PURE__ */ new Map();
12778
+ MAX_CACHE_ENTRIES = 50;
12779
+ DECOMPOSITION_SYSTEM = `You are a task-decomposition assistant. Given a user's coding request and a snapshot of their project directory, produce 2\u20134 well-scoped, non-overlapping research tasks that can be executed in parallel by independent agents.
12780
+
12781
+ Rules:
12782
+ - Each task must be self-contained and actionable.
12783
+ - Tasks must NOT overlap in scope. If two tasks would investigate the same file or concept, merge them.
12784
+ - Respect file/directory boundaries mentioned in the prompt or visible in the file tree.
12785
+ - Scale task count with perceived complexity: 2 tasks for simple questions, 3\u20134 for broad audits or multi-file changes.
12786
+ - Return ONLY a JSON object with this exact shape (no markdown fences, no extra text):
12787
+ {"tasks":["task 1","task 2",...],"reasoning":"brief explanation of why you split this way"}`;
12368
12788
  }
12369
12789
  });
12370
12790
 
@@ -12477,8 +12897,8 @@ async function runEmitMode(opts2) {
12477
12897
  async function nextFollowUp() {
12478
12898
  if (followUpQueue.length > 0) return followUpQueue.shift();
12479
12899
  if (stdinClosed) return null;
12480
- return new Promise((resolve4) => {
12481
- followUpResolver = resolve4;
12900
+ return new Promise((resolve5) => {
12901
+ followUpResolver = resolve5;
12482
12902
  });
12483
12903
  }
12484
12904
  const cwd = process.cwd();
@@ -12660,8 +13080,8 @@ ${conflicts.join("\n")}`,
12660
13080
  return "allow";
12661
13081
  }
12662
13082
  if (opts2.multiTurn) {
12663
- const choice = await new Promise((resolve4) => {
12664
- pendingPermissions.set(reqId, resolve4);
13083
+ const choice = await new Promise((resolve5) => {
13084
+ pendingPermissions.set(reqId, resolve5);
12665
13085
  });
12666
13086
  if (choice === "allow" || choice === "allow_session") {
12667
13087
  emit("PermissionGranted", { request_id: reqId });
@@ -12764,9 +13184,9 @@ var init_emit_mode = __esm({
12764
13184
 
12765
13185
  // src/remote/deploy-commute.ts
12766
13186
  import { spawn as spawn4 } from "child_process";
12767
- import { mkdtemp, readFile as readFile13, writeFile as writeFile10, rm } from "fs/promises";
13187
+ import { mkdtemp, readFile as readFile14, writeFile as writeFile10, rm } from "fs/promises";
12768
13188
  import { tmpdir as tmpdir3 } from "os";
12769
- import { join as join22 } from "path";
13189
+ import { join as join23 } from "path";
12770
13190
  import { randomBytes as randomBytes2 } from "crypto";
12771
13191
  async function cfApiFetch(accountId, apiToken, path, init) {
12772
13192
  const url = `${CF_API}/accounts/${encodeURIComponent(accountId)}${path}`;
@@ -12836,7 +13256,7 @@ function generateSecret2() {
12836
13256
  return randomBytes2(32).toString("hex");
12837
13257
  }
12838
13258
  function runCmd(cmd, args, opts2 = {}) {
12839
- return new Promise((resolve4) => {
13259
+ return new Promise((resolve5) => {
12840
13260
  const child = spawn4(cmd, args, {
12841
13261
  cwd: opts2.cwd,
12842
13262
  env: { ...process.env, ...opts2.env ?? {} },
@@ -12857,11 +13277,11 @@ function runCmd(cmd, args, opts2 = {}) {
12857
13277
  const timer2 = opts2.timeoutMs ? setTimeout(() => child.kill("SIGKILL"), opts2.timeoutMs) : null;
12858
13278
  child.on("close", (code) => {
12859
13279
  if (timer2) clearTimeout(timer2);
12860
- resolve4({ stdout, stderr, code: code ?? -1 });
13280
+ resolve5({ stdout, stderr, code: code ?? -1 });
12861
13281
  });
12862
13282
  child.on("error", (err) => {
12863
13283
  if (timer2) clearTimeout(timer2);
12864
- resolve4({ stdout, stderr: stderr + String(err), code: -1 });
13284
+ resolve5({ stdout, stderr: stderr + String(err), code: -1 });
12865
13285
  });
12866
13286
  });
12867
13287
  }
@@ -12997,8 +13417,8 @@ ${wranglerInstall.stderr.slice(-600)}`,
12997
13417
  ok: true
12998
13418
  };
12999
13419
  yield { message: "Prerequisites ready", ok: true };
13000
- const tmpRoot = await mkdtemp(join22(tmpdir3(), "kimiflare-commute-"));
13001
- const repoDir = join22(tmpRoot, "kimiflare-commute");
13420
+ const tmpRoot = await mkdtemp(join23(tmpdir3(), "kimiflare-commute-"));
13421
+ const repoDir = join23(tmpRoot, "kimiflare-commute");
13002
13422
  yield { message: `Fetching worker source from GitHub (${COMMUTE_REPO})\u2026` };
13003
13423
  const clone = await runCmd("git", ["clone", "--depth", "1", "--branch", COMMUTE_BRANCH, COMMUTE_REPO, repoDir], { timeoutMs: 6e4 });
13004
13424
  if (clone.code !== 0) {
@@ -13007,8 +13427,8 @@ ${(clone.stderr || clone.stdout).slice(0, 400)}`, error: true };
13007
13427
  throw new Error("clone failed");
13008
13428
  }
13009
13429
  yield { message: "Source fetched from GitHub", ok: true };
13010
- const workerDir = join22(repoDir, "remote", "worker");
13011
- const wranglerToml = join22(workerDir, "wrangler.toml");
13430
+ const workerDir = join23(repoDir, "remote", "worker");
13431
+ const wranglerToml = join23(workerDir, "wrangler.toml");
13012
13432
  yield { message: "Installing Worker dependencies (npm install)\u2026" };
13013
13433
  const install = await runCmd("npm", ["install", "--no-audit", "--no-fund", "--loglevel=error"], {
13014
13434
  cwd: workerDir,
@@ -13097,7 +13517,7 @@ ${(install.stderr || install.stdout).slice(-1200).trim()}`,
13097
13517
  ok: true
13098
13518
  };
13099
13519
  yield { message: "Patching wrangler.toml\u2026" };
13100
- let toml = await readFile13(wranglerToml, "utf8");
13520
+ let toml = await readFile14(wranglerToml, "utf8");
13101
13521
  toml = toml.replace(/^name\s*=\s*"[^"]+"/m, `name = "${workerName}"`);
13102
13522
  toml = toml.replace(
13103
13523
  /(\[\[kv_namespaces\]\][\s\S]*?binding\s*=\s*"OAUTH_KV"[\s\S]*?id\s*=\s*")[^"]+(")/,
@@ -13352,6 +13772,7 @@ var init_builtins = __esm({
13352
13772
  { name: "checkpoints", description: "List checkpoints in current session", source: "builtin" },
13353
13773
  { name: "compact", description: "Summarize old turns to free context", source: "builtin" },
13354
13774
  { name: "clear", description: "Clear current conversation", source: "builtin" },
13775
+ { name: "fresh", description: "Reset session and start fresh with the last plan", source: "builtin" },
13355
13776
  { name: "init", description: "Scan repo and write KIMI.md", source: "builtin" },
13356
13777
  { name: "remote", argHint: "<prompt>", description: "Run a remote session on Cloudflare", source: "builtin" },
13357
13778
  { name: "update", description: "Check for updates", source: "builtin" },
@@ -14624,8 +15045,8 @@ var init_frontmatter = __esm({
14624
15045
  });
14625
15046
 
14626
15047
  // src/skills/loader.ts
14627
- import { readFile as readFile14, readdir as readdir5, stat as stat5 } from "fs/promises";
14628
- import { join as join23, extname } from "path";
15048
+ import { readFile as readFile15, readdir as readdir6, stat as stat6 } from "fs/promises";
15049
+ import { join as join24, extname } from "path";
14629
15050
  function normalizeManifest(raw, filePath) {
14630
15051
  const name = typeof raw.name === "string" ? raw.name : "";
14631
15052
  const description = typeof raw.description === "string" ? raw.description : "";
@@ -14639,7 +15060,7 @@ function normalizeManifest(raw, filePath) {
14639
15060
  return { name, description, match, scope, priority, enabled };
14640
15061
  }
14641
15062
  async function loadSkillFile(filePath) {
14642
- const raw = await readFile14(filePath, "utf-8");
15063
+ const raw = await readFile15(filePath, "utf-8");
14643
15064
  const parsed = parseFrontmatter(raw);
14644
15065
  const manifest = normalizeManifest(parsed.data, filePath);
14645
15066
  const body = parsed.content.trim();
@@ -14658,11 +15079,11 @@ async function loadSkillFile(filePath) {
14658
15079
  }
14659
15080
  async function loadSkillsFromDir(dirPath) {
14660
15081
  try {
14661
- const entries = await readdir5(dirPath);
15082
+ const entries = await readdir6(dirPath);
14662
15083
  const files = [];
14663
15084
  for (const entry of entries) {
14664
- const full = join23(dirPath, entry);
14665
- const s = await stat5(full);
15085
+ const full = join24(dirPath, entry);
15086
+ const s = await stat6(full);
14666
15087
  if (s.isFile() && extname(entry) === ".md") {
14667
15088
  files.push(full);
14668
15089
  }
@@ -14689,12 +15110,12 @@ var init_loader = __esm({
14689
15110
  });
14690
15111
 
14691
15112
  // src/skills/manager.ts
14692
- import { mkdir as mkdir10, writeFile as writeFile11, unlink as unlink2, readFile as readFile15 } from "fs/promises";
14693
- import { join as join24 } from "path";
15113
+ import { mkdir as mkdir10, writeFile as writeFile11, unlink as unlink2, readFile as readFile16 } from "fs/promises";
15114
+ import { join as join25 } from "path";
14694
15115
  function getSkillDirs(cwd) {
14695
15116
  return {
14696
- projectDir: join24(cwd, ".kimiflare", "skills"),
14697
- globalDir: join24(process.env.HOME ?? "", ".config", "kimiflare", "skills")
15117
+ projectDir: join25(cwd, ".kimiflare", "skills"),
15118
+ globalDir: join25(process.env.HOME ?? "", ".config", "kimiflare", "skills")
14698
15119
  };
14699
15120
  }
14700
15121
  async function listAllSkills(cwd) {
@@ -14708,7 +15129,7 @@ async function listAllSkills(cwd) {
14708
15129
  async function createSkill(opts2) {
14709
15130
  const dirs = getSkillDirs(opts2.cwd);
14710
15131
  const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
14711
- const filepath = join24(dir, `${opts2.name}.md`);
15132
+ const filepath = join25(dir, `${opts2.name}.md`);
14712
15133
  const frontmatter = {
14713
15134
  name: opts2.name,
14714
15135
  enabled: true,
@@ -14744,7 +15165,7 @@ async function setSkillEnabled(name, enabled, cwd) {
14744
15165
  const all = await listAllSkills(cwd);
14745
15166
  const skill = all.project.find((s) => s.name === name) ?? all.global.find((s) => s.name === name);
14746
15167
  if (!skill) throw new Error(`skill "${name}" not found`);
14747
- const raw = await readFile15(skill.filePath, "utf-8");
15168
+ const raw = await readFile16(skill.filePath, "utf-8");
14748
15169
  const parsed = parseFrontmatter(raw);
14749
15170
  parsed.data.enabled = enabled;
14750
15171
  const yaml = Object.entries(parsed.data).map(([k, v]) => {
@@ -14829,13 +15250,13 @@ var init_frontmatter2 = __esm({
14829
15250
  // src/commands/loader.ts
14830
15251
  import { open, realpath as realpath2 } from "fs/promises";
14831
15252
  import { homedir as homedir14 } from "os";
14832
- import { join as join25, relative as relative5, sep as sep2 } from "path";
15253
+ import { join as join26, relative as relative5, sep as sep2 } from "path";
14833
15254
  function projectCommandsDir(cwd = process.cwd()) {
14834
- return join25(cwd, ".kimiflare", "commands");
15255
+ return join26(cwd, ".kimiflare", "commands");
14835
15256
  }
14836
15257
  function globalCommandsDir() {
14837
- const xdg = process.env.XDG_CONFIG_HOME || join25(homedir14(), ".config");
14838
- return join25(xdg, "kimiflare", "commands");
15258
+ const xdg = process.env.XDG_CONFIG_HOME || join26(homedir14(), ".config");
15259
+ return join26(xdg, "kimiflare", "commands");
14839
15260
  }
14840
15261
  async function loadCustomCommands(cwd = process.cwd()) {
14841
15262
  const warnings = [];
@@ -15110,12 +15531,12 @@ var init_worker_client = __esm({
15110
15531
 
15111
15532
  // src/init/context-generator.ts
15112
15533
  import { existsSync as existsSync4, statSync as statSync4 } from "fs";
15113
- import { join as join26 } from "path";
15534
+ import { join as join27 } from "path";
15114
15535
  function detectFlavor(cwd) {
15115
15536
  for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
15116
15537
  if (flavor === "generic") continue;
15117
15538
  for (const sig of signatures) {
15118
- const path = join26(cwd, sig);
15539
+ const path = join27(cwd, sig);
15119
15540
  if (sig.includes("*")) {
15120
15541
  try {
15121
15542
  const parts = sig.split("*");
@@ -15136,14 +15557,14 @@ function detectFlavor(cwd) {
15136
15557
  }
15137
15558
  function findFile(cwd, candidates) {
15138
15559
  for (const c of candidates) {
15139
- if (existsSync4(join26(cwd, c))) return c;
15560
+ if (existsSync4(join27(cwd, c))) return c;
15140
15561
  }
15141
15562
  return null;
15142
15563
  }
15143
15564
  function findSourceRoots(cwd) {
15144
15565
  const roots = [];
15145
15566
  for (const r of SOURCE_ROOT_CANDIDATES) {
15146
- const p = join26(cwd, r);
15567
+ const p = join27(cwd, r);
15147
15568
  try {
15148
15569
  const s = statSync4(p);
15149
15570
  if (s.isDirectory()) roots.push(r);
@@ -15154,9 +15575,9 @@ function findSourceRoots(cwd) {
15154
15575
  }
15155
15576
  function findCiConfig(cwd) {
15156
15577
  for (const c of CI_PATHS) {
15157
- if (existsSync4(join26(cwd, c))) {
15578
+ if (existsSync4(join27(cwd, c))) {
15158
15579
  try {
15159
- const s = statSync4(join26(cwd, c));
15580
+ const s = statSync4(join27(cwd, c));
15160
15581
  return s.isDirectory() ? c : c;
15161
15582
  } catch {
15162
15583
  }
@@ -15293,7 +15714,7 @@ function analyzeProject(cwd) {
15293
15714
  ciConfig: findCiConfig(cwd),
15294
15715
  readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
15295
15716
  sourceRoots: findSourceRoots(cwd),
15296
- hasGit: existsSync4(join26(cwd, ".git"))
15717
+ hasGit: existsSync4(join27(cwd, ".git"))
15297
15718
  };
15298
15719
  }
15299
15720
  function bashDiscoveryCommands(profile) {
@@ -15458,7 +15879,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
15458
15879
  }
15459
15880
  function buildInitPrompt(cwd) {
15460
15881
  const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
15461
- (n) => existsSync4(join26(cwd, n))
15882
+ (n) => existsSync4(join27(cwd, n))
15462
15883
  );
15463
15884
  const isRefresh = existingName !== void 0;
15464
15885
  const targetFilename = existingName ?? "KIMI.md";
@@ -15548,8 +15969,8 @@ __export(ui_mode_exports, {
15548
15969
  });
15549
15970
  import { execSync as execSync3, spawn as spawn5 } from "child_process";
15550
15971
  import { appendFileSync, openSync } from "fs";
15551
- import { readdir as readdir6 } from "fs/promises";
15552
- import { join as join27, relative as relative6 } from "path";
15972
+ import { readdir as readdir7 } from "fs/promises";
15973
+ import { join as join28, relative as relative6 } from "path";
15553
15974
  import { platform as platform3 } from "os";
15554
15975
  function kimiLog(payload) {
15555
15976
  if (!KIMI_LOG_PATH) return;
@@ -16052,6 +16473,8 @@ Executor opened PR: ${prUrl}` : plan });
16052
16473
  if (!(err instanceof Error && err.name === "AbortError")) {
16053
16474
  cam.send("ShowToast", { text: `multi-agent spawn failed: ${err instanceof Error ? err.message : String(err)}`, kind: "error", ttl_ms: 4e3 });
16054
16475
  }
16476
+ } finally {
16477
+ multiAgentSupervisor.clearWorkers();
16055
16478
  }
16056
16479
  return;
16057
16480
  }
@@ -16245,8 +16668,8 @@ Executor opened PR: ${prUrl}` : plan });
16245
16668
  cam.send("PermissionGranted", { request_id: reqId });
16246
16669
  return "allow";
16247
16670
  }
16248
- const choice = await new Promise((resolve4) => {
16249
- pendingPermissions.set(reqId, resolve4);
16671
+ const choice = await new Promise((resolve5) => {
16672
+ pendingPermissions.set(reqId, resolve5);
16250
16673
  });
16251
16674
  cam.send(
16252
16675
  choice === "deny" ? "PermissionDenied" : "PermissionGranted",
@@ -16281,8 +16704,8 @@ Executor opened PR: ${prUrl}` : plan });
16281
16704
  async function nextFollowUp() {
16282
16705
  if (followUpQueue.length > 0) return followUpQueue.shift();
16283
16706
  if (aborted2) return null;
16284
- return new Promise((resolve4) => {
16285
- followUpResolver = resolve4;
16707
+ return new Promise((resolve5) => {
16708
+ followUpResolver = resolve5;
16286
16709
  });
16287
16710
  }
16288
16711
  async function openInboxModal() {
@@ -17489,14 +17912,14 @@ async function registerMentions(cam, recents) {
17489
17912
  if (depth > 3 || collected.length >= 200) return;
17490
17913
  let entries;
17491
17914
  try {
17492
- entries = await readdir6(dir, { withFileTypes: true });
17915
+ entries = await readdir7(dir, { withFileTypes: true });
17493
17916
  } catch {
17494
17917
  return;
17495
17918
  }
17496
17919
  for (const e of entries) {
17497
17920
  if (collected.length >= 200) return;
17498
17921
  if (e.name.startsWith(".") || SKIP.has(e.name)) continue;
17499
- const full = join27(dir, e.name);
17922
+ const full = join28(dir, e.name);
17500
17923
  if (e.isDirectory()) {
17501
17924
  await walk2(full, depth + 1);
17502
17925
  } else if (e.isFile()) {
@@ -17924,6 +18347,22 @@ var init_manager5 = __esm({
17924
18347
  }
17925
18348
  return out;
17926
18349
  }
18350
+ /** Export a compact MCP context summary for multi-agent workers. */
18351
+ exportContext() {
18352
+ const servers = this.listServers();
18353
+ if (servers.length === 0) return "";
18354
+ const lines = ["Available MCP servers:"];
18355
+ for (const s of servers) {
18356
+ lines.push(`- ${s.name} (${s.type}, ${s.toolCount} tools)`);
18357
+ const conn = this.connections.get(s.name);
18358
+ if (conn) {
18359
+ for (const entry of conn.tools.slice(0, 20)) {
18360
+ lines.push(` - ${entry.spec.name}: ${entry.spec.description.split("\n")[0]}`);
18361
+ }
18362
+ }
18363
+ }
18364
+ return lines.join("\n");
18365
+ }
17927
18366
  };
17928
18367
  }
17929
18368
  });
@@ -20282,19 +20721,19 @@ function usePermissionController(getMode, onPlanModeBlocked) {
20282
20721
  const onPlanModeBlockedRef = useRef2(onPlanModeBlocked);
20283
20722
  onPlanModeBlockedRef.current = onPlanModeBlocked;
20284
20723
  const askPermission = useCallback2(
20285
- (req, askOpts) => new Promise((resolve4) => {
20724
+ (req, askOpts) => new Promise((resolve5) => {
20286
20725
  const outcome = decidePermission(req, getModeRef.current(), askOpts);
20287
20726
  if (outcome.kind === "resolve") {
20288
- resolve4(outcome.decision);
20727
+ resolve5(outcome.decision);
20289
20728
  return;
20290
20729
  }
20291
20730
  if (outcome.kind === "plan_blocked") {
20292
20731
  onPlanModeBlockedRef.current(outcome.toolName);
20293
- resolve4(AUTO_DENY);
20732
+ resolve5(AUTO_DENY);
20294
20733
  return;
20295
20734
  }
20296
- resolveRef.current = resolve4;
20297
- setPending({ tool: req.tool, args: req.args, resolve: resolve4 });
20735
+ resolveRef.current = resolve5;
20736
+ setPending({ tool: req.tool, args: req.args, resolve: resolve5 });
20298
20737
  }),
20299
20738
  []
20300
20739
  );
@@ -20704,6 +21143,7 @@ function WorkerList({ workers, isSynthesizing, narration }) {
20704
21143
  const running = workers.filter((w) => w.status === "running").length;
20705
21144
  const completed = workers.filter((w) => w.status === "completed").length;
20706
21145
  const failed = workers.filter((w) => w.status === "failed").length;
21146
+ const budgetExhausted = workers.filter((w) => w.status === "budget_exhausted").length;
20707
21147
  const pending = workers.filter((w) => w.status === "pending").length;
20708
21148
  const showSynthesis = isSynthesizing && running === 0;
20709
21149
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
@@ -20716,8 +21156,24 @@ function WorkerList({ workers, isSynthesizing, narration }) {
20716
21156
  pending > 0 ? `${pending} todo \xB7 ` : "",
20717
21157
  running > 0 ? `${running} ongoing \xB7 ` : "",
20718
21158
  completed > 0 ? `${completed} done \xB7 ` : "",
21159
+ budgetExhausted > 0 ? `${budgetExhausted} budget hit \xB7 ` : "",
20719
21160
  failed > 0 ? `${failed} failed \xB7 ` : ""
20720
21161
  ] }) }),
21162
+ (() => {
21163
+ const anyPreRead = workers.find((w) => w.preReadFiles && w.preReadFiles.length > 0);
21164
+ if (!anyPreRead) return null;
21165
+ const fileCount = anyPreRead.preReadFiles.length;
21166
+ const chars = anyPreRead.preReadChars ?? 0;
21167
+ return /* @__PURE__ */ jsx13(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsxs11(Text12, { color: theme.info.color, children: [
21168
+ "\u{1F4E6} Shared cache: ",
21169
+ fileCount,
21170
+ " file",
21171
+ fileCount > 1 ? "s" : "",
21172
+ " pre-read (~",
21173
+ chars.toLocaleString(),
21174
+ " chars)"
21175
+ ] }) });
21176
+ })(),
20721
21177
  workers.map((w) => /* @__PURE__ */ jsx13(WorkerRow, { worker: w }, w.id)),
20722
21178
  showSynthesis && /* @__PURE__ */ jsx13(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsxs11(Text12, { color: theme.info.color, children: [
20723
21179
  /* @__PURE__ */ jsx13(Spinner5, { type: "dots" }),
@@ -20737,9 +21193,9 @@ function WorkerRow({ worker }) {
20737
21193
  }, [worker.status]);
20738
21194
  const elapsed = formatElapsed6(now2 - worker.startedAt);
20739
21195
  const modeLabel2 = worker.mode === "plan" ? "research" : "executor";
20740
- const statusIcon = worker.status === "pending" ? /* @__PURE__ */ jsx13(Text12, { color: theme.muted?.color ?? theme.info.color, children: "\u2610" }) : worker.status === "running" ? /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, children: /* @__PURE__ */ jsx13(Spinner5, { type: "line" }) }) : worker.status === "completed" ? /* @__PURE__ */ jsx13(Text12, { color: theme.palette.success, children: "\u2611" }) : /* @__PURE__ */ jsx13(Text12, { color: theme.palette.error, children: "\u2612" });
20741
- const statusLabel = worker.status === "pending" ? "todo" : worker.status === "running" ? "ongoing" : worker.status === "completed" ? "done" : "failed";
20742
- const isDone = worker.status === "completed" || worker.status === "failed";
21196
+ const statusIcon = worker.status === "pending" ? /* @__PURE__ */ jsx13(Text12, { color: theme.muted?.color ?? theme.info.color, children: "\u2610" }) : worker.status === "running" ? /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, children: /* @__PURE__ */ jsx13(Spinner5, { type: "line" }) }) : worker.status === "completed" ? /* @__PURE__ */ jsx13(Text12, { color: theme.palette.success, children: "\u2611" }) : worker.status === "budget_exhausted" ? /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, children: "\u26A0" }) : /* @__PURE__ */ jsx13(Text12, { color: theme.palette.error, children: "\u2612" });
21197
+ const statusLabel = worker.status === "pending" ? "todo" : worker.status === "running" ? "ongoing" : worker.status === "completed" ? "done" : worker.status === "budget_exhausted" ? "budget hit" : "failed";
21198
+ const isDone = worker.status === "completed" || worker.status === "failed" || worker.status === "budget_exhausted";
20743
21199
  const hasSteps = worker.steps && worker.steps.length > 0;
20744
21200
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginLeft: 2, children: [
20745
21201
  /* @__PURE__ */ jsx13(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { children: [
@@ -20774,7 +21230,8 @@ function WorkerRow({ worker }) {
20774
21230
  ] }) : null
20775
21231
  ] }) }),
20776
21232
  hasSteps && /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginLeft: 4, children: worker.steps.map((step, i) => /* @__PURE__ */ jsx13(StepRow, { step, theme }, `${worker.id}-step-${i}`)) }),
20777
- worker.logs.length > 0 && /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginLeft: 4, children: worker.logs.slice(-3).map((line, i) => /* @__PURE__ */ jsx13(Text12, { color: theme.muted?.color ?? theme.info.color, dimColor: true, children: line.slice(0, 120) }, `${worker.id}-log-${i}`)) })
21233
+ worker.logs.length > 0 && /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginLeft: 4, children: worker.logs.slice(-3).map((line, i) => /* @__PURE__ */ jsx13(Text12, { color: theme.muted?.color ?? theme.info.color, dimColor: true, children: line.slice(0, 120) }, `${worker.id}-log-${i}`)) }),
21234
+ isDone && worker.result?.phases && worker.result.phases.length > 0 && /* @__PURE__ */ jsx13(Box11, { marginLeft: 4, children: /* @__PURE__ */ jsx13(Text12, { color: theme.muted?.color ?? theme.info.color, dimColor: true, children: worker.result.phases.map((p) => `${p.name}: ${formatElapsed6(p.ms)}`).join(" \xB7 ") }) })
20778
21235
  ] });
20779
21236
  }
20780
21237
  function StepRow({ step, theme }) {
@@ -22214,10 +22671,10 @@ var init_welcome = __esm({
22214
22671
  });
22215
22672
 
22216
22673
  // src/util/image.ts
22217
- import { readFile as readFile16 } from "fs/promises";
22674
+ import { readFile as readFile17 } from "fs/promises";
22218
22675
  import { basename as basename4 } from "path";
22219
22676
  async function encodeImageFile(filePath) {
22220
- const buf = await readFile16(filePath);
22677
+ const buf = await readFile17(filePath);
22221
22678
  if (buf.byteLength > MAX_IMAGE_BYTES) {
22222
22679
  throw new Error(
22223
22680
  `image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
@@ -22484,11 +22941,11 @@ var init_wcag = __esm({
22484
22941
  });
22485
22942
 
22486
22943
  // src/ui/theme-loader.ts
22487
- import { readFile as readFile17, readdir as readdir7 } from "fs/promises";
22488
- import { join as join28 } from "path";
22944
+ import { readFile as readFile18, readdir as readdir8 } from "fs/promises";
22945
+ import { join as join29 } from "path";
22489
22946
  import { homedir as homedir15 } from "os";
22490
22947
  function projectThemesDir(cwd = process.cwd()) {
22491
- return join28(cwd, ".kimiflare", "themes");
22948
+ return join29(cwd, ".kimiflare", "themes");
22492
22949
  }
22493
22950
  function isHexColor(c) {
22494
22951
  return /^#[0-9a-fA-F]{6}$/.test(c);
@@ -22572,15 +23029,15 @@ async function loadThemesFromDir(dir, source) {
22572
23029
  const errors = [];
22573
23030
  let files;
22574
23031
  try {
22575
- files = await readdir7(dir);
23032
+ files = await readdir8(dir);
22576
23033
  } catch {
22577
23034
  return { themes, errors };
22578
23035
  }
22579
23036
  for (const file of files.filter((f) => f.endsWith(".json"))) {
22580
- const path = join28(dir, file);
23037
+ const path = join29(dir, file);
22581
23038
  let raw;
22582
23039
  try {
22583
- raw = await readFile17(path, "utf-8");
23040
+ raw = await readFile18(path, "utf-8");
22584
23041
  } catch (e) {
22585
23042
  errors.push(`${path}: ${e instanceof Error ? e.message : String(e)}`);
22586
23043
  continue;
@@ -22726,8 +23183,8 @@ var init_theme_loader = __esm({
22726
23183
  "use strict";
22727
23184
  init_wcag();
22728
23185
  init_theme();
22729
- USER_THEMES_DIR = join28(
22730
- process.env.XDG_CONFIG_HOME || join28(homedir15(), ".config"),
23186
+ USER_THEMES_DIR = join29(
23187
+ process.env.XDG_CONFIG_HOME || join29(homedir15(), ".config"),
22731
23188
  "kimiflare",
22732
23189
  "themes"
22733
23190
  );
@@ -25127,7 +25584,7 @@ var init_inbox_modal = __esm({
25127
25584
  // src/ui/app-helpers.ts
25128
25585
  import { execSync as execSync4, spawn as spawn7 } from "child_process";
25129
25586
  import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
25130
- import { join as join29 } from "path";
25587
+ import { join as join30 } from "path";
25131
25588
  import { platform as platform5 } from "os";
25132
25589
  function buildFilePickerIgnoreList(cwd) {
25133
25590
  const hardcoded = [
@@ -25199,7 +25656,7 @@ function buildFilePickerIgnoreList(cwd) {
25199
25656
  ];
25200
25657
  const gitignorePatterns = [];
25201
25658
  try {
25202
- const gitignorePath = join29(cwd, ".gitignore");
25659
+ const gitignorePath = join30(cwd, ".gitignore");
25203
25660
  const stats = statSync5(gitignorePath);
25204
25661
  if (stats.size > MAX_GITIGNORE_SIZE) {
25205
25662
  return hardcoded;
@@ -26534,7 +26991,8 @@ var init_help_menu = __esm({
26534
26991
  commands: [
26535
26992
  { command: "/resume", description: "pick a past conversation" },
26536
26993
  { command: "/compact", description: "summarize old turns to free context" },
26537
- { command: "/clear", description: "clear current conversation" }
26994
+ { command: "/clear", description: "clear current conversation" },
26995
+ { command: "/fresh", description: "reset session and start fresh with the last plan" }
26538
26996
  ]
26539
26997
  },
26540
26998
  {
@@ -27306,20 +27764,78 @@ var init_input_handlers = __esm({
27306
27764
  }
27307
27765
  });
27308
27766
 
27767
+ // src/agent/distill.ts
27768
+ function distillSessionPlan(messages) {
27769
+ for (let i = messages.length - 1; i >= 0; i--) {
27770
+ const m = messages[i];
27771
+ if (m?.role !== "assistant") continue;
27772
+ let text = "";
27773
+ if (typeof m.content === "string") {
27774
+ text = m.content;
27775
+ } else if (Array.isArray(m.content)) {
27776
+ text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
27777
+ }
27778
+ text = text.trim();
27779
+ if (text.length > 20) {
27780
+ return text;
27781
+ }
27782
+ }
27783
+ return null;
27784
+ }
27785
+ var init_distill = __esm({
27786
+ "src/agent/distill.ts"() {
27787
+ "use strict";
27788
+ }
27789
+ });
27790
+
27791
+ // src/util/clipboard.ts
27792
+ import { execSync as execSync5 } from "child_process";
27793
+ import { platform as platform7 } from "os";
27794
+ function writeToClipboard(text) {
27795
+ const os2 = platform7();
27796
+ try {
27797
+ if (os2 === "darwin") {
27798
+ execSync5("pbcopy", { input: text, timeout: 5e3 });
27799
+ return { success: true, message: "Copied to clipboard" };
27800
+ }
27801
+ if (os2 === "win32") {
27802
+ execSync5("clip", { input: text, timeout: 5e3 });
27803
+ return { success: true, message: "Copied to clipboard" };
27804
+ }
27805
+ try {
27806
+ execSync5("xclip -selection clipboard", { input: text, timeout: 5e3 });
27807
+ return { success: true, message: "Copied to clipboard" };
27808
+ } catch {
27809
+ execSync5("xsel --clipboard --input", { input: text, timeout: 5e3 });
27810
+ return { success: true, message: "Copied to clipboard" };
27811
+ }
27812
+ } catch {
27813
+ return {
27814
+ success: false,
27815
+ message: "Clipboard not available \u2014 plan will be shown below"
27816
+ };
27817
+ }
27818
+ }
27819
+ var init_clipboard = __esm({
27820
+ "src/util/clipboard.ts"() {
27821
+ "use strict";
27822
+ }
27823
+ });
27824
+
27309
27825
  // src/cost-attribution/tui-report.ts
27310
27826
  var tui_report_exports = {};
27311
27827
  __export(tui_report_exports, {
27312
27828
  getCategoryReportText: () => getCategoryReportText
27313
27829
  });
27314
- import { readFile as readFile18 } from "fs/promises";
27315
- import { join as join30 } from "path";
27830
+ import { readFile as readFile19 } from "fs/promises";
27831
+ import { join as join31 } from "path";
27316
27832
  import { homedir as homedir16 } from "os";
27317
27833
  function usageDir3() {
27318
- const xdg = process.env.XDG_DATA_HOME || join30(homedir16(), ".local", "share");
27319
- return join30(xdg, "kimiflare");
27834
+ const xdg = process.env.XDG_DATA_HOME || join31(homedir16(), ".local", "share");
27835
+ return join31(xdg, "kimiflare");
27320
27836
  }
27321
27837
  function usagePath3() {
27322
- return join30(usageDir3(), "usage.json");
27838
+ return join31(usageDir3(), "usage.json");
27323
27839
  }
27324
27840
  function today3() {
27325
27841
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -27331,7 +27847,7 @@ function daysAgo2(n) {
27331
27847
  }
27332
27848
  async function loadLog3() {
27333
27849
  try {
27334
- const raw = await readFile18(usagePath3(), "utf8");
27850
+ const raw = await readFile19(usagePath3(), "utf8");
27335
27851
  return JSON.parse(raw);
27336
27852
  } catch {
27337
27853
  return { version: 1, days: [], sessions: [] };
@@ -27388,7 +27904,7 @@ var init_tui_report = __esm({
27388
27904
  });
27389
27905
 
27390
27906
  // src/ui/slash-commands.ts
27391
- import { join as join31 } from "path";
27907
+ import { join as join32 } from "path";
27392
27908
  import { unlink as unlink4 } from "fs/promises";
27393
27909
  import QRCode from "qrcode";
27394
27910
  function dispatchSlashCommand(ctx, cmd) {
@@ -27400,7 +27916,7 @@ function dispatchSlashCommand(ctx, cmd) {
27400
27916
  if (!handler) return false;
27401
27917
  return handler(ctx, rest, arg);
27402
27918
  }
27403
- var handleExit, handleClear, 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;
27919
+ 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;
27404
27920
  var init_slash_commands = __esm({
27405
27921
  "src/ui/slash-commands.ts"() {
27406
27922
  "use strict";
@@ -27424,6 +27940,8 @@ var init_slash_commands = __esm({
27424
27940
  init_session_store();
27425
27941
  init_deploy();
27426
27942
  init_tui_auth();
27943
+ init_distill();
27944
+ init_clipboard();
27427
27945
  handleExit = (ctx) => {
27428
27946
  void ctx.lspManagerRef.current.stopAll().finally(() => ctx.exit());
27429
27947
  return true;
@@ -27463,6 +27981,65 @@ var init_slash_commands = __esm({
27463
27981
  ctx.updateNudgedRef.current = false;
27464
27982
  return true;
27465
27983
  };
27984
+ handleFresh = (ctx) => {
27985
+ const { busy, mkKey: mkKey2, setEvents } = ctx;
27986
+ if (busy) {
27987
+ setEvents((e) => [
27988
+ ...e,
27989
+ { kind: "info", key: mkKey2(), text: "can't /fresh while model is running \u2014 press Esc to interrupt first" }
27990
+ ]);
27991
+ return true;
27992
+ }
27993
+ const plan = distillSessionPlan(ctx.messagesRef.current);
27994
+ if (!plan) {
27995
+ setEvents((e) => [
27996
+ ...e,
27997
+ { kind: "error", key: mkKey2(), text: "No plan found to start fresh with." }
27998
+ ]);
27999
+ return true;
28000
+ }
28001
+ const clipResult = writeToClipboard(plan);
28002
+ if (ctx.cacheStableRef.current && ctx.messagesRef.current.length >= 2) {
28003
+ ctx.messagesRef.current = [ctx.messagesRef.current[0], ctx.messagesRef.current[1]];
28004
+ } else {
28005
+ ctx.messagesRef.current = [ctx.messagesRef.current[0]];
28006
+ }
28007
+ ctx.resetSession();
28008
+ ctx.executorRef.current.clearArtifacts();
28009
+ if (ctx.flushTimeoutRef.current) {
28010
+ clearTimeout(ctx.flushTimeoutRef.current);
28011
+ ctx.flushTimeoutRef.current = null;
28012
+ }
28013
+ ctx.pendingTextRef.current.clear();
28014
+ ctx.activeAsstIdRef.current = null;
28015
+ ctx.pendingToolCallsRef.current.clear();
28016
+ ctx.usageRef.current = null;
28017
+ ctx.turnCounterRef.current = 0;
28018
+ setEvents([]);
28019
+ ctx.setUsage(null);
28020
+ ctx.setSessionUsage(null);
28021
+ ctx.gatewayMetaRef.current = null;
28022
+ ctx.setGatewayMeta(null);
28023
+ ctx.clearTaskTracking();
28024
+ ctx.compactSuggestedRef.current = false;
28025
+ ctx.updateNudgedRef.current = false;
28026
+ ctx.messagesRef.current.push({ role: "user", content: plan });
28027
+ setEvents((e) => [
28028
+ ...e,
28029
+ {
28030
+ kind: "info",
28031
+ key: mkKey2(),
28032
+ text: clipResult.success ? "Plan copied to clipboard. Starting fresh session with plan only\u2026" : "Clipboard unavailable. Starting fresh session with plan only\u2026"
28033
+ }
28034
+ ]);
28035
+ if (!clipResult.success) {
28036
+ setEvents((e) => [
28037
+ ...e,
28038
+ { kind: "info", key: mkKey2(), text: "--- Plan ---\n" + plan }
28039
+ ]);
28040
+ }
28041
+ return true;
28042
+ };
27466
28043
  handleReasoning = (ctx) => {
27467
28044
  ctx.setShowReasoning((s) => {
27468
28045
  const next = !s;
@@ -27762,14 +28339,24 @@ var init_slash_commands = __esm({
27762
28339
  return true;
27763
28340
  };
27764
28341
  handleMode = (ctx, _rest, arg) => {
27765
- const { setEvents, mkKey: mkKey2 } = ctx;
28342
+ const { setEvents, mkKey: mkKey2, mode } = ctx;
27766
28343
  if (!arg) {
27767
28344
  ctx.setShowModePicker(true);
27768
28345
  return true;
27769
28346
  }
27770
28347
  if (arg === "edit" || arg === "plan" || arg === "auto" || arg === "multi-agent-experimental") {
28348
+ const prevMode = mode;
27771
28349
  ctx.setMode(arg);
27772
28350
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `mode: ${arg}` }]);
28351
+ if (prevMode === "plan" && (arg === "auto" || arg === "edit")) {
28352
+ const nonSystemCount = ctx.messagesRef.current.filter((m) => m.role !== "system").length;
28353
+ if (nonSystemCount > 10) {
28354
+ setEvents((e) => [
28355
+ ...e,
28356
+ { kind: "info", key: mkKey2(), text: "Tip: you have extensive planning context. Run `/fresh` to start clean with just the plan." }
28357
+ ]);
28358
+ }
28359
+ }
27773
28360
  return true;
27774
28361
  }
27775
28362
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "usage: /mode edit|plan|auto|multi-agent-experimental" }]);
@@ -28119,7 +28706,7 @@ ${lines.join("\n")}` }]);
28119
28706
  void (async () => {
28120
28707
  try {
28121
28708
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
28122
- const file = await loadSession(join31(sessionsDir3(), `${currentId}.json`));
28709
+ const file = await loadSession(join32(sessionsDir3(), `${currentId}.json`));
28123
28710
  const cps = file.checkpoints ?? [];
28124
28711
  if (cps.length === 0) {
28125
28712
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "no checkpoints in this session" }]);
@@ -28164,7 +28751,7 @@ ${lines.join("\n")}` }]);
28164
28751
  try {
28165
28752
  ctx.ensureSessionId();
28166
28753
  const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
28167
- const filePath = join31(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
28754
+ const filePath = join32(sessionsDir3(), `${ctx.sessionIdRef.current}.json`);
28168
28755
  await addCheckpoint(filePath, cp);
28169
28756
  setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `checkpoint saved: "${label}"` }]);
28170
28757
  } catch (e) {
@@ -28635,6 +29222,7 @@ project: ${projectSettingsPath(cwd)}`
28635
29222
  handlers = {
28636
29223
  "/exit": handleExit,
28637
29224
  "/clear": handleClear,
29225
+ "/fresh": handleFresh,
28638
29226
  "/reasoning": handleReasoning,
28639
29227
  "/cost": handleCost,
28640
29228
  "/shell": handleShell,
@@ -28669,7 +29257,7 @@ project: ${projectSettingsPath(cwd)}`
28669
29257
 
28670
29258
  // src/init/run-init.ts
28671
29259
  import { existsSync as existsSync6 } from "fs";
28672
- import { join as join32 } from "path";
29260
+ import { join as join33 } from "path";
28673
29261
  async function runInit(deps) {
28674
29262
  const {
28675
29263
  cfg,
@@ -28766,7 +29354,7 @@ async function runInit(deps) {
28766
29354
  lspManagerRef.current.notifyChange(path, content);
28767
29355
  } else {
28768
29356
  void import("fs/promises").then(
28769
- ({ readFile: readFile21 }) => readFile21(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
29357
+ ({ readFile: readFile22 }) => readFile22(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
28770
29358
  })
28771
29359
  );
28772
29360
  }
@@ -28852,9 +29440,9 @@ async function runInit(deps) {
28852
29440
  },
28853
29441
  onGatewayMeta: updateGatewayMeta,
28854
29442
  askPermission: (req) => askForPermission(req, { promptOnBlockedBash: true }),
28855
- onLoopDetected: () => new Promise((resolve4) => {
28856
- loopResolveRef.current = resolve4;
28857
- setLoopModal({ resolve: resolve4 });
29443
+ onLoopDetected: () => new Promise((resolve5) => {
29444
+ loopResolveRef.current = resolve5;
29445
+ setLoopModal({ resolve: resolve5 });
28858
29446
  }),
28859
29447
  onKimiMdStale: () => {
28860
29448
  if (!kimiMdStaleNudgedRef.current) {
@@ -28872,7 +29460,7 @@ async function runInit(deps) {
28872
29460
  }
28873
29461
  }
28874
29462
  });
28875
- if (existsSync6(join32(cwd, "KIMI.md"))) {
29463
+ if (existsSync6(join33(cwd, "KIMI.md"))) {
28876
29464
  if (cacheStableRef.current) {
28877
29465
  messagesRef.current[1] = {
28878
29466
  role: "system",
@@ -28966,11 +29554,11 @@ var init_run_init = __esm({
28966
29554
  });
28967
29555
 
28968
29556
  // src/skills/discovery.ts
28969
- import { readdir as readdir8, stat as stat6, readFile as readFile19 } from "fs/promises";
28970
- import { join as join33, extname as extname2 } from "path";
29557
+ import { readdir as readdir9, stat as stat7, readFile as readFile20 } from "fs/promises";
29558
+ import { join as join34, extname as extname2 } from "path";
28971
29559
  async function dirExists(path) {
28972
29560
  try {
28973
- const s = await stat6(path);
29561
+ const s = await stat7(path);
28974
29562
  return s.isDirectory();
28975
29563
  } catch {
28976
29564
  return false;
@@ -28978,7 +29566,7 @@ async function dirExists(path) {
28978
29566
  }
28979
29567
  async function fileExists(path) {
28980
29568
  try {
28981
- const s = await stat6(path);
29569
+ const s = await stat7(path);
28982
29570
  return s.isFile();
28983
29571
  } catch {
28984
29572
  return false;
@@ -28986,25 +29574,25 @@ async function fileExists(path) {
28986
29574
  }
28987
29575
  async function scanSkillDir(dirPath, source) {
28988
29576
  if (!await dirExists(dirPath)) return [];
28989
- const entries = await readdir8(dirPath, { withFileTypes: true });
29577
+ const entries = await readdir9(dirPath, { withFileTypes: true });
28990
29578
  const files = [];
28991
29579
  for (const entry of entries) {
28992
29580
  if (!entry.isFile()) continue;
28993
29581
  if (!SKILL_EXTENSIONS.has(extname2(entry.name))) continue;
28994
- files.push({ filePath: join33(dirPath, entry.name), source });
29582
+ files.push({ filePath: join34(dirPath, entry.name), source });
28995
29583
  }
28996
29584
  return files;
28997
29585
  }
28998
29586
  async function discoverSkills(cwd) {
28999
- const agentsSkills = await scanSkillDir(join33(cwd, ".agents", "skills"), "agents");
29000
- const agentsMd = await fileExists(join33(cwd, "AGENTS.md")) ? [{ filePath: join33(cwd, "AGENTS.md"), source: "agents-md" }] : [];
29001
- const githubSkills = await scanSkillDir(join33(cwd, ".github", "skills"), "github");
29002
- const kimiflareSkills = await scanSkillDir(join33(cwd, ".kimiflare", "skills"), "kimiflare");
29587
+ const agentsSkills = await scanSkillDir(join34(cwd, ".agents", "skills"), "agents");
29588
+ const agentsMd = await fileExists(join34(cwd, "AGENTS.md")) ? [{ filePath: join34(cwd, "AGENTS.md"), source: "agents-md" }] : [];
29589
+ const githubSkills = await scanSkillDir(join34(cwd, ".github", "skills"), "github");
29590
+ const kimiflareSkills = await scanSkillDir(join34(cwd, ".kimiflare", "skills"), "kimiflare");
29003
29591
  const ordered = [...agentsSkills, ...agentsMd, ...githubSkills, ...kimiflareSkills];
29004
29592
  return ordered;
29005
29593
  }
29006
29594
  async function readSkillFile(filePath) {
29007
- const bytes = await readFile19(filePath);
29595
+ const bytes = await readFile20(filePath);
29008
29596
  return { bytes, text: bytes.toString("utf-8") };
29009
29597
  }
29010
29598
  var SKILL_EXTENSIONS;
@@ -29016,9 +29604,9 @@ var init_discovery = __esm({
29016
29604
  });
29017
29605
 
29018
29606
  // src/skills/parser.ts
29019
- import { createHash as createHash2 } from "crypto";
29607
+ import { createHash as createHash3 } from "crypto";
29020
29608
  function sha256(input) {
29021
- return createHash2("sha256").update(input).digest("hex");
29609
+ return createHash3("sha256").update(input).digest("hex");
29022
29610
  }
29023
29611
  function computeContentHash(rawText, parserVersion) {
29024
29612
  return sha256(`${rawText}
@@ -29209,16 +29797,16 @@ var init_skills = __esm({
29209
29797
  });
29210
29798
 
29211
29799
  // src/util/state.ts
29212
- import { readFile as readFile20, writeFile as writeFile13, mkdir as mkdir12 } from "fs/promises";
29800
+ import { readFile as readFile21, writeFile as writeFile13, mkdir as mkdir12 } from "fs/promises";
29213
29801
  import { homedir as homedir17 } from "os";
29214
- import { join as join34 } from "path";
29802
+ import { join as join35 } from "path";
29215
29803
  function statePath() {
29216
- const xdg = process.env.XDG_CONFIG_HOME || join34(homedir17(), ".config");
29217
- return join34(xdg, "kimiflare", "state.json");
29804
+ const xdg = process.env.XDG_CONFIG_HOME || join35(homedir17(), ".config");
29805
+ return join35(xdg, "kimiflare", "state.json");
29218
29806
  }
29219
29807
  async function readState() {
29220
29808
  try {
29221
- const raw = await readFile20(statePath(), "utf8");
29809
+ const raw = await readFile21(statePath(), "utf8");
29222
29810
  return JSON.parse(raw);
29223
29811
  } catch {
29224
29812
  return {};
@@ -29226,7 +29814,7 @@ async function readState() {
29226
29814
  }
29227
29815
  async function writeState(state) {
29228
29816
  const path = statePath();
29229
- await mkdir12(join34(path, ".."), { recursive: true });
29817
+ await mkdir12(join35(path, ".."), { recursive: true });
29230
29818
  await writeFile13(path, JSON.stringify(state, null, 2) + "\n", "utf8");
29231
29819
  }
29232
29820
  async function markCreatorMessageSeen(version) {
@@ -29246,7 +29834,7 @@ var init_state = __esm({
29246
29834
 
29247
29835
  // src/ui/run-startup-tasks.ts
29248
29836
  import { existsSync as existsSync7 } from "fs";
29249
- import { join as join35 } from "path";
29837
+ import { join as join36 } from "path";
29250
29838
  function runStartupTasks(deps) {
29251
29839
  const {
29252
29840
  cfg,
@@ -29297,7 +29885,7 @@ function runStartupTasks(deps) {
29297
29885
  }
29298
29886
  });
29299
29887
  if (cfg.memoryEnabled) {
29300
- const dbPath = cfg.memoryDbPath ?? join35(process.cwd(), ".kimiflare", "memory.db");
29888
+ const dbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
29301
29889
  const manager = new MemoryManager({
29302
29890
  dbPath,
29303
29891
  accountId: cfg.accountId,
@@ -29331,7 +29919,7 @@ function runStartupTasks(deps) {
29331
29919
  });
29332
29920
  const cwd = process.cwd();
29333
29921
  sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
29334
- if (existsSync7(join35(cwd, "KIMI.md"))) {
29922
+ if (existsSync7(join36(cwd, "KIMI.md"))) {
29335
29923
  const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
29336
29924
  const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
29337
29925
  if (driftCount >= 5) {
@@ -29342,7 +29930,7 @@ function runStartupTasks(deps) {
29342
29930
  memoryManagerRef.current?.close();
29343
29931
  memoryManagerRef.current = null;
29344
29932
  }
29345
- const skillDbPath = cfg.memoryDbPath ?? join35(process.cwd(), ".kimiflare", "memory.db");
29933
+ const skillDbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
29346
29934
  const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
29347
29935
  initSkillsSchema(skillDb);
29348
29936
  void indexSkills({
@@ -29811,7 +30399,7 @@ __export(app_exports, {
29811
30399
  import React23, { useState as useState27, useRef as useRef7, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
29812
30400
  import { Box as Box39, Text as Text40, useApp, useInput as useInput19, render } from "ink";
29813
30401
  import { existsSync as existsSync8 } from "fs";
29814
- import { join as join36 } from "path";
30402
+ import { join as join37 } from "path";
29815
30403
  import { jsx as jsx41, jsxs as jsxs39 } from "react/jsx-runtime";
29816
30404
  function App({
29817
30405
  initialCfg,
@@ -31124,7 +31712,7 @@ ${wcagWarnings.join("\n")}` }
31124
31712
  }
31125
31713
  }
31126
31714
  turnCounterRef.current += 1;
31127
- if (turnCounterRef.current % 15 === 0 && existsSync8(join36(process.cwd(), "KIMI.md")) && !kimiMdStale) {
31715
+ if (turnCounterRef.current % 15 === 0 && existsSync8(join37(process.cwd(), "KIMI.md")) && !kimiMdStale) {
31128
31716
  setEvents((e) => [
31129
31717
  ...e,
31130
31718
  { kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
@@ -31164,6 +31752,9 @@ ${wcagWarnings.join("\n")}` }
31164
31752
  const controller = new AbortController();
31165
31753
  multiAgentAbortRef.current = controller;
31166
31754
  try {
31755
+ supervisorRef.current.memoryManager = memoryManagerRef.current;
31756
+ supervisorRef.current.lspManager = lspManagerRef.current;
31757
+ supervisorRef.current.mcpManager = mcpManagerRef.current;
31167
31758
  const { plan, conflicts, recommendations, prUrl, executor } = await supervisorRef.current.autoSpawnWorkers(
31168
31759
  trimmed,
31169
31760
  `Current project: ${process.cwd()}`,
@@ -31190,6 +31781,8 @@ ${wcagWarnings.join("\n")}` }
31190
31781
  }
31191
31782
  ]);
31192
31783
  messagesRef.current.push({ role: "assistant", content: plan });
31784
+ setActiveWorkers([]);
31785
+ supervisorRef.current.clearWorkers();
31193
31786
  if (conflicts.length > 0) {
31194
31787
  setEvents((e) => [
31195
31788
  ...e,
@@ -31225,6 +31818,8 @@ ${conflicts.join("\n")}` }
31225
31818
  { kind: "error", key: mkKey(), text: `multi-agent spawn failed: ${err.message}` }
31226
31819
  ]);
31227
31820
  }
31821
+ setActiveWorkers([]);
31822
+ supervisorRef.current.clearWorkers();
31228
31823
  endTurn();
31229
31824
  return;
31230
31825
  } finally {
@@ -31334,13 +31929,13 @@ ${conflicts.join("\n")}` }
31334
31929
  }
31335
31930
  },
31336
31931
  askPermission: askForPermission,
31337
- onToolLimitReached: () => new Promise((resolve4) => {
31338
- limitResolveRef.current = resolve4;
31339
- setLimitModal({ limit: 50, resolve: resolve4 });
31932
+ onToolLimitReached: () => new Promise((resolve5) => {
31933
+ limitResolveRef.current = resolve5;
31934
+ setLimitModal({ limit: 50, resolve: resolve5 });
31340
31935
  }),
31341
- onLoopDetected: () => new Promise((resolve4) => {
31342
- loopResolveRef.current = resolve4;
31343
- setLoopModal({ resolve: resolve4 });
31936
+ onLoopDetected: () => new Promise((resolve5) => {
31937
+ loopResolveRef.current = resolve5;
31938
+ setLoopModal({ resolve: resolve5 });
31344
31939
  }),
31345
31940
  onKimiMdStale: () => {
31346
31941
  if (!kimiMdStaleNudgedRef.current) {
@@ -31440,7 +32035,7 @@ ${conflicts.join("\n")}` }
31440
32035
  lspManagerRef.current.notifyChange(path, content2);
31441
32036
  } else {
31442
32037
  void import("fs/promises").then(
31443
- ({ readFile: readFile21 }) => readFile21(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
32038
+ ({ readFile: readFile22 }) => readFile22(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
31444
32039
  })
31445
32040
  );
31446
32041
  }
@@ -31687,7 +32282,7 @@ ${conflicts.join("\n")}` }
31687
32282
  onCommandDelete: handleCommandDelete2,
31688
32283
  lspServers: cfg?.lspServers ?? {},
31689
32284
  lspScope,
31690
- hasProjectDir: existsSync8(join36(process.cwd(), ".kimiflare")),
32285
+ hasProjectDir: existsSync8(join37(process.cwd(), ".kimiflare")),
31691
32286
  onLspSave: handleLspSave2,
31692
32287
  themes: themeList(),
31693
32288
  onPickTheme: handleThemePick,