kimiflare 0.77.0 → 0.79.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
@@ -11985,7 +11985,7 @@ var init_supervisor = __esm({
11985
11985
  * Future: the coordinator will call this automatically when intent classification
11986
11986
  * signals a "heavy" task (see docs/plans/multi-agent-standalone-workers-plan.md).
11987
11987
  */
11988
- async spawnWorkers(workers, onUpdate) {
11988
+ async spawnWorkers(workers, onUpdate, signal) {
11989
11989
  const cfg = await loadConfig().catch(() => null);
11990
11990
  const endpoint = process.env.KIMIFLARE_WORKER_ENDPOINT ?? cfg?.workerEndpoint ?? cfg?.remoteWorkerUrl;
11991
11991
  if (!endpoint) {
@@ -12068,7 +12068,7 @@ var init_supervisor = __esm({
12068
12068
  prBody: w.prBody
12069
12069
  } : {}
12070
12070
  };
12071
- const timeoutMs = parseInt(process.env.KIMIFLARE_WORKER_TIMEOUT_MS ?? "600000", 10);
12071
+ const timeoutMs = parseInt(process.env.KIMIFLARE_WORKER_TIMEOUT_MS ?? "900000", 10);
12072
12072
  worker.logs.push(`[coordinator] Sending payload (${JSON.stringify(payload).length} bytes)`);
12073
12073
  worker.logs.push(`[coordinator] Worker will clone ${repo.owner}/${repo.repo} and run kimiflare inside Cloudflare Sandbox`);
12074
12074
  worker.logs.push(`[coordinator] Typical runtime: 1\u20134 min. Timeout: ${Math.round(timeoutMs / 1e3)}s`);
@@ -12086,13 +12086,27 @@ var init_supervisor = __esm({
12086
12086
  throw new Error(`Worker start failed: ${startRes.status} ${text.slice(0, 200)}`);
12087
12087
  }
12088
12088
  const { workerId: remoteWorkerId } = await startRes.json();
12089
+ worker.remoteWorkerId = remoteWorkerId;
12089
12090
  worker.logs.push(`[coordinator] Worker started (id: ${remoteWorkerId}) \u2014 polling for progress\u2026`);
12090
12091
  onUpdate?.([...activeWorkers.values()]);
12091
12092
  const pollInterval = 3e3;
12092
12093
  const startTime = Date.now();
12093
12094
  let lastLogCount = 0;
12095
+ let lastStep = "";
12094
12096
  let data;
12095
12097
  while (Date.now() - startTime < timeoutMs) {
12098
+ if (signal?.aborted) {
12099
+ worker.logs.push(`[coordinator] Cancelling worker (id: ${remoteWorkerId})\u2026`);
12100
+ onUpdate?.([...activeWorkers.values()]);
12101
+ try {
12102
+ await fetch(`${endpoint}/worker/${remoteWorkerId}/cancel`, {
12103
+ method: "POST",
12104
+ headers: apiKey ? { "X-Worker-Api-Key": apiKey } : {}
12105
+ });
12106
+ } catch {
12107
+ }
12108
+ throw new Error("Cancelled by user");
12109
+ }
12096
12110
  await new Promise((r) => setTimeout(r, pollInterval));
12097
12111
  let progressRes;
12098
12112
  let lastPollErr;
@@ -12120,13 +12134,30 @@ var init_supervisor = __esm({
12120
12134
  continue;
12121
12135
  }
12122
12136
  const progress = await progressRes.json();
12137
+ const allSteps = [];
12138
+ for (let i = 0; i < progress.totalSteps; i++) {
12139
+ const isCompleted = i < progress.completedSteps.length;
12140
+ const isActive = i === progress.stepIndex - 1 && !isCompleted && progress.status === "running";
12141
+ const isFailed = progress.status === "failed" && i === progress.stepIndex - 1;
12142
+ allSteps.push({
12143
+ label: progress.completedSteps[i] ?? progress.step,
12144
+ status: isFailed ? "failed" : isCompleted ? "completed" : isActive ? "active" : "pending"
12145
+ });
12146
+ }
12147
+ worker.steps = allSteps;
12123
12148
  const newLogs = progress.logs.slice(lastLogCount);
12124
12149
  lastLogCount = progress.logs.length;
12125
12150
  for (const logLine of newLogs) {
12126
12151
  worker.logs.push(`[worker] ${logLine}`);
12127
12152
  }
12128
- worker.logs.push(`[coordinator] Step ${progress.stepIndex}/${progress.totalSteps}: ${progress.message}`);
12129
- onUpdate?.([...activeWorkers.values()]);
12153
+ const stepKey = `${progress.stepIndex}:${progress.step}`;
12154
+ if (stepKey !== lastStep) {
12155
+ lastStep = stepKey;
12156
+ worker.logs.push(`[coordinator] Step ${progress.stepIndex}/${progress.totalSteps}: ${progress.message}`);
12157
+ onUpdate?.([...activeWorkers.values()]);
12158
+ } else if (newLogs.length > 0) {
12159
+ onUpdate?.([...activeWorkers.values()]);
12160
+ }
12130
12161
  if (progress.status === "completed" || progress.status === "failed") {
12131
12162
  if (progress.result) {
12132
12163
  data = progress.result;
@@ -12155,6 +12186,13 @@ var init_supervisor = __esm({
12155
12186
  worker.result = data;
12156
12187
  worker.rawOutput = data.rawOutput;
12157
12188
  worker.reasoning = data.reasoning;
12189
+ if (worker.steps && worker.steps.length > 0) {
12190
+ for (const s of worker.steps) {
12191
+ if (s.status === "pending" || s.status === "active") {
12192
+ s.status = worker.status === "completed" ? "completed" : "failed";
12193
+ }
12194
+ }
12195
+ }
12158
12196
  worker.logs.push(`[coordinator] Worker finished with status: ${data.status}`);
12159
12197
  if (data.phases && data.phases.length > 0) {
12160
12198
  const timeline = data.phases.map((p) => `${p.name}: ${Math.round(p.ms / 1e3)}s`).join(" \xB7 ");
@@ -12272,11 +12310,21 @@ var init_supervisor = __esm({
12272
12310
  *
12273
12311
  * Returns the synthesized plan after all workers complete.
12274
12312
  */
12275
- async autoSpawnWorkers(prompt, context, onUpdate, onPhaseChange) {
12313
+ async autoSpawnWorkers(prompt, context, onUpdate, onPhaseChange, signal, onNarration) {
12314
+ if (this._activeWorkers.size > 0) {
12315
+ throw new Error(
12316
+ `Multi-agent already active (${this._activeWorkers.size} worker(s) in flight). Wait for completion or cancel before starting a new heavy task.`
12317
+ );
12318
+ }
12276
12319
  const workers = decomposePrompt(prompt, context);
12320
+ const narrationLines = [
12321
+ `Decomposing your request into ${workers.length} parallel research task${workers.length > 1 ? "s" : ""}:`,
12322
+ ...workers.map((w, i) => ` ${i + 1}. ${w.task.slice(0, 200)}${w.task.length > 200 ? "\u2026" : ""}`)
12323
+ ];
12324
+ onNarration?.(narrationLines.join("\n"));
12277
12325
  onPhaseChange?.("spawning");
12278
12326
  try {
12279
- const results = await this.spawnWorkers(workers, onUpdate);
12327
+ const results = await this.spawnWorkers(workers, onUpdate, signal);
12280
12328
  onPhaseChange?.("synthesizing");
12281
12329
  const synth = this.synthesizeFindings(results);
12282
12330
  const cfg = await loadConfig().catch(() => null);
@@ -12294,7 +12342,8 @@ var init_supervisor = __esm({
12294
12342
  try {
12295
12343
  const execResults = await this.spawnWorkers(
12296
12344
  [{ mode: "execute", task: executeTask, context }],
12297
- onUpdate
12345
+ onUpdate,
12346
+ signal
12298
12347
  );
12299
12348
  const exec2 = execResults[0];
12300
12349
  if (!exec2) {
@@ -12719,6 +12768,70 @@ import { mkdtemp, readFile as readFile13, writeFile as writeFile10, rm } from "f
12719
12768
  import { tmpdir as tmpdir3 } from "os";
12720
12769
  import { join as join22 } from "path";
12721
12770
  import { randomBytes as randomBytes2 } from "crypto";
12771
+ async function cfApiFetch(accountId, apiToken, path, init) {
12772
+ const url = `${CF_API}/accounts/${encodeURIComponent(accountId)}${path}`;
12773
+ const res = await fetch(url, {
12774
+ ...init,
12775
+ headers: {
12776
+ Authorization: `Bearer ${apiToken}`,
12777
+ "Content-Type": "application/json",
12778
+ "User-Agent": getUserAgent(),
12779
+ ...init?.headers
12780
+ }
12781
+ });
12782
+ const json = await res.json();
12783
+ return json;
12784
+ }
12785
+ async function listDurableObjectNamespaces(accountId, apiToken) {
12786
+ const json = await cfApiFetch(
12787
+ accountId,
12788
+ apiToken,
12789
+ "/workers/durable_objects/namespaces"
12790
+ );
12791
+ if (!json.success || !json.result) {
12792
+ throw new Error(
12793
+ json.errors?.map((e) => e.message).join(", ") ?? "Failed to list DO namespaces"
12794
+ );
12795
+ }
12796
+ return json.result;
12797
+ }
12798
+ async function deleteDurableObjectNamespace(accountId, apiToken, namespaceId) {
12799
+ const json = await cfApiFetch(
12800
+ accountId,
12801
+ apiToken,
12802
+ `/workers/durable_objects/namespaces/${encodeURIComponent(namespaceId)}`,
12803
+ { method: "DELETE" }
12804
+ );
12805
+ if (!json.success) {
12806
+ throw new Error(
12807
+ json.errors?.map((e) => e.message).join(", ") ?? "Failed to delete DO namespace"
12808
+ );
12809
+ }
12810
+ }
12811
+ async function listContainerApplications(accountId, apiToken) {
12812
+ const json = await cfApiFetch(
12813
+ accountId,
12814
+ apiToken,
12815
+ "/containers/applications"
12816
+ );
12817
+ if (!json.success || !json.result) {
12818
+ return [];
12819
+ }
12820
+ return json.result;
12821
+ }
12822
+ async function deleteContainerApplication(accountId, apiToken, appId) {
12823
+ const json = await cfApiFetch(
12824
+ accountId,
12825
+ apiToken,
12826
+ `/containers/applications/${encodeURIComponent(appId)}`,
12827
+ { method: "DELETE" }
12828
+ );
12829
+ if (!json.success) {
12830
+ throw new Error(
12831
+ json.errors?.map((e) => e.message).join(", ") ?? "Failed to delete container application"
12832
+ );
12833
+ }
12834
+ }
12722
12835
  function generateSecret2() {
12723
12836
  return randomBytes2(32).toString("hex");
12724
12837
  }
@@ -12991,21 +13104,23 @@ ${(install.stderr || install.stdout).slice(-1200).trim()}`,
12991
13104
  `$1${finalKvId}$2`
12992
13105
  );
12993
13106
  toml = toml.replace(/\n\[\[artifacts\]\][\s\S]*?(?=\n\[|\n*$)/g, "\n");
12994
- const existingMigrations = toml.match(/\[\[migrations\]\]/);
12995
- if (existingMigrations) {
12996
- toml = toml.replace(
12997
- /\[\[migrations\]\][\s\S]*?new_sqlite_classes\s*=\s*\[[^\]]*\]/,
12998
- `[[migrations]]
12999
- tag = "v2"
13107
+ if (!workerExists) {
13108
+ const existingMigrations = toml.match(/\[\[migrations\]\]/);
13109
+ if (existingMigrations) {
13110
+ toml = toml.replace(
13111
+ /\[\[migrations\]\][\s\S]*?new_sqlite_classes\s*=\s*\[[^\]]*\]/,
13112
+ `[[migrations]]
13113
+ tag = "v1"
13000
13114
  new_sqlite_classes = ["SessionDO", "WorkerDO", "Sandbox"]`
13001
- );
13002
- } else {
13003
- toml += `
13115
+ );
13116
+ } else {
13117
+ toml += `
13004
13118
  # Auto-added by kimiflare /multi-agent \u2192 Set up (fresh deploy only)
13005
13119
  [[migrations]]
13006
13120
  tag = "v1"
13007
13121
  new_sqlite_classes = ["SessionDO", "WorkerDO", "Sandbox"]
13008
13122
  `;
13123
+ }
13009
13124
  }
13010
13125
  if (!/\[observability\.logs\]/.test(toml)) {
13011
13126
  toml += `
@@ -13061,6 +13176,7 @@ invocation_logs = true
13061
13176
  ...cfg,
13062
13177
  workerEndpoint: workerUrl,
13063
13178
  workerApiKey,
13179
+ workerName,
13064
13180
  multiAgentEnabled: true
13065
13181
  };
13066
13182
  await saveConfig(next);
@@ -13070,12 +13186,13 @@ invocation_logs = true
13070
13186
  yield { message: "Setup complete \u2014 multi-agent is ready to use.", done: true };
13071
13187
  return { workerEndpoint: workerUrl, workerApiKey };
13072
13188
  }
13073
- async function* teardownCommute() {
13189
+ async function* teardownCommute(opts2 = {}) {
13074
13190
  const cfg = await loadConfig();
13075
13191
  if (!cfg?.accountId || !cfg?.apiToken) {
13076
13192
  yield { message: "Cloudflare credentials missing \u2014 nothing to tear down.", error: true };
13077
13193
  throw new Error("missing CF creds");
13078
13194
  }
13195
+ const workerName = opts2.workerName ?? cfg.workerName ?? WORKER_NAME;
13079
13196
  const cfEnv = {
13080
13197
  CLOUDFLARE_ACCOUNT_ID: cfg.accountId,
13081
13198
  CLOUDFLARE_API_TOKEN: cfg.apiToken
@@ -13084,25 +13201,72 @@ async function* teardownCommute() {
13084
13201
  yield { message: "wrangler not found. Install: npm install -g wrangler", error: true };
13085
13202
  throw new Error("wrangler missing");
13086
13203
  }
13087
- yield { message: `Deleting Worker "${WORKER_NAME}"\u2026` };
13088
- const del = await runCmd("wrangler", ["delete", "--name", WORKER_NAME], {
13204
+ yield { message: `Deleting Worker "${workerName}"\u2026` };
13205
+ const del = await runCmd("wrangler", ["delete", "--name", workerName], {
13089
13206
  env: cfEnv,
13090
13207
  input: "y\n",
13091
13208
  timeoutMs: 6e4
13092
13209
  });
13093
13210
  if (del.code === 0) {
13094
- yield { message: `Worker "${WORKER_NAME}" deleted`, ok: true };
13211
+ yield { message: `Worker "${workerName}" deleted`, ok: true };
13095
13212
  } else {
13096
13213
  const combined = (del.stdout + del.stderr).toLowerCase();
13097
13214
  if (combined.includes("not found") || combined.includes("does not exist") || combined.includes("10007")) {
13098
13215
  yield { message: "Worker not found (already deleted or never created)", ok: true };
13099
13216
  } else {
13100
13217
  yield {
13101
- message: explainWranglerFailure(`wrangler delete --name ${WORKER_NAME}`, del.stdout, del.stderr),
13218
+ message: explainWranglerFailure(`wrangler delete --name ${workerName}`, del.stdout, del.stderr),
13102
13219
  error: true
13103
13220
  };
13104
13221
  }
13105
13222
  }
13223
+ yield { message: `Looking up Durable Object namespaces for "${workerName}"\u2026` };
13224
+ try {
13225
+ const namespaces = await listDurableObjectNamespaces(cfg.accountId, cfg.apiToken);
13226
+ const targets = namespaces.filter(
13227
+ (ns) => ns.script === workerName || ns.name.startsWith(`${workerName}_`)
13228
+ );
13229
+ if (targets.length === 0) {
13230
+ yield { message: "No Durable Object namespaces found (nothing to delete)", ok: true };
13231
+ } else {
13232
+ for (const ns of targets) {
13233
+ try {
13234
+ await deleteDurableObjectNamespace(cfg.accountId, cfg.apiToken, ns.id);
13235
+ yield { message: `DO namespace ${ns.name} deleted (${ns.id.slice(0, 8)}\u2026)`, ok: true };
13236
+ } catch (err) {
13237
+ yield {
13238
+ message: `DO namespace ${ns.name} delete warning: ${err instanceof Error ? err.message : String(err)}`
13239
+ };
13240
+ }
13241
+ }
13242
+ }
13243
+ } catch (err) {
13244
+ yield {
13245
+ message: `DO namespace lookup warning: ${err instanceof Error ? err.message : String(err)}`
13246
+ };
13247
+ }
13248
+ yield { message: `Looking up container applications for "${workerName}"\u2026` };
13249
+ try {
13250
+ const apps = await listContainerApplications(cfg.accountId, cfg.apiToken);
13251
+ const appPrefix = `${workerName}-`.toLowerCase();
13252
+ const targets = apps.filter((app) => app.name.toLowerCase().startsWith(appPrefix));
13253
+ if (targets.length === 0) {
13254
+ yield { message: "No container applications found (nothing to delete)", ok: true };
13255
+ } else {
13256
+ for (const app of targets) {
13257
+ try {
13258
+ await deleteContainerApplication(cfg.accountId, cfg.apiToken, app.id);
13259
+ yield { message: `Container application ${app.name} deleted (${app.id.slice(0, 8)}\u2026)`, ok: true };
13260
+ } catch (err) {
13261
+ yield {
13262
+ message: `Container application ${app.name} delete warning: ${err instanceof Error ? err.message : String(err)}`
13263
+ };
13264
+ }
13265
+ }
13266
+ }
13267
+ } catch {
13268
+ yield { message: "(could not list container applications \u2014 skipping container cleanup)" };
13269
+ }
13106
13270
  yield { message: "Listing KV namespaces to find OAUTH_KV\u2026" };
13107
13271
  const kvList = await runCmd("wrangler", ["kv", "namespace", "list"], { env: cfEnv, timeoutMs: 3e4 });
13108
13272
  if (kvList.code === 0) {
@@ -13136,6 +13300,7 @@ async function* teardownCommute() {
13136
13300
  ...cfg,
13137
13301
  workerEndpoint: void 0,
13138
13302
  workerApiKey: void 0,
13303
+ workerName: void 0,
13139
13304
  multiAgentEnabled: false,
13140
13305
  autoExecute: false
13141
13306
  };
@@ -13143,15 +13308,17 @@ async function* teardownCommute() {
13143
13308
  yield { message: "Local multi-agent config cleared", ok: true };
13144
13309
  yield { message: "Tear-down complete \u2014 multi-agent is fully removed.", done: true };
13145
13310
  }
13146
- var COMMUTE_REPO, COMMUTE_BRANCH, WORKER_NAME, KV_TITLE, TOKEN_TEMPLATE_URL;
13311
+ var COMMUTE_REPO, COMMUTE_BRANCH, WORKER_NAME, KV_TITLE, CF_API, TOKEN_TEMPLATE_URL;
13147
13312
  var init_deploy_commute = __esm({
13148
13313
  "src/remote/deploy-commute.ts"() {
13149
13314
  "use strict";
13150
13315
  init_config();
13316
+ init_version();
13151
13317
  COMMUTE_REPO = "https://github.com/sinameraji/kimiflare-commute.git";
13152
13318
  COMMUTE_BRANCH = "main";
13153
13319
  WORKER_NAME = "kimiflare-multi-agent";
13154
13320
  KV_TITLE = "kimiflare-multi-agent-OAUTH_KV";
13321
+ CF_API = "https://api.cloudflare.com/client/v4";
13155
13322
  TOKEN_TEMPLATE_URL = "https://dash.cloudflare.com/profile/api-tokens";
13156
13323
  }
13157
13324
  });
@@ -15588,7 +15755,7 @@ ${err instanceof Error ? err.message : err}`);
15588
15755
  cam.send("AssistantStreamStarted", { stream_id: sid });
15589
15756
  cam.send("AssistantTokenDelta", { stream_id: sid, token: "# Tearing down multi-agent\n\n" });
15590
15757
  try {
15591
- for await (const step of teardownCommute()) {
15758
+ for await (const step of teardownCommute({ workerName: cfg.workerName })) {
15592
15759
  const prefix = step.error ? "\u2717 " : step.done || step.ok ? "\u2713 " : "\xB7 ";
15593
15760
  cam.send("AssistantTokenDelta", { stream_id: sid, token: `${prefix}${step.message}
15594
15761
  ` });
@@ -20531,15 +20698,19 @@ import { useEffect as useEffect5, useState as useState9 } from "react";
20531
20698
  import { Box as Box11, Text as Text12 } from "ink";
20532
20699
  import Spinner5 from "ink-spinner";
20533
20700
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
20534
- function WorkerList({ workers, isSynthesizing }) {
20701
+ function WorkerList({ workers, isSynthesizing, narration }) {
20535
20702
  const theme = useTheme();
20536
- if (workers.length === 0) return null;
20703
+ if (workers.length === 0 && !narration) return null;
20537
20704
  const running = workers.filter((w) => w.status === "running").length;
20538
20705
  const completed = workers.filter((w) => w.status === "completed").length;
20539
20706
  const failed = workers.filter((w) => w.status === "failed").length;
20540
20707
  const pending = workers.filter((w) => w.status === "pending").length;
20541
20708
  const showSynthesis = isSynthesizing && running === 0;
20542
20709
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
20710
+ narration && /* @__PURE__ */ jsx13(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text12, { color: theme.info.color, italic: true, children: narration.split("\n").map((line, i) => /* @__PURE__ */ jsxs11(Text12, { children: [
20711
+ line,
20712
+ "\n"
20713
+ ] }, `narration-${i}`)) }) }),
20543
20714
  /* @__PURE__ */ jsx13(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: theme.accent, bold: true, children: [
20544
20715
  "Workers: ",
20545
20716
  pending > 0 ? `${pending} todo \xB7 ` : "",
@@ -20569,7 +20740,7 @@ function WorkerRow({ worker }) {
20569
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" });
20570
20741
  const statusLabel = worker.status === "pending" ? "todo" : worker.status === "running" ? "ongoing" : worker.status === "completed" ? "done" : "failed";
20571
20742
  const isDone = worker.status === "completed" || worker.status === "failed";
20572
- const visibleLogs = worker.status === "running" ? worker.logs.slice(-5) : worker.status === "pending" ? [] : worker.logs.slice(-8);
20743
+ const hasSteps = worker.steps && worker.steps.length > 0;
20573
20744
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginLeft: 2, children: [
20574
20745
  /* @__PURE__ */ jsx13(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { children: [
20575
20746
  statusIcon,
@@ -20602,7 +20773,37 @@ function WorkerRow({ worker }) {
20602
20773
  worker.error.slice(0, 60)
20603
20774
  ] }) : null
20604
20775
  ] }) }),
20605
- visibleLogs.length > 0 && /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", marginLeft: 4, children: visibleLogs.map((line, i) => /* @__PURE__ */ jsx13(Text12, { color: theme.muted?.color ?? theme.info.color, dimColor: true, children: line.slice(0, 120) }, `${worker.id}-log-${i}`)) })
20776
+ 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}`)) })
20778
+ ] });
20779
+ }
20780
+ function StepRow({ step, theme }) {
20781
+ if (step.status === "completed") {
20782
+ return /* @__PURE__ */ jsxs11(Text12, { color: theme.palette.success, children: [
20783
+ " ",
20784
+ "\u2713 ",
20785
+ /* @__PURE__ */ jsx13(Text12, { strikethrough: true, children: step.label })
20786
+ ] });
20787
+ }
20788
+ if (step.status === "active") {
20789
+ return /* @__PURE__ */ jsxs11(Text12, { color: theme.accent, bold: true, children: [
20790
+ " ",
20791
+ /* @__PURE__ */ jsx13(Spinner5, { type: "line" }),
20792
+ " ",
20793
+ step.label
20794
+ ] });
20795
+ }
20796
+ if (step.status === "failed") {
20797
+ return /* @__PURE__ */ jsxs11(Text12, { color: theme.palette.error, children: [
20798
+ " ",
20799
+ "\u2612 ",
20800
+ step.label
20801
+ ] });
20802
+ }
20803
+ return /* @__PURE__ */ jsxs11(Text12, { color: theme.muted?.color ?? theme.info.color, dimColor: true, children: [
20804
+ " ",
20805
+ "\u2610 ",
20806
+ step.label
20606
20807
  ] });
20607
20808
  }
20608
20809
  function formatElapsed6(ms) {
@@ -21222,14 +21423,14 @@ async function cfFetch(url, init) {
21222
21423
  }
21223
21424
  async function ensureStore(accountId, apiToken) {
21224
21425
  const listed = await cfFetch(
21225
- `${CF_API}/accounts/${encodeURIComponent(accountId)}/secrets_store/stores`,
21426
+ `${CF_API2}/accounts/${encodeURIComponent(accountId)}/secrets_store/stores`,
21226
21427
  { method: "GET", apiToken }
21227
21428
  );
21228
21429
  if (!listed.ok) return listed;
21229
21430
  const existing = listed.value.find((s) => s.name === STORE_NAME);
21230
21431
  if (existing) return { ok: true, value: existing.id };
21231
21432
  const created = await cfFetch(
21232
- `${CF_API}/accounts/${encodeURIComponent(accountId)}/secrets_store/stores`,
21433
+ `${CF_API2}/accounts/${encodeURIComponent(accountId)}/secrets_store/stores`,
21233
21434
  { method: "POST", apiToken, body: { name: STORE_NAME } }
21234
21435
  );
21235
21436
  if (!created.ok) return created;
@@ -21237,7 +21438,7 @@ async function ensureStore(accountId, apiToken) {
21237
21438
  }
21238
21439
  async function pushProviderKey(accountId, apiToken, storeId, name, value, comment = "kimi-code BYOK") {
21239
21440
  const res = await cfFetch(
21240
- `${CF_API}/accounts/${encodeURIComponent(accountId)}/secrets_store/stores/${encodeURIComponent(
21441
+ `${CF_API2}/accounts/${encodeURIComponent(accountId)}/secrets_store/stores/${encodeURIComponent(
21241
21442
  storeId
21242
21443
  )}/secrets`,
21243
21444
  {
@@ -21256,12 +21457,12 @@ async function pushProviderKey(accountId, apiToken, storeId, name, value, commen
21256
21457
  function aliasFor(provider) {
21257
21458
  return `kimi-code-${provider}`;
21258
21459
  }
21259
- var CF_API, STORE_NAME;
21460
+ var CF_API2, STORE_NAME;
21260
21461
  var init_secrets_store = __esm({
21261
21462
  "src/agent/secrets-store.ts"() {
21262
21463
  "use strict";
21263
21464
  init_version();
21264
- CF_API = "https://api.cloudflare.com/client/v4";
21465
+ CF_API2 = "https://api.cloudflare.com/client/v4";
21265
21466
  STORE_NAME = "kimi-code";
21266
21467
  }
21267
21468
  });
@@ -25241,13 +25442,14 @@ function MultiAgentModal({ initial, onSave, onDone, remoteWorkerUrl, remoteAuthS
25241
25442
  setDeploying(true);
25242
25443
  setDeployLog(["Starting tear-down\u2026"]);
25243
25444
  try {
25244
- for await (const step of teardownCommute()) {
25445
+ for await (const step of teardownCommute({ workerName: state.workerName })) {
25245
25446
  const prefix = step.error ? "\u2717 " : step.done || step.ok ? "\u2713 " : "\xB7 ";
25246
25447
  setDeployLog((l) => [...l, `${prefix}${step.message}`]);
25247
25448
  if (step.done) {
25248
25449
  persist({
25249
25450
  workerEndpoint: void 0,
25250
25451
  workerApiKey: void 0,
25452
+ workerName: void 0,
25251
25453
  multiAgentEnabled: false,
25252
25454
  autoExecute: false
25253
25455
  });
@@ -25259,7 +25461,7 @@ function MultiAgentModal({ initial, onSave, onDone, remoteWorkerUrl, remoteAuthS
25259
25461
  } finally {
25260
25462
  setDeploying(false);
25261
25463
  }
25262
- }, [persist]);
25464
+ }, [persist, state.workerName]);
25263
25465
  const beginEdit = useCallback7((f) => {
25264
25466
  if (f === "deploy") {
25265
25467
  void runDeploy2();
@@ -29772,6 +29974,7 @@ function App({
29772
29974
  const [lastSessionTopic, setLastSessionTopic] = useState27(null);
29773
29975
  const [activeWorkers, setActiveWorkers] = useState27([]);
29774
29976
  const [isSynthesizing, setIsSynthesizing] = useState27(false);
29977
+ const [coordinatorNarration, setCoordinatorNarration] = useState27("");
29775
29978
  useEffect11(() => {
29776
29979
  setGitBranch(detectGitBranch());
29777
29980
  }, []);
@@ -29851,6 +30054,7 @@ ${wcagWarnings.join("\n")}` }
29851
30054
  const mcpInitRef = useRef7(false);
29852
30055
  const submitRef = useRef7(() => {
29853
30056
  });
30057
+ const multiAgentAbortRef = useRef7(null);
29854
30058
  const lspManagerRef = useRef7(new LspManager());
29855
30059
  const lspToolsRef = useRef7([]);
29856
30060
  const lspInitRef = useRef7(false);
@@ -30234,6 +30438,11 @@ ${wcagWarnings.join("\n")}` }
30234
30438
  hasPerm: hasPendingPermission(),
30235
30439
  hasLimit: limitResolveRef.current !== null
30236
30440
  });
30441
+ if (multiAgentAbortRef.current) {
30442
+ multiAgentAbortRef.current.abort();
30443
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "cancelling multi-agent workers\u2026" }]);
30444
+ return;
30445
+ }
30237
30446
  const outcome = interruptOrExit(interruptDepsRef.current);
30238
30447
  if (!outcome.didInterruptTurn && !outcome.hadPermission && !outcome.hadLimit && !outcome.hadLoop) {
30239
30448
  logger.info("input:ctrl+c:exiting");
@@ -30243,10 +30452,18 @@ ${wcagWarnings.join("\n")}` }
30243
30452
  if (key.escape) {
30244
30453
  const now2 = Date.now();
30245
30454
  const modalOpen = perm !== null || limitModal !== null || loopModal !== null || showLspWizard || showCommandList || commandWizard !== null || commandToDelete !== null || resumeSessions !== null || checkpointSession !== null || showThemePicker;
30246
- if (!modalOpen && (busyRef.current || supervisorRef.current.isRunning) && activeScopeRef.current && !isAbortingRef.current && now2 - lastEscapeAtRef.current > 500) {
30247
- lastEscapeAtRef.current = now2;
30248
- interruptTurn(interruptDepsRef.current);
30249
- return;
30455
+ if (!modalOpen && !isAbortingRef.current && now2 - lastEscapeAtRef.current > 500) {
30456
+ if (multiAgentAbortRef.current) {
30457
+ lastEscapeAtRef.current = now2;
30458
+ multiAgentAbortRef.current.abort();
30459
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "cancelling multi-agent workers\u2026" }]);
30460
+ return;
30461
+ }
30462
+ if ((busyRef.current || supervisorRef.current.isRunning) && activeScopeRef.current) {
30463
+ lastEscapeAtRef.current = now2;
30464
+ interruptTurn(interruptDepsRef.current);
30465
+ return;
30466
+ }
30250
30467
  }
30251
30468
  }
30252
30469
  if (key.ctrl && inputChar === "r") {
@@ -30277,6 +30494,11 @@ ${wcagWarnings.join("\n")}` }
30277
30494
  hasLimit: limitResolveRef.current !== null,
30278
30495
  hasLoop: loopResolveRef.current !== null
30279
30496
  });
30497
+ if (multiAgentAbortRef.current) {
30498
+ multiAgentAbortRef.current.abort();
30499
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "cancelling multi-agent workers\u2026" }]);
30500
+ return;
30501
+ }
30280
30502
  const outcome = interruptOrExit({
30281
30503
  ...interruptDepsRef.current,
30282
30504
  skipPendingToolCleanup: true
@@ -30928,23 +31150,46 @@ ${wcagWarnings.join("\n")}` }
30928
31150
  ...e,
30929
31151
  { kind: "info", key: mkKey(), text: `multi-agent mode active, but task is ${classification.tier} \u2014 running locally` }
30930
31152
  ]);
31153
+ } else if (activeWorkers.length > 0) {
31154
+ setEvents((e) => [
31155
+ ...e,
31156
+ { kind: "info", key: mkKey(), text: `multi-agent workers already active (${activeWorkers.length}) \u2014 routing to coordinator` }
31157
+ ]);
30931
31158
  } else {
30932
31159
  setEvents((e) => [
30933
31160
  ...e,
30934
31161
  { kind: "info", key: mkKey(), text: "multi-agent mode: spawning parallel research workers..." }
30935
31162
  ]);
31163
+ setCoordinatorNarration("");
31164
+ const controller = new AbortController();
31165
+ multiAgentAbortRef.current = controller;
30936
31166
  try {
30937
31167
  const { plan, conflicts, recommendations, prUrl, executor } = await supervisorRef.current.autoSpawnWorkers(
30938
31168
  trimmed,
30939
31169
  `Current project: ${process.cwd()}`,
30940
31170
  (workers) => setActiveWorkers(workers),
30941
- (phase) => setIsSynthesizing(phase === "synthesizing")
31171
+ (phase) => setIsSynthesizing(phase === "synthesizing"),
31172
+ controller.signal,
31173
+ (text2) => setCoordinatorNarration(text2)
30942
31174
  );
31175
+ setCoordinatorNarration("");
30943
31176
  setEvents((e) => [
30944
31177
  ...e,
30945
31178
  { kind: "info", key: mkKey(), text: "workers completed \u2014 synthesizing findings" }
30946
31179
  ]);
30947
- messagesRef.current.push({ role: "system", content: plan });
31180
+ const asstId = mkAssistantId();
31181
+ setEvents((e) => [
31182
+ ...e,
31183
+ {
31184
+ kind: "assistant",
31185
+ key: `asst_${asstId}`,
31186
+ id: asstId,
31187
+ text: plan,
31188
+ reasoning: "",
31189
+ streaming: false
31190
+ }
31191
+ ]);
31192
+ messagesRef.current.push({ role: "assistant", content: plan });
30948
31193
  if (conflicts.length > 0) {
30949
31194
  setEvents((e) => [
30950
31195
  ...e,
@@ -30967,13 +31212,23 @@ ${conflicts.join("\n")}` }
30967
31212
  return;
30968
31213
  } catch (e) {
30969
31214
  setIsSynthesizing(false);
31215
+ setCoordinatorNarration("");
30970
31216
  const err = e;
30971
- setEvents((e2) => [
30972
- ...e2,
30973
- { kind: "error", key: mkKey(), text: `multi-agent spawn failed: ${err.message}` }
30974
- ]);
31217
+ if (err.message === "Cancelled by user") {
31218
+ setEvents((e2) => [
31219
+ ...e2,
31220
+ { kind: "info", key: mkKey(), text: "multi-agent cancelled" }
31221
+ ]);
31222
+ } else {
31223
+ setEvents((e2) => [
31224
+ ...e2,
31225
+ { kind: "error", key: mkKey(), text: `multi-agent spawn failed: ${err.message}` }
31226
+ ]);
31227
+ }
30975
31228
  endTurn();
30976
31229
  return;
31230
+ } finally {
31231
+ multiAgentAbortRef.current = null;
30977
31232
  }
30978
31233
  }
30979
31234
  }
@@ -31465,6 +31720,7 @@ ${conflicts.join("\n")}` }
31465
31720
  multiAgentEnabled: cfg.multiAgentEnabled,
31466
31721
  workerEndpoint: cfg.workerEndpoint,
31467
31722
  workerApiKey: cfg.workerApiKey,
31723
+ workerName: cfg.workerName,
31468
31724
  autoExecute: cfg.autoExecute
31469
31725
  } : void 0,
31470
31726
  onMultiAgentSave: async (patch) => {
@@ -31588,7 +31844,7 @@ ${conflicts.join("\n")}` }
31588
31844
  }
31589
31845
  }
31590
31846
  ) : /* @__PURE__ */ jsxs39(Box39, { flexDirection: "column", marginTop: 1, children: [
31591
- activeWorkers.length > 0 && /* @__PURE__ */ jsx41(WorkerList, { workers: activeWorkers, isSynthesizing }),
31847
+ (activeWorkers.length > 0 || coordinatorNarration) && /* @__PURE__ */ jsx41(WorkerList, { workers: activeWorkers, isSynthesizing, narration: coordinatorNarration }),
31592
31848
  tasks.length > 0 && /* @__PURE__ */ jsx41(
31593
31849
  TaskList,
31594
31850
  {