@joeyshi12/casper 0.15.6 → 0.16.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.
Files changed (68) hide show
  1. package/README.md +10 -6
  2. package/dist/mcp.js +1 -1
  3. package/dist/server.js +836 -723
  4. package/dist/web/assets/{abnfDiagram-N423BO3Z-jvSYyvNw.js → abnfDiagram-N423BO3Z-DQdd062N.js} +1 -1
  5. package/dist/web/assets/{arc-BiHYHdUQ.js → arc-WhOJ_KR1.js} +1 -1
  6. package/dist/web/assets/{architectureDiagram-T3A2C74G-4Eqc-u3C.js → architectureDiagram-T3A2C74G-3zZgBQZT.js} +1 -1
  7. package/dist/web/assets/{blockDiagram-VBNYF7ZC-BcOcmlBN.js → blockDiagram-VBNYF7ZC-CW9mWWLl.js} +1 -1
  8. package/dist/web/assets/{c4Diagram-5PPSVZJV-BUudyYqU.js → c4Diagram-5PPSVZJV-xXmfR_aG.js} +1 -1
  9. package/dist/web/assets/channel-QybFlytR.js +1 -0
  10. package/dist/web/assets/{chunk-2GRJ4B5K-bTMhoJSQ.js → chunk-2GRJ4B5K-CuCN8EG_.js} +1 -1
  11. package/dist/web/assets/{chunk-2Q5K7J3B-BYp6dfdp.js → chunk-2Q5K7J3B-BC-h61jv.js} +1 -1
  12. package/dist/web/assets/{chunk-5RXB4S5H-Dg3ASlI5.js → chunk-5RXB4S5H-BqZ4ICSN.js} +1 -1
  13. package/dist/web/assets/{chunk-5VM5RSS4-B7uwpCHf.js → chunk-5VM5RSS4-_EcX9Eg6.js} +1 -1
  14. package/dist/web/assets/{chunk-6Q2QTUOP-DVTiWdGW.js → chunk-6Q2QTUOP-Rjg4xt7f.js} +1 -1
  15. package/dist/web/assets/{chunk-GF5L2VYU-C2LdpbyU.js → chunk-GF5L2VYU-CllNzjfD.js} +1 -1
  16. package/dist/web/assets/{chunk-JWPE2WC7-CMX4-S8P.js → chunk-JWPE2WC7-Cc24s09e.js} +1 -1
  17. package/dist/web/assets/{chunk-KBJHAD2P-D92STFuF.js → chunk-KBJHAD2P-DYClY3b3.js} +1 -1
  18. package/dist/web/assets/{chunk-RYQCIY6F-B2PLxj_Q.js → chunk-RYQCIY6F-C5PSe7M_.js} +1 -1
  19. package/dist/web/assets/{chunk-XXDRQBXY-DCpI_0Ix.js → chunk-XXDRQBXY-DK8-XmXo.js} +1 -1
  20. package/dist/web/assets/classDiagram-JCYQIIEL-CcJ6l3H0.js +1 -0
  21. package/dist/web/assets/classDiagram-v2-OCEON4UE-CcJ6l3H0.js +1 -0
  22. package/dist/web/assets/{cose-bilkent-JH36ORCC-KtY-8vxF.js → cose-bilkent-JH36ORCC-Dxs4GA2q.js} +1 -1
  23. package/dist/web/assets/{cynefin-VYW2F7L2-DkTsGV_4.js → cynefin-VYW2F7L2-kzjViLcF.js} +1 -1
  24. package/dist/web/assets/{cynefinDiagram-MW4NZA55-BVPMHgGu.js → cynefinDiagram-MW4NZA55-B9zBzt3B.js} +1 -1
  25. package/dist/web/assets/{dagre-VZM6K2ZE-DT-S1NOU.js → dagre-VZM6K2ZE-BNwMkCZi.js} +1 -1
  26. package/dist/web/assets/{diagram-7IWD3JNH-DUJvV7wV.js → diagram-7IWD3JNH-DpIoQsS7.js} +1 -1
  27. package/dist/web/assets/{diagram-B4RE2ZJO-DmpnY7ob.js → diagram-B4RE2ZJO-BKWdzhKi.js} +1 -1
  28. package/dist/web/assets/{diagram-LBJQPF4R-nEMZNwen.js → diagram-LBJQPF4R-DBbKFigX.js} +1 -1
  29. package/dist/web/assets/{diagram-Q27KOJAE-Dr-59ElI.js → diagram-Q27KOJAE-B7qLPRw9.js} +1 -1
  30. package/dist/web/assets/{diagram-UB23O5K3-4wvmRauY.js → diagram-UB23O5K3-DVncy6q5.js} +1 -1
  31. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-055xAlHi.js → ebnfDiagram-BXEA7PRR-B9rA3ylb.js} +1 -1
  32. package/dist/web/assets/{erDiagram-JOGREHBK-DqWTDG8u.js → erDiagram-JOGREHBK-BExDcOc7.js} +1 -1
  33. package/dist/web/assets/{flowDiagram-UKHOOZJN-D3lUh0yb.js → flowDiagram-UKHOOZJN-B1xPLh9u.js} +1 -1
  34. package/dist/web/assets/{ganttDiagram-PKOTCBZU-aV5l2zrT.js → ganttDiagram-PKOTCBZU-BYS5NkM6.js} +1 -1
  35. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-BK2KCI35.js → gitGraphDiagram-DS77QQ5N-CN4An7lt.js} +1 -1
  36. package/dist/web/assets/{index-Dv1My9Ob.js → index-BuQTY976.js} +1 -1
  37. package/dist/web/assets/{index-B5ZDbS-R.js → index-BwBYAAwi.js} +48 -48
  38. package/dist/web/assets/{infoDiagram-6WML65LV-BcSXYnWm.js → infoDiagram-6WML65LV-sk97M0G1.js} +1 -1
  39. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-C0LFAfcz.js → ishikawaDiagram-WSZJBQD7-BzW2rVh6.js} +1 -1
  40. package/dist/web/assets/{journeyDiagram-NVQOT4AX-1Zs22Gpd.js → journeyDiagram-NVQOT4AX-iaCo98OS.js} +1 -1
  41. package/dist/web/assets/{kanban-definition-27J2QSJJ-DW6RwD7S.js → kanban-definition-27J2QSJJ-Ce38xzYm.js} +1 -1
  42. package/dist/web/assets/{linear-Cfzhrbed.js → linear-BoUX9N6W.js} +1 -1
  43. package/dist/web/assets/{mermaid.core-D0VPssrU.js → mermaid.core-CC54dZcH.js} +4 -4
  44. package/dist/web/assets/{mindmap-definition-FAOFIHXS-BeD5KiWO.js → mindmap-definition-FAOFIHXS-BxRXpNJ-.js} +1 -1
  45. package/dist/web/assets/{pegDiagram-VL7TDLO6-JDLyI4Gc.js → pegDiagram-VL7TDLO6-Cg0-J9c8.js} +1 -1
  46. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-BY3XpjEU.js → pieDiagram-7S7Q4E2Y-DUjXAJq4.js} +1 -1
  47. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-C4oT4Lqc.js → quadrantDiagram-CIZ2JOQS-q1HsW0pP.js} +1 -1
  48. package/dist/web/assets/{railroadDiagram-AXF67PYL-lfzvVyK7.js → railroadDiagram-AXF67PYL-ats1_veK.js} +1 -1
  49. package/dist/web/assets/{requirementDiagram-LRYGKXZP-wGnOD5SS.js → requirementDiagram-LRYGKXZP-CV2_uFpI.js} +1 -1
  50. package/dist/web/assets/{sankeyDiagram-W5VNT64P-DJQ2VW0G.js → sankeyDiagram-W5VNT64P-BwyC8qNn.js} +1 -1
  51. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-Bz_rjvWZ.js → sequenceDiagram-SI44F4Z6-72yj0uCA.js} +1 -1
  52. package/dist/web/assets/{sizeCapture-X5ZJPWSS-BgxIA36S.js → sizeCapture-X5ZJPWSS-CV7PeUrN.js} +1 -1
  53. package/dist/web/assets/{stateDiagram-OKZ733FA-DpL_B9Mu.js → stateDiagram-OKZ733FA-B08_WgjO.js} +1 -1
  54. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-80B2GltS.js +1 -0
  55. package/dist/web/assets/{swimlanes-SLNWSIFB-B6uTv46X.js → swimlanes-SLNWSIFB-DnBKpTQH.js} +2 -2
  56. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CeWA7IPZ.js +8 -0
  57. package/dist/web/assets/{timeline-definition-Z64GVDOM-DvMUcNNs.js → timeline-definition-Z64GVDOM-BAoT5Fu8.js} +1 -1
  58. package/dist/web/assets/{vennDiagram-T6HMQDX7-K_9U-J2A.js → vennDiagram-T6HMQDX7-VLzfwvza.js} +1 -1
  59. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-DwMi4kCd.js → wardleyDiagram-T6FBY63Y-QaCRHB2e.js} +1 -1
  60. package/dist/web/assets/{xychartDiagram-ELKLHX3M-BkcnShpk.js → xychartDiagram-ELKLHX3M-DCUP6k1x.js} +1 -1
  61. package/dist/web/index.html +1 -1
  62. package/dist/web/sw.js +1 -1
  63. package/package.json +1 -1
  64. package/dist/web/assets/channel-BueZNEdT.js +0 -1
  65. package/dist/web/assets/classDiagram-JCYQIIEL-CSEqsvyn.js +0 -1
  66. package/dist/web/assets/classDiagram-v2-OCEON4UE-CSEqsvyn.js +0 -1
  67. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-DsD9j9Fu.js +0 -1
  68. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-BxNKQaPC.js +0 -8
package/dist/server.js CHANGED
@@ -118,13 +118,9 @@ function agentConfig(prompt, mcp) {
118
118
  casper: mcp ? { command: process.execPath, args: [mcp], env: {}, timeout: 1e4 } : { command: "casper", args: ["mcp"], env: {}, timeout: 1e4 }
119
119
  },
120
120
  tools: ["*"],
121
- toolAliases: {},
122
- allowedTools: [],
123
121
  resources: [],
124
- hooks: {},
125
- toolsSettings: {},
126
- includeMcpJson: true,
127
- model: null
122
+ hooks: [],
123
+ includeMcpJson: true
128
124
  };
129
125
  }
130
126
  function installAgentFile(home2, dataDir) {
@@ -317,17 +313,18 @@ var init_config = __esm({
317
313
  kiroBin: resolveKiroBin(setting("KIRO_BIN", "kiroBin", "kiro-cli"), home),
318
314
  defaultCwd: setting("DEFAULT_CWD", "defaultCwd", process.cwd()),
319
315
  maxLiveSessions: settingInt("MAX_LIVE_SESSIONS", "maxLiveSessions", 6),
320
- /** The agent Casper installs, with the widget tools. Falls back to kiro_default
321
- * if missing rather than failing. */
316
+ /** The agent Casper installs, with the widget tools. kiro falls back to its Default
317
+ * mode if it is missing rather than failing. */
322
318
  defaultAgent: setting("DEFAULT_AGENT", "defaultAgent", "casper"),
323
319
  /** Filesystem root the file endpoints are confined to. Defaults to /; narrow it
324
320
  * to keep authenticated users out of system files such as /etc or SSH keys. */
325
321
  fileRoot: path5.resolve(setting("CASPER_FILE_ROOT", "fileRoot", "/")),
322
+ /** kiro's session store, one directory per workspace. */
326
323
  kiroSessionsDir: path5.resolve(
327
324
  setting(
328
325
  "CASPER_KIRO_SESSIONS_DIR",
329
326
  "kiroSessionsDir",
330
- path5.join(home, ".kiro", "sessions", "cli")
327
+ path5.join(home, ".kiro", "sessions")
331
328
  )
332
329
  ),
333
330
  casperDataDir: dataDirPath(),
@@ -403,13 +400,6 @@ CREATE TABLE IF NOT EXISTS turn_endings (
403
400
  items TEXT NOT NULL
404
401
  );
405
402
  CREATE INDEX IF NOT EXISTS turn_endings_chat ON turn_endings (chat_id);
406
- CREATE TABLE IF NOT EXISTS subagent_links (
407
- child_session_id TEXT PRIMARY KEY,
408
- parent_session_id TEXT NOT NULL,
409
- tool_call_id TEXT NOT NULL,
410
- stage_name TEXT NOT NULL
411
- );
412
- CREATE INDEX IF NOT EXISTS subagent_links_parent ON subagent_links (parent_session_id);
413
403
  `;
414
404
  }
415
405
  });
@@ -639,10 +629,22 @@ function isJsonRpcError(m) {
639
629
  function isJsonRpcNotification(m) {
640
630
  return !("id" in m) && "method" in m;
641
631
  }
642
- var ACP_METHODS, KIRO_NOTIFICATIONS;
632
+ function kiroToolName(call) {
633
+ const meta = call._meta?.kiro;
634
+ if (meta?.kind === "agent-subtask")
635
+ return SUBAGENT_TOOL;
636
+ const server = meta?.serverName;
637
+ if (!server)
638
+ return void 0;
639
+ const title = call.title ?? "";
640
+ const prefix = `@${server}/`;
641
+ return `${server}/${title.startsWith(prefix) ? title.slice(prefix.length) : title}`;
642
+ }
643
+ var SUBAGENT_TOOL, ACP_METHODS, KIRO_AGENT_NOT_FOUND;
643
644
  var init_acp = __esm({
644
645
  "shared/dist/acp.js"() {
645
646
  "use strict";
647
+ SUBAGENT_TOOL = "invoke_sub_agent";
646
648
  ACP_METHODS = {
647
649
  initialize: "initialize",
648
650
  sessionNew: "session/new",
@@ -650,19 +652,12 @@ var init_acp = __esm({
650
652
  sessionPrompt: "session/prompt",
651
653
  sessionCancel: "session/cancel",
652
654
  sessionSetMode: "session/set_mode",
653
- sessionSetModel: "session/set_model",
655
+ sessionSetConfigOption: "session/set_config_option",
654
656
  sessionUpdate: "session/update",
655
- commandsExecute: "_kiro.dev/commands/execute"
656
- };
657
- KIRO_NOTIFICATIONS = {
658
- metadata: "_kiro.dev/metadata",
659
- subagentListUpdate: "_kiro.dev/subagent/list_update",
660
- mcpServerInitialized: "_kiro.dev/mcp/server_initialized",
661
- mcpServerInitFailure: "_kiro.dev/mcp/server_init_failure",
662
- commandsAvailable: "_kiro.dev/commands/available",
663
- mcpOauthRequest: "_kiro.dev/mcp/oauth_request",
664
- compactionStatus: "_kiro.dev/compaction/status"
657
+ requestPermission: "session/request_permission",
658
+ compact: "_kiro/session/compact"
665
659
  };
660
+ KIRO_AGENT_NOT_FOUND = "_kiro/customAgent/not_found";
666
661
  }
667
662
  });
668
663
 
@@ -759,7 +754,7 @@ function foldSessionUpdate(draft, u, seq, ts) {
759
754
  }
760
755
  if (u.sessionUpdate === "tool_call") {
761
756
  const tc = u;
762
- const toolName = tc._meta?.kiro?.toolName;
757
+ const toolName = kiroToolName(tc);
763
758
  return {
764
759
  items: [
765
760
  ...commitDraft(draft, `s-${seq}`, ts),
@@ -801,6 +796,7 @@ var emptyDraft;
801
796
  var init_turnFold = __esm({
802
797
  "shared/dist/turnFold.js"() {
803
798
  "use strict";
799
+ init_acp();
804
800
  emptyDraft = () => ({ items: [], streamingText: "", streamingThought: "" });
805
801
  }
806
802
  });
@@ -831,10 +827,15 @@ import { execFile } from "node:child_process";
831
827
  import { promisify } from "node:util";
832
828
  function invalidateAgents() {
833
829
  cache = null;
830
+ liveModes = null;
831
+ }
832
+ function rememberModes(modes) {
833
+ if (modes.length > 0) liveModes = modes;
834
834
  }
835
835
  async function listAgents() {
836
+ if (liveModes) return liveModes;
836
837
  if (cache && Date.now() - cache.at < CACHE_TTL_MS) return cache.agents;
837
- const found = /* @__PURE__ */ new Map();
838
+ const found = /* @__PURE__ */ new Map([[DEFAULT_MODE.id, DEFAULT_MODE]]);
838
839
  try {
839
840
  const { stdout, stderr } = await execFileAsync(config.kiroBin, ["agent", "list"], {
840
841
  cwd: config.defaultCwd,
@@ -843,30 +844,26 @@ async function listAgents() {
843
844
  const text2 = (stderr || "") + "\n" + (stdout || "");
844
845
  for (const rawLine of text2.split("\n")) {
845
846
  const line = rawLine.replace(ANSI, "");
846
- const m = /^\s{0,2}(\*\s)?([A-Za-z0-9_-]+)\s{2,}(\(Built-in\)|Global|Workspace|Local)(?:\s|$)/.exec(
847
- line
848
- );
847
+ const m = /^\s{0,2}(\*\s)?([A-Za-z0-9_-]+)\s{2,}(Global|Workspace|Local)(?:\s|$)/.exec(line);
849
848
  if (!m) continue;
850
849
  const id = m[2];
851
850
  if (!found.has(id)) found.set(id, { id, name: id });
852
851
  }
853
852
  } catch {
854
853
  }
855
- const agents = found.size > 0 ? [...found.values()] : FALLBACK;
856
- cache = { at: Date.now(), agents };
854
+ cache = { at: Date.now(), agents: [...found.values()] };
857
855
  return cache.agents;
858
856
  }
859
- var execFileAsync, FALLBACK, CACHE_TTL_MS, cache, ANSI;
857
+ var execFileAsync, DEFAULT_MODE, CACHE_TTL_MS, cache, liveModes, ANSI;
860
858
  var init_agents = __esm({
861
859
  "server/src/session/agents.ts"() {
862
860
  "use strict";
863
861
  init_config();
864
862
  execFileAsync = promisify(execFile);
865
- FALLBACK = [
866
- { id: "kiro_default", name: "kiro_default", description: "General-purpose Kiro agent" }
867
- ];
863
+ DEFAULT_MODE = { id: "vibe", name: "Default", description: "General coding assistance" };
868
864
  CACHE_TTL_MS = 15e3;
869
865
  cache = null;
866
+ liveModes = null;
870
867
  ANSI = /\x1b\[[0-9;]*m/g;
871
868
  }
872
869
  });
@@ -1072,6 +1069,10 @@ var init_AcpClient = __esm({
1072
1069
  if (this.closed) return;
1073
1070
  this.write({ jsonrpc: "2.0", id, result });
1074
1071
  }
1072
+ respondError(id, code, message) {
1073
+ if (this.closed) return;
1074
+ this.write({ jsonrpc: "2.0", id, error: { code, message } });
1075
+ }
1075
1076
  write(msg) {
1076
1077
  this.writable.write(JSON.stringify(msg) + "\n");
1077
1078
  }
@@ -1093,7 +1094,12 @@ var init_AcpClient = __esm({
1093
1094
  import { spawn } from "node:child_process";
1094
1095
  import { EventEmitter as EventEmitter3 } from "node:events";
1095
1096
  import split22 from "split2";
1096
- var STDERR_KEEP, KiroProcess;
1097
+ function permissionOutcome(params) {
1098
+ const options = params?.options ?? [];
1099
+ const pick = options.find((o) => o.kind === "allow_once") ?? options.find((o) => o.kind === "reject_once");
1100
+ return pick ? { outcome: { outcome: "selected", optionId: pick.optionId } } : { outcome: { outcome: "cancelled" } };
1101
+ }
1102
+ var STDERR_KEEP, COMPACT_TIMEOUT_MS, KiroProcess;
1097
1103
  var init_KiroProcess = __esm({
1098
1104
  "server/src/session/KiroProcess.ts"() {
1099
1105
  "use strict";
@@ -1101,18 +1107,19 @@ var init_KiroProcess = __esm({
1101
1107
  init_config();
1102
1108
  init_AcpClient();
1103
1109
  STDERR_KEEP = 8;
1110
+ COMPACT_TIMEOUT_MS = 10 * 6e4;
1104
1111
  KiroProcess = class extends EventEmitter3 {
1105
1112
  client;
1106
1113
  child;
1107
1114
  log;
1108
1115
  disposed = false;
1109
1116
  recentStderr = [];
1117
+ // kiro's optional methods, as advertised by initialize.
1118
+ extensionMethods = /* @__PURE__ */ new Set();
1110
1119
  constructor(opts, log) {
1111
1120
  super();
1112
1121
  this.log = log;
1113
- const args = ["acp", "--trust-all-tools"];
1114
- if (opts.agent) args.push("--agent", opts.agent);
1115
- if (opts.model) args.push("--model", opts.model);
1122
+ const args = ["acp", "--agent-engine", "v3"];
1116
1123
  this.log.info({ bin: config.kiroBin, args, cwd: opts.cwd }, "spawning kiro-cli acp");
1117
1124
  this.child = spawn(config.kiroBin, args, {
1118
1125
  cwd: opts.cwd,
@@ -1124,8 +1131,12 @@ var init_KiroProcess = __esm({
1124
1131
  this.emit("notification", n);
1125
1132
  });
1126
1133
  this.client.on("serverRequest", (req) => {
1127
- this.log.debug({ method: req.method }, "acp: unhandled server request");
1128
- this.client.respond(req.id, {});
1134
+ if (req.method === ACP_METHODS.requestPermission) {
1135
+ this.client.respond(req.id, permissionOutcome(req.params));
1136
+ return;
1137
+ }
1138
+ this.log.debug({ method: req.method }, "acp: unsupported request from kiro");
1139
+ this.client.respondError(req.id, -32601, `Method not found: ${req.method}`);
1129
1140
  });
1130
1141
  const stderrLines = this.child.stderr.pipe(split22());
1131
1142
  stderrLines.on("data", (line) => {
@@ -1157,9 +1168,9 @@ var init_KiroProcess = __esm({
1157
1168
  this.child.stdout.on("error", () => {
1158
1169
  });
1159
1170
  }
1160
- /** The ACP initialize handshake. kiro's reply is not used. */
1171
+ /** The ACP initialize handshake. Keeps the optional methods kiro advertises. */
1161
1172
  async initialize() {
1162
- await this.client.request(ACP_METHODS.initialize, {
1173
+ const res = await this.client.request(ACP_METHODS.initialize, {
1163
1174
  protocolVersion: 1,
1164
1175
  clientCapabilities: {
1165
1176
  fs: { readTextFile: false, writeTextFile: false },
@@ -1167,6 +1178,10 @@ var init_KiroProcess = __esm({
1167
1178
  },
1168
1179
  clientInfo: { name: "casper", version: "0.5.0" }
1169
1180
  });
1181
+ const methods = res?.agentCapabilities?._meta?.kiro?.extensionMethods;
1182
+ this.extensionMethods = new Set(
1183
+ Array.isArray(methods) ? methods.filter((m) => typeof m === "string") : []
1184
+ );
1170
1185
  }
1171
1186
  newSession(params) {
1172
1187
  return this.client.request(ACP_METHODS.sessionNew, params);
@@ -1189,14 +1204,19 @@ var init_KiroProcess = __esm({
1189
1204
  return this.client.request(ACP_METHODS.sessionSetMode, { sessionId, modeId });
1190
1205
  }
1191
1206
  setModel(sessionId, modelId) {
1192
- return this.client.request(ACP_METHODS.sessionSetModel, { sessionId, modelId });
1193
- }
1194
- execCommand(sessionId, command) {
1195
- return this.client.request(ACP_METHODS.commandsExecute, {
1207
+ return this.client.request(ACP_METHODS.sessionSetConfigOption, {
1196
1208
  sessionId,
1197
- command: { command: command.replace(/^\//, ""), args: {} }
1209
+ configId: "model",
1210
+ value: modelId
1198
1211
  });
1199
1212
  }
1213
+ /** Summarises the conversation so far. Resolves once kiro has finished. */
1214
+ compact(sessionId) {
1215
+ if (!this.extensionMethods.has(ACP_METHODS.compact)) {
1216
+ return Promise.reject(new Error("This kiro-cli does not offer compaction"));
1217
+ }
1218
+ return this.client.request(ACP_METHODS.compact, { sessionId }, COMPACT_TIMEOUT_MS);
1219
+ }
1200
1220
  /** Recent stderr from kiro, for attaching to a failure. */
1201
1221
  stderrTail() {
1202
1222
  return this.recentStderr.join("\n").trim();
@@ -1418,287 +1438,383 @@ var init_SubagentTracker = __esm({
1418
1438
  "server/src/session/SubagentTracker.ts"() {
1419
1439
  "use strict";
1420
1440
  SubagentTracker = class {
1421
- byChild = /* @__PURE__ */ new Map();
1422
- pendingCalls = [];
1423
- callStarted(toolCallId, stages) {
1424
- this.pendingCalls.push({ toolCallId, stages });
1425
- }
1426
- callFinished(toolCallId) {
1427
- const i = this.pendingCalls.findIndex((c) => c.toolCallId === toolCallId);
1428
- if (i >= 0) this.pendingCalls.splice(i, 1);
1429
- }
1430
- callFor(stageName) {
1431
- for (let i = this.pendingCalls.length - 1; i >= 0; i--) {
1432
- if (this.pendingCalls[i].stages.some((s) => s.name === stageName)) return this.pendingCalls[i];
1441
+ constructor(now = () => (/* @__PURE__ */ new Date()).toISOString()) {
1442
+ this.now = now;
1443
+ }
1444
+ byId = /* @__PURE__ */ new Map();
1445
+ /** The tool call that starts a subagent arrived. */
1446
+ started(subagentId, toolCallId, name) {
1447
+ if (this.byId.has(subagentId)) return false;
1448
+ const at = this.now();
1449
+ this.byId.set(subagentId, {
1450
+ sessionId: subagentId,
1451
+ stageName: name,
1452
+ toolCallId,
1453
+ status: "working",
1454
+ createdAt: at,
1455
+ updatedAt: at
1456
+ });
1457
+ return true;
1458
+ }
1459
+ /** One of the subagent's own updates arrived. `activity` is the title of a tool call it
1460
+ * started, which becomes the row's "what it is doing now". */
1461
+ progressed(subagentId, activity) {
1462
+ const s = this.byId.get(subagentId);
1463
+ if (!s || s.status !== "working") return false;
1464
+ s.updatedAt = this.now();
1465
+ if (!activity || activity === s.activity) return false;
1466
+ s.activity = activity;
1467
+ return true;
1468
+ }
1469
+ /** The call that started a subagent finished. */
1470
+ finished(toolCallId, ok2) {
1471
+ for (const s of this.byId.values()) {
1472
+ if (s.toolCallId !== toolCallId || s.status !== "working") continue;
1473
+ s.status = ok2 ? "completed" : "failed";
1474
+ s.activity = void 0;
1475
+ s.updatedAt = this.now();
1476
+ return true;
1433
1477
  }
1434
- return void 0;
1478
+ return false;
1435
1479
  }
1436
- /** Folds a list_update into the tracked state, returning links newly resolved
1437
- * this call, so the caller persists each one once. */
1438
- apply(subagents, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
1439
- const resolved = [];
1440
- for (const info of subagents) {
1441
- const existing = this.byChild.get(info.sessionId);
1442
- const nowIso = now();
1443
- const toolCallId = existing?.toolCallId ?? this.callFor(info.sessionName)?.toolCallId;
1444
- if (toolCallId && !existing?.toolCallId) {
1445
- resolved.push({ sessionId: info.sessionId, toolCallId, stageName: info.sessionName });
1446
- }
1447
- this.byChild.set(info.sessionId, {
1448
- sessionId: info.sessionId,
1449
- stageName: info.sessionName,
1450
- toolCallId,
1451
- status: info.status.type === "terminated" ? "completed" : "working",
1452
- activity: info.status.message,
1453
- createdAt: existing?.createdAt ?? nowIso,
1454
- updatedAt: nowIso
1455
- });
1480
+ /** Called when the parent turn ends. The turn waits on its subagents, so any still
1481
+ * working was stopped with it, and kiro does not always report that. */
1482
+ turnEnded() {
1483
+ let changed = false;
1484
+ for (const s of this.byId.values()) {
1485
+ if (s.status !== "working") continue;
1486
+ s.status = "failed";
1487
+ s.activity = "Stopped";
1488
+ s.updatedAt = this.now();
1489
+ changed = true;
1456
1490
  }
1457
- return resolved;
1458
- }
1459
- /** Started stages plus a synthetic 'pending' row for each declared stage that
1460
- * hasn't started, so a stage waiting on a dependency still shows up. */
1461
- listForCall(toolCallId) {
1462
- const call = this.pendingCalls.find((c) => c.toolCallId === toolCallId);
1463
- const started = [...this.byChild.values()].filter((s) => s.toolCallId === toolCallId);
1464
- if (!call) return started.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1465
- const startedNames = new Set(started.map((s) => s.stageName));
1466
- const pending = call.stages.filter((s) => !startedNames.has(s.name)).map((s) => ({
1467
- sessionId: `pending:${toolCallId}:${s.name}`,
1468
- stageName: s.name,
1469
- toolCallId,
1470
- status: "pending",
1471
- activity: s.depends_on?.length ? `Waits for ${s.depends_on.join(" and ")}` : void 0,
1472
- createdAt: "",
1473
- updatedAt: ""
1474
- }));
1475
- return [...started.sort((a, b) => a.createdAt.localeCompare(b.createdAt)), ...pending];
1491
+ return changed;
1476
1492
  }
1493
+ /** Copies, so a recorded event keeps the state it was recorded with. */
1477
1494
  list() {
1478
- return [...this.byChild.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1495
+ return [...this.byId.values()].map((s) => ({ ...s })).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1479
1496
  }
1480
- get(sessionId) {
1481
- return this.byChild.get(sessionId);
1497
+ get(subagentId) {
1498
+ const s = this.byId.get(subagentId);
1499
+ return s && { ...s };
1482
1500
  }
1483
1501
  };
1484
1502
  }
1485
1503
  });
1486
1504
 
1487
- // server/src/session/subagentResult.ts
1488
- function showSummaryAsAnswer(transcript) {
1489
- const out = [];
1490
- for (const item of transcript) {
1491
- if (item.type === "message" && item.message.role === "assistant" && item.message.text.trim() === INTERRUPTED) {
1492
- continue;
1493
- }
1494
- if (item.type === "tool_call" && item.tool.name === "summary") {
1495
- const input = item.tool.input;
1496
- const result = typeof input?.taskResult === "string" ? input.taskResult.trim() : "";
1497
- if (result) out.push({ type: "message", message: { id: item.tool.id, role: "assistant", text: result } });
1498
- continue;
1499
- }
1500
- out.push(item);
1501
- }
1502
- return out;
1503
- }
1504
- var INTERRUPTED;
1505
- var init_subagentResult = __esm({
1506
- "server/src/session/subagentResult.ts"() {
1507
- "use strict";
1508
- INTERRUPTED = "Tool uses were interrupted, waiting for the next user prompt";
1509
- }
1510
- });
1511
-
1512
- // server/src/session/subagentFallbackMatch.ts
1513
- function isStageInput(v) {
1514
- return typeof v === "object" && v !== null && typeof v.name === "string";
1515
- }
1516
- function subagentCallInput(tool) {
1517
- if (tool.name !== "subagent") return null;
1518
- const input = tool.input;
1519
- if (!input || !Array.isArray(input.stages)) return null;
1520
- const stages = input.stages.filter(isStageInput);
1521
- return { task: typeof input.task === "string" ? input.task : void 0, stages };
1522
- }
1523
- function expectedTitle(call, stage) {
1524
- const task = call.task ?? "";
1525
- const expanded = stage.prompt_template ? stage.prompt_template.replace("{task}", task) : task;
1526
- return expanded.slice(0, TITLE_MAX_LENGTH);
1527
- }
1528
- function subagentCallsIn(transcript) {
1529
- const calls = [];
1530
- transcript.forEach((item, itemIndex) => {
1531
- if (item.type !== "tool_call") return;
1532
- const input = subagentCallInput(item.tool);
1533
- if (!input) return;
1534
- calls.push({ toolCallId: item.tool.id, itemIndex, input });
1535
- });
1536
- return calls;
1537
- }
1538
- function matchSubagentsFallback(calls, children) {
1539
- const sortedCalls = [...calls].sort((a, b) => a.itemIndex - b.itemIndex);
1540
- const unclaimed = [...children].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1541
- const matches = [];
1542
- for (const call of sortedCalls) {
1543
- for (const stage of call.input.stages) {
1544
- const want = expectedTitle(call.input, stage);
1545
- if (!want) continue;
1546
- const i = unclaimed.findIndex((c) => want.startsWith(c.title) || c.title.startsWith(want));
1547
- if (i < 0) continue;
1548
- const child = unclaimed.splice(i, 1)[0];
1549
- matches.push({ sessionId: child.sessionId, toolCallId: call.toolCallId, stageName: stage.name });
1550
- }
1551
- }
1552
- return matches;
1553
- }
1554
- var TITLE_MAX_LENGTH;
1555
- var init_subagentFallbackMatch = __esm({
1556
- "server/src/session/subagentFallbackMatch.ts"() {
1557
- "use strict";
1558
- TITLE_MAX_LENGTH = 150;
1559
- }
1560
- });
1561
-
1562
- // server/src/session/kiroFiles.ts
1505
+ // server/src/session/kiroV3Files.ts
1563
1506
  import fs9 from "node:fs/promises";
1564
1507
  import path10 from "node:path";
1565
- function isContentBlock(v) {
1566
- return isRecord(v) && typeof v.kind === "string";
1567
- }
1568
- function toolUseData(b) {
1569
- if (b.kind !== "toolUse" || !isRecord(b.data)) return null;
1570
- const { toolUseId, name, input } = b.data;
1571
- if (typeof toolUseId !== "string") return null;
1572
- return { toolUseId, name: typeof name === "string" ? name : void 0, input };
1573
- }
1574
- function toolResultData(b) {
1575
- if (b.kind !== "toolResult" || !isRecord(b.data)) return null;
1576
- const { toolUseId, status, content } = b.data;
1577
- if (typeof toolUseId !== "string") return null;
1578
- return { toolUseId, status: typeof status === "string" ? status : void 0, content };
1579
- }
1580
- function contentBlocks(content) {
1581
- return Array.isArray(content) ? content.filter(isContentBlock) : [];
1582
- }
1583
- function blockText(c) {
1584
- if (typeof c.data === "string") return c.data;
1585
- if (isRecord(c.data) && typeof c.data.text === "string") return c.data.text;
1586
- return "";
1508
+ function iso(value) {
1509
+ if (!value) return void 0;
1510
+ const ms = Date.parse(value);
1511
+ return Number.isNaN(ms) ? value : new Date(ms).toISOString();
1587
1512
  }
1588
- function renderableBlocks(content) {
1589
- return contentBlocks(content).filter((b) => b.kind !== "image");
1513
+ async function workspaceDirs() {
1514
+ const root = config.kiroSessionsDir;
1515
+ try {
1516
+ const entries = await fs9.readdir(root, { withFileTypes: true });
1517
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== V2_STORE).map((e) => path10.join(root, e.name));
1518
+ } catch {
1519
+ return [];
1520
+ }
1590
1521
  }
1591
- function summarize(j) {
1592
- const state = j.session_state;
1593
- const turns = state?.conversation_metadata?.user_turn_metadatas ?? [];
1594
- const contextUsagePercentage = state?.rts_model_state?.context_usage_percentage ?? turns[turns.length - 1]?.context_usage_percentage;
1595
- return {
1596
- sessionId: j.session_id,
1597
- title: j.title?.trim() ?? "",
1598
- cwd: j.cwd,
1599
- createdAt: j.created_at,
1600
- updatedAt: j.updated_at,
1601
- agentId: state?.agent_name,
1602
- modelId: state?.rts_model_state?.model_info?.model_id,
1603
- contextUsagePercentage
1604
- };
1522
+ async function v3SessionDirs() {
1523
+ const out = /* @__PURE__ */ new Map();
1524
+ await Promise.all(
1525
+ (await workspaceDirs()).map(async (dir) => {
1526
+ try {
1527
+ for (const e of await fs9.readdir(dir, { withFileTypes: true })) {
1528
+ if (e.isDirectory() && !e.name.startsWith(".") && isValidSessionId(e.name)) {
1529
+ out.set(e.name, path10.join(dir, e.name));
1530
+ }
1531
+ }
1532
+ } catch {
1533
+ }
1534
+ })
1535
+ );
1536
+ return out;
1605
1537
  }
1606
- async function deletePersistedSession(sessionId) {
1607
- if (!isValidSessionId(sessionId)) return;
1608
- const targets = [
1609
- path10.join(config.kiroSessionsDir, `${sessionId}.json`),
1610
- path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`),
1611
- path10.join(config.kiroSessionsDir, `${sessionId}.history`),
1612
- path10.join(config.kiroSessionsDir, `${sessionId}.lock`),
1613
- path10.join(config.kiroSessionsDir, sessionId)
1614
- ];
1615
- await Promise.all(targets.map((f) => fs9.rm(f, { recursive: true, force: true })));
1538
+ async function v3SessionDir(sessionId) {
1539
+ if (!isValidSessionId(sessionId)) return void 0;
1540
+ const found = await Promise.all(
1541
+ (await workspaceDirs()).map(async (dir) => {
1542
+ const candidate = path10.join(dir, sessionId);
1543
+ try {
1544
+ await fs9.access(path10.join(candidate, "session.json"));
1545
+ return candidate;
1546
+ } catch {
1547
+ return void 0;
1548
+ }
1549
+ })
1550
+ );
1551
+ return found.find((d) => d !== void 0);
1616
1552
  }
1617
- async function readPersistedSession(sessionId) {
1618
- if (!isValidSessionId(sessionId)) return null;
1553
+ function contextUsageOf(line) {
1554
+ if (!line.includes('"contextUsage"')) return void 0;
1619
1555
  try {
1620
- const raw = await fs9.readFile(
1621
- path10.join(config.kiroSessionsDir, `${sessionId}.json`),
1622
- "utf8"
1623
- );
1624
- return summarize(JSON.parse(raw));
1556
+ const p = JSON.parse(line).payload;
1557
+ const value = isRecord(p.value) ? p.value.usagePercentage : void 0;
1558
+ return p.type === "session_metadata" && p.key === "contextUsage" && typeof value === "number" ? value : void 0;
1625
1559
  } catch {
1626
- return null;
1560
+ return void 0;
1627
1561
  }
1628
1562
  }
1629
- async function withLastWrite(child) {
1563
+ async function lastContextUsage(file) {
1564
+ let handle2;
1630
1565
  try {
1631
- const st = await fs9.stat(path10.join(config.kiroSessionsDir, `${child.sessionId}.jsonl`));
1632
- const mtime = st.mtime.toISOString();
1633
- return mtime > child.updatedAt ? { ...child, updatedAt: mtime } : child;
1566
+ handle2 = await fs9.open(file, "r");
1634
1567
  } catch {
1635
- return child;
1568
+ return void 0;
1636
1569
  }
1637
- }
1638
- async function readChildSession(childSessionId, parentSessionId) {
1639
- if (!isValidSessionId(childSessionId)) return null;
1640
1570
  try {
1641
- const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, `${childSessionId}.json`), "utf8");
1642
- const j = JSON.parse(raw);
1643
- return j.parent_session_id === parentSessionId ? await withLastWrite({ ...summarize(j), parentSessionId }) : null;
1644
- } catch {
1645
- return null;
1571
+ const { size } = await handle2.stat();
1572
+ let end = size;
1573
+ let tail = "";
1574
+ while (end > 0 && size - end < CONTEXT_TAIL_BYTES) {
1575
+ const start = Math.max(0, end - CONTEXT_CHUNK_BYTES);
1576
+ const buf = Buffer.alloc(end - start);
1577
+ await handle2.read(buf, 0, buf.length, start);
1578
+ end = start;
1579
+ const lines = (buf.toString("utf8") + tail).split("\n");
1580
+ tail = end > 0 ? lines.shift() : "";
1581
+ for (let i = lines.length - 1; i >= 0; i--) {
1582
+ const value = contextUsageOf(lines[i]);
1583
+ if (value !== void 0) return value;
1584
+ }
1585
+ }
1586
+ return tail ? contextUsageOf(tail) : void 0;
1587
+ } finally {
1588
+ await handle2.close();
1646
1589
  }
1647
1590
  }
1648
- async function listChildSessions(parentSessionId) {
1649
- if (!isValidSessionId(parentSessionId)) return [];
1650
- let names;
1591
+ async function readV3Session(dir) {
1592
+ let j;
1651
1593
  try {
1652
- names = await fs9.readdir(config.kiroSessionsDir);
1594
+ j = JSON.parse(await fs9.readFile(path10.join(dir, "session.json"), "utf8"));
1653
1595
  } catch {
1654
- return [];
1596
+ return null;
1655
1597
  }
1656
- const out = [];
1657
- await Promise.all(
1658
- names.filter((n) => n.endsWith(".json")).map(async (n) => {
1659
- try {
1660
- const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, n), "utf8");
1661
- const j = JSON.parse(raw);
1662
- if (j.parent_session_id === parentSessionId) {
1663
- out.push(await withLastWrite({ ...summarize(j), parentSessionId }));
1664
- }
1665
- } catch {
1666
- }
1667
- })
1668
- );
1669
- return out;
1670
- }
1671
- async function promptCount(sessionId) {
1672
- const entries = await readJsonlEntries(sessionId);
1673
- return entries.filter((e) => e.kind === "Prompt").length;
1598
+ const title = j.title?.trim() ?? "";
1599
+ const createdAt = iso(j.createdAt) ?? (/* @__PURE__ */ new Date(0)).toISOString();
1600
+ return {
1601
+ sessionId: j.id,
1602
+ title: title === PLACEHOLDER_TITLE ? "" : title,
1603
+ cwd: j.workspacePaths?.[0] ?? "",
1604
+ createdAt,
1605
+ updatedAt: iso(j.lastModifiedAt) ?? createdAt,
1606
+ agentId: j.agentMode,
1607
+ modelId: j.modelId,
1608
+ contextUsagePercentage: await lastContextUsage(path10.join(dir, "messages.jsonl"))
1609
+ };
1674
1610
  }
1675
- async function readJsonlEntries(sessionId) {
1676
- if (!isValidSessionId(sessionId)) return [];
1611
+ async function readLines(file, filter) {
1677
1612
  let raw;
1678
1613
  try {
1679
- raw = await fs9.readFile(path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`), "utf8");
1614
+ raw = await fs9.readFile(file, "utf8");
1680
1615
  } catch {
1681
1616
  return [];
1682
1617
  }
1683
- const entries = [];
1618
+ const out = [];
1684
1619
  for (const line of raw.split("\n")) {
1685
- const trimmed = line.trim();
1686
- if (!trimmed) continue;
1620
+ if (!line.trim() || filter && !line.includes(filter)) continue;
1687
1621
  try {
1688
- entries.push(JSON.parse(trimmed));
1622
+ const parsed = JSON.parse(line);
1623
+ if (isRecord(parsed) && isRecord(parsed.payload) && typeof parsed.id === "string") out.push(parsed);
1689
1624
  } catch {
1690
1625
  }
1691
1626
  }
1692
- return entries;
1627
+ return out;
1693
1628
  }
1694
- async function hasRecordedTurns(sessionId) {
1695
- if (!isValidSessionId(sessionId)) return false;
1696
- try {
1697
- const st = await fs9.stat(path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`));
1698
- return st.size > 0;
1699
- } catch {
1700
- return false;
1629
+ function userText(content) {
1630
+ if (typeof content === "string") return content;
1631
+ if (!Array.isArray(content)) return "";
1632
+ return content.map((b) => isRecord(b) && b.type === "text" && typeof b.text === "string" ? b.text : "").join("");
1633
+ }
1634
+ function resultBlocks(content) {
1635
+ if (typeof content === "string") return content ? [{ type: "text", text: content }] : [];
1636
+ if (Array.isArray(content)) return content.filter((b) => !(isRecord(b) && b.type === "image"));
1637
+ return isRecord(content) ? [{ kind: "json", data: content }] : [];
1638
+ }
1639
+ function isCompactionCopy(id, seen) {
1640
+ if (!id.startsWith("compact_")) return false;
1641
+ const rest = id.slice("compact_".length);
1642
+ const sep = rest.indexOf("_");
1643
+ return sep >= 0 && seen.has(rest.slice(sep + 1));
1644
+ }
1645
+ async function readV3Entries(file) {
1646
+ const out = [];
1647
+ const toolsById = /* @__PURE__ */ new Map();
1648
+ const addTool = (tool) => {
1649
+ const seen2 = toolsById.get(tool.id);
1650
+ if (seen2) {
1651
+ Object.assign(seen2, { ...tool, content: seen2.content, output: seen2.output ?? tool.output });
1652
+ return;
1653
+ }
1654
+ toolsById.set(tool.id, tool);
1655
+ out.push({ kind: "item", item: { type: "tool_call", tool } });
1656
+ };
1657
+ const seen = /* @__PURE__ */ new Set();
1658
+ for (const line of await readLines(file)) {
1659
+ const copy = isCompactionCopy(line.id, seen);
1660
+ seen.add(line.id);
1661
+ if (copy) continue;
1662
+ const p = line.payload;
1663
+ const parsed = line.timestamp ? Date.parse(line.timestamp) : NaN;
1664
+ const at = Number.isNaN(parsed) ? void 0 : parsed;
1665
+ switch (p.type) {
1666
+ case "user":
1667
+ out.push({ kind: "prompt", id: line.id, text: userText(p.content), atMs: at, timestamp: at });
1668
+ break;
1669
+ case "assistant": {
1670
+ const text2 = str2(p.content) ?? "";
1671
+ if (!text2.trim()) break;
1672
+ if (p.operationType === "Summary") {
1673
+ out.push({ kind: "item", item: { type: "compaction", id: `c-${line.id}`, summary: text2, timestamp: at } });
1674
+ } else if (p.operationType === "Reasoning") {
1675
+ if (text2.trim() !== HIDDEN_REASONING) {
1676
+ out.push({ kind: "item", item: { type: "message", message: { id: `t-${line.id}`, role: "thinking", text: text2.trim(), timestamp: at } } });
1677
+ }
1678
+ } else {
1679
+ out.push({ kind: "item", item: { type: "message", message: { id: `a-${line.id}`, role: "assistant", text: text2, timestamp: at } } });
1680
+ }
1681
+ break;
1682
+ }
1683
+ case "tool_call": {
1684
+ const id = str2(p.toolCallId);
1685
+ if (!id) break;
1686
+ const title = str2(p.title);
1687
+ const meta = isRecord(p._meta) ? p._meta : void 0;
1688
+ const args = p.args;
1689
+ const input = p.toolName === "tool_call" && isRecord(args) && "arguments" in args ? args.arguments : args;
1690
+ addTool({
1691
+ id,
1692
+ // A call kiro copied in from a kiro 2.x session has no kind, only 2.x's tool name,
1693
+ // which the web renders by.
1694
+ name: kiroToolName({ title, _meta: meta }) ?? (p.kind === void 0 ? str2(p.toolName) : void 0),
1695
+ title: title ?? str2(p.toolName) ?? id,
1696
+ kind: str2(p.kind),
1697
+ status: p.status === "failed" ? "failed" : "completed",
1698
+ input,
1699
+ content: []
1700
+ });
1701
+ break;
1702
+ }
1703
+ case "tool_result": {
1704
+ const tool = toolsById.get(str2(p.toolCallId) ?? "");
1705
+ if (!tool) break;
1706
+ tool.status = p.success === false ? "failed" : "completed";
1707
+ tool.content = resultBlocks(p.content);
1708
+ break;
1709
+ }
1710
+ case "sub_agent_start": {
1711
+ const name = str2(p.subAgentName) ?? "subagent";
1712
+ addTool({
1713
+ id: line.id.endsWith(START_SUFFIX) ? line.id.slice(0, -START_SUFFIX.length) : line.id,
1714
+ name: SUBAGENT_TOOL,
1715
+ title: `Sub-agent: ${name}`,
1716
+ kind: "other",
1717
+ // Without a result the parent turn ended while the subagent ran.
1718
+ status: "failed",
1719
+ input: { name, prompt: str2(p.prompt), explanation: str2(p.explanation) },
1720
+ content: []
1721
+ });
1722
+ break;
1723
+ }
1724
+ case "sub_agent_complete": {
1725
+ const tool = toolsById.get(line.id.endsWith(COMPLETE_SUFFIX) ? line.id.slice(0, -COMPLETE_SUFFIX.length) : "");
1726
+ if (!tool) break;
1727
+ tool.status = p.status === "success" ? "completed" : "failed";
1728
+ tool.output = str2(p.response);
1729
+ break;
1730
+ }
1731
+ default:
1732
+ break;
1733
+ }
1734
+ }
1735
+ return out;
1736
+ }
1737
+ async function readSubExecutions(dir) {
1738
+ const byId = /* @__PURE__ */ new Map();
1739
+ for (const line of await readLines(path10.join(dir, "messages.jsonl"), '"sub_agent_')) {
1740
+ const p = line.payload;
1741
+ const subId = str2(p.subSessionId);
1742
+ if (!subId) continue;
1743
+ const at = iso(line.timestamp) ?? (/* @__PURE__ */ new Date(0)).toISOString();
1744
+ if (p.type === "sub_agent_start") {
1745
+ byId.set(subId, {
1746
+ sessionId: subId,
1747
+ name: str2(p.subAgentName) ?? subId,
1748
+ toolCallId: line.id.endsWith(START_SUFFIX) ? line.id.slice(0, -START_SUFFIX.length) : line.id,
1749
+ status: "failed",
1750
+ stopped: true,
1751
+ createdAt: at,
1752
+ updatedAt: at
1753
+ });
1754
+ } else if (p.type === "sub_agent_complete") {
1755
+ const sub = byId.get(subId);
1756
+ if (!sub) continue;
1757
+ sub.status = p.status === "success" ? "completed" : "failed";
1758
+ sub.stopped = false;
1759
+ sub.updatedAt = at;
1760
+ }
1701
1761
  }
1762
+ return [...byId.values()];
1763
+ }
1764
+ function subExecutionFile(dir, subagentId) {
1765
+ return isValidSessionId(subagentId) ? path10.join(dir, "sub-executions", `${subagentId}.jsonl`) : void 0;
1766
+ }
1767
+ var PLACEHOLDER_TITLE, HIDDEN_REASONING, CONTEXT_TAIL_BYTES, CONTEXT_CHUNK_BYTES, isRecord, str2, V2_STORE, START_SUFFIX, COMPLETE_SUFFIX;
1768
+ var init_kiroV3Files = __esm({
1769
+ "server/src/session/kiroV3Files.ts"() {
1770
+ "use strict";
1771
+ init_dist();
1772
+ init_config();
1773
+ init_paths2();
1774
+ PLACEHOLDER_TITLE = "New Session";
1775
+ HIDDEN_REASONING = "...";
1776
+ CONTEXT_TAIL_BYTES = 1024 * 1024;
1777
+ CONTEXT_CHUNK_BYTES = 16 * 1024;
1778
+ isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1779
+ str2 = (v) => typeof v === "string" ? v : void 0;
1780
+ V2_STORE = "cli";
1781
+ START_SUFFIX = "-sub-agent-start";
1782
+ COMPLETE_SUFFIX = "-sub-agent-complete";
1783
+ }
1784
+ });
1785
+
1786
+ // server/src/session/kiroFiles.ts
1787
+ import fs10 from "node:fs/promises";
1788
+ import path11 from "node:path";
1789
+ async function readPersistedSession(sessionId) {
1790
+ const dir = await v3SessionDir(sessionId);
1791
+ return dir ? readV3Session(dir) : null;
1792
+ }
1793
+ async function readPersistedSessions(sessionIds) {
1794
+ const dirs = await v3SessionDirs();
1795
+ const out = /* @__PURE__ */ new Map();
1796
+ await Promise.all(
1797
+ sessionIds.filter(isValidSessionId).map(async (id) => {
1798
+ const dir = dirs.get(id);
1799
+ const found = dir ? await readV3Session(dir) : null;
1800
+ if (found) out.set(id, found);
1801
+ })
1802
+ );
1803
+ return out;
1804
+ }
1805
+ async function deletePersistedSession(sessionId) {
1806
+ const dir = await v3SessionDir(sessionId);
1807
+ if (dir) await fs10.rm(dir, { recursive: true, force: true });
1808
+ }
1809
+ async function isLoadable(sessionId) {
1810
+ return await v3SessionDir(sessionId) !== void 0;
1811
+ }
1812
+ async function readEntries(sessionId) {
1813
+ const dir = await v3SessionDir(sessionId);
1814
+ return dir ? readV3Entries(path11.join(dir, "messages.jsonl")) : [];
1815
+ }
1816
+ async function promptCount(sessionId) {
1817
+ return (await readEntries(sessionId)).filter((e) => e.kind === "prompt").length;
1702
1818
  }
1703
1819
  function endingItems(e) {
1704
1820
  const prefix = `te${e.id}-`;
@@ -1710,93 +1826,71 @@ function endingItems(e) {
1710
1826
  );
1711
1827
  return items;
1712
1828
  }
1713
- async function hydrateTranscript(sessionId, attachments, endings = []) {
1829
+ function mergeTranscript(entries, attachments, endings) {
1714
1830
  const items = [];
1715
1831
  let userOrdinal = 0;
1716
- const toolsById = /* @__PURE__ */ new Map();
1717
1832
  const waiting = [...endings];
1718
1833
  let replaced = false;
1719
1834
  const unrecorded = (e) => [
1720
1835
  { type: "message", message: { id: `te${e.id}-u`, role: "user", text: e.prompt, timestamp: e.startedAt } },
1721
1836
  ...endingItems(e)
1722
1837
  ];
1723
- for (const entry of await readJsonlEntries(sessionId)) {
1724
- const content = contentBlocks(entry.data.content);
1725
- const textOf = (kind) => content.filter((c) => c.kind === kind).map(blockText).join("");
1726
- const baseId = entry.data.message_id ?? `${items.length}`;
1727
- const ts = entry.data.meta?.timestamp;
1728
- const pushMsg = (msg) => items.push({ type: "message", message: msg });
1729
- if (entry.kind === "Prompt") {
1730
- replaced = false;
1731
- const text2 = stripAttachmentsLine(textOf("text"));
1732
- let matched;
1733
- if (ts !== void 0) {
1734
- const at = ts * 1e3;
1735
- matched = waiting.find(
1736
- (e) => e.prompt.trim() === text2.trim() && at >= e.startedAt - STAMP_PAD_MS && at <= e.endedAt + STAMP_PAD_MS
1737
- );
1738
- while (waiting.length && waiting[0] !== matched && waiting[0].endedAt < at - STAMP_PAD_MS) {
1739
- items.push(...unrecorded(waiting.shift()));
1740
- }
1741
- if (matched) waiting.splice(waiting.indexOf(matched), 1);
1742
- }
1743
- const attached = attachments?.get(userOrdinal);
1744
- userOrdinal++;
1745
- if (text2.trim() || attached?.length)
1746
- pushMsg({ id: `u-${baseId}`, role: "user", text: text2, timestamp: ts, attachments: attached });
1747
- if (matched) {
1748
- items.push(...endingItems(matched));
1749
- replaced = true;
1750
- }
1751
- } else if (entry.kind === "AssistantMessage") {
1752
- if (replaced) continue;
1753
- const thinking = textOf("thinking");
1754
- if (thinking.trim())
1755
- pushMsg({ id: `t-${baseId}`, role: "thinking", text: thinking.trim(), timestamp: ts });
1756
- const text2 = textOf("text");
1757
- if (text2.trim())
1758
- pushMsg({ id: `a-${baseId}`, role: "assistant", text: text2, timestamp: ts });
1759
- for (const c of content) {
1760
- const d = toolUseData(c);
1761
- if (!d) continue;
1762
- const tool = {
1763
- id: d.toolUseId,
1764
- name: d.name,
1765
- title: d.name ?? d.toolUseId,
1766
- status: "completed",
1767
- input: d.input,
1768
- content: []
1769
- };
1770
- toolsById.set(d.toolUseId, tool);
1771
- items.push({ type: "tool_call", tool });
1772
- }
1773
- } else if (entry.kind === "ToolResults") {
1774
- if (replaced) continue;
1775
- for (const c of content) {
1776
- const d = toolResultData(c);
1777
- if (!d) continue;
1778
- const tool = toolsById.get(d.toolUseId);
1779
- if (!tool) continue;
1780
- tool.status = d.status === "error" ? "failed" : "completed";
1781
- tool.content = renderableBlocks(d.content);
1838
+ for (const entry of entries) {
1839
+ if (entry.kind === "item") {
1840
+ if (!replaced || entry.item.type === "compaction") items.push(entry.item);
1841
+ continue;
1842
+ }
1843
+ replaced = false;
1844
+ const text2 = stripAttachmentsLine(entry.text);
1845
+ let matched;
1846
+ const at = entry.atMs;
1847
+ if (at !== void 0) {
1848
+ matched = waiting.find(
1849
+ (e) => e.prompt.trim() === text2.trim() && at >= e.startedAt - STAMP_PAD_MS && at <= e.endedAt + STAMP_PAD_MS
1850
+ );
1851
+ while (waiting.length && waiting[0] !== matched && waiting[0].endedAt < at - STAMP_PAD_MS) {
1852
+ items.push(...unrecorded(waiting.shift()));
1782
1853
  }
1783
- } else if (entry.kind === "Compaction") {
1784
- const summary = entry.data.summary ?? "";
1785
- if (summary.trim())
1786
- items.push({ type: "compaction", id: `c-${baseId}`, summary, timestamp: ts });
1854
+ if (matched) waiting.splice(waiting.indexOf(matched), 1);
1855
+ }
1856
+ const attached = attachments?.get(userOrdinal);
1857
+ userOrdinal++;
1858
+ if (text2.trim() || attached?.length) {
1859
+ items.push({ type: "message", message: { id: `u-${entry.id}`, role: "user", text: text2, timestamp: entry.timestamp, attachments: attached } });
1860
+ }
1861
+ if (matched) {
1862
+ items.push(...endingItems(matched));
1863
+ replaced = true;
1787
1864
  }
1788
1865
  }
1789
1866
  for (const e of waiting) items.push(...unrecorded(e));
1790
1867
  return items;
1791
1868
  }
1792
- var isRecord, STAMP_PAD_MS;
1869
+ async function hydrateTranscript(sessionId, attachments, endings = []) {
1870
+ return mergeTranscript(await readEntries(sessionId), attachments, endings);
1871
+ }
1872
+ async function listSubExecutions(sessionId) {
1873
+ const dir = await v3SessionDir(sessionId);
1874
+ return dir ? readSubExecutions(dir) : null;
1875
+ }
1876
+ async function hydrateSubExecution(sessionId, subagentId) {
1877
+ const dir = await v3SessionDir(sessionId);
1878
+ const file = dir ? subExecutionFile(dir, subagentId) : void 0;
1879
+ if (!file) return null;
1880
+ try {
1881
+ await fs10.access(file);
1882
+ } catch {
1883
+ return null;
1884
+ }
1885
+ return mergeTranscript(await readV3Entries(file), void 0, []);
1886
+ }
1887
+ var STAMP_PAD_MS;
1793
1888
  var init_kiroFiles = __esm({
1794
1889
  "server/src/session/kiroFiles.ts"() {
1795
1890
  "use strict";
1796
1891
  init_dist();
1797
- init_config();
1798
1892
  init_paths2();
1799
- isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1893
+ init_kiroV3Files();
1800
1894
  STAMP_PAD_MS = 2e3;
1801
1895
  }
1802
1896
  });
@@ -1918,50 +2012,18 @@ var init_chatStore = __esm({
1918
2012
  }
1919
2013
  });
1920
2014
 
1921
- // server/src/session/subagentLinks.ts
1922
- var SubagentLinkStore;
1923
- var init_subagentLinks = __esm({
1924
- "server/src/session/subagentLinks.ts"() {
1925
- "use strict";
1926
- init_db();
1927
- SubagentLinkStore = class {
1928
- /** Record (or correct) one child's link. Idempotent: a repeat call with the same
1929
- * values is a no-op, and the row always reflects the latest call to resolve it. */
1930
- record(childSessionId, parentSessionId, toolCallId, stageName) {
1931
- db().prepare(
1932
- `INSERT INTO subagent_links (child_session_id, parent_session_id, tool_call_id, stage_name)
1933
- VALUES (?, ?, ?, ?)
1934
- ON CONFLICT(child_session_id) DO UPDATE SET
1935
- parent_session_id = excluded.parent_session_id,
1936
- tool_call_id = excluded.tool_call_id,
1937
- stage_name = excluded.stage_name`
1938
- ).run(childSessionId, parentSessionId, toolCallId, stageName);
1939
- }
1940
- /** Every link recorded for one parent session's children, keyed by child session id. */
1941
- forParent(parentSessionId) {
1942
- const rows = db().prepare(
1943
- "SELECT child_session_id, tool_call_id, stage_name FROM subagent_links WHERE parent_session_id = ?"
1944
- ).all(parentSessionId);
1945
- const out = /* @__PURE__ */ new Map();
1946
- for (const r of rows) out.set(r.child_session_id, { toolCallId: r.tool_call_id, stageName: r.stage_name });
1947
- return out;
1948
- }
1949
- };
1950
- }
1951
- });
1952
-
1953
2015
  // server/src/session/SessionManager.ts
1954
- import fs10 from "node:fs";
1955
- import path11 from "node:path";
2016
+ import fs11 from "node:fs";
2017
+ import path12 from "node:path";
1956
2018
  function resolveCwd(input) {
1957
2019
  const raw = input?.trim();
1958
- const abs = raw ? path11.resolve(config.defaultCwd, raw) : config.defaultCwd;
2020
+ const abs = raw ? path12.resolve(config.defaultCwd, raw) : config.defaultCwd;
1959
2021
  if (!isWithinRoot(config.fileRoot, abs)) {
1960
2022
  throw new Error(`Working directory is outside the allowed root: ${abs}`);
1961
2023
  }
1962
2024
  let stat;
1963
2025
  try {
1964
- stat = fs10.statSync(abs);
2026
+ stat = fs11.statSync(abs);
1965
2027
  } catch {
1966
2028
  stat = void 0;
1967
2029
  }
@@ -1969,37 +2031,44 @@ function resolveCwd(input) {
1969
2031
  throw new Error(`Working directory path is a file, not a directory: ${abs}`);
1970
2032
  }
1971
2033
  if (!stat) {
1972
- fs10.mkdirSync(abs, { recursive: true });
2034
+ fs11.mkdirSync(abs, { recursive: true });
1973
2035
  }
1974
2036
  return abs;
1975
2037
  }
1976
- function mapNotification(n) {
1977
- switch (n.method) {
1978
- case "session/update":
1979
- return { kind: "session_update", update: n.params.update };
1980
- case KIRO_NOTIFICATIONS.metadata:
1981
- return { kind: "metadata", params: n.params };
1982
- case KIRO_NOTIFICATIONS.compactionStatus:
1983
- return { kind: "compaction", params: n.params };
2038
+ function kiroMeta(update) {
2039
+ return update._meta?.kiro;
2040
+ }
2041
+ function sessionInfoEvent(sessionId, update) {
2042
+ const info = update._meta?.kiro;
2043
+ switch (info?.kind) {
2044
+ case "context_usage":
2045
+ return typeof info.usagePercentage === "number" ? { kind: "metadata", params: { sessionId, contextUsagePercentage: info.usagePercentage } } : null;
2046
+ case "summarization_started":
2047
+ return { kind: "compaction", params: { sessionId, status: { type: "started" }, summary: null } };
2048
+ case "summarization_completed":
2049
+ return {
2050
+ kind: "compaction",
2051
+ params: { sessionId, status: { type: "completed" }, summary: info.conversationSummary ?? null }
2052
+ };
2053
+ case "summarization_failed":
2054
+ return { kind: "compaction", params: { sessionId, status: { type: "failed" }, summary: null } };
1984
2055
  default:
1985
2056
  return null;
1986
2057
  }
1987
2058
  }
1988
- function notificationSessionId(n) {
1989
- const params = n.params;
1990
- return params?.sessionId;
2059
+ function sessionModel(res) {
2060
+ return res.configOptions?.find((o) => o.id === "model")?.currentValue;
1991
2061
  }
1992
- function subagentCallStages(update) {
1993
- if (update.sessionUpdate !== "tool_call") return null;
1994
- const meta = update._meta;
1995
- if (meta?.kiro?.toolName !== "subagent") return null;
1996
- const toolCallId = update.toolCallId;
1997
- const rawInput = update.rawInput;
1998
- if (typeof toolCallId !== "string" || !Array.isArray(rawInput?.stages)) return null;
1999
- const stages = rawInput.stages.filter(
2000
- (s) => typeof s === "object" && s !== null && typeof s.name === "string"
2001
- );
2002
- return { toolCallId, stages };
2062
+ function subExecutionSummary(sub) {
2063
+ return {
2064
+ sessionId: sub.sessionId,
2065
+ stageName: sub.name,
2066
+ toolCallId: sub.toolCallId,
2067
+ status: sub.status,
2068
+ activity: sub.stopped ? "Stopped" : void 0,
2069
+ createdAt: sub.createdAt,
2070
+ updatedAt: sub.updatedAt
2071
+ };
2003
2072
  }
2004
2073
  function newerOf(a, b) {
2005
2074
  return a && a.localeCompare(b) > 0 ? a : b;
@@ -2011,7 +2080,7 @@ function firstPromptText(transcript) {
2011
2080
  function isGhost(s, hasFile) {
2012
2081
  return !hasFile && s.hasBeenLive && !s.proc;
2013
2082
  }
2014
- var CANCEL_GRACE_MS, Session, TRANSCRIPT_PAGE_SIZE, SessionManager;
2083
+ var CANCEL_GRACE_MS, Session, TRANSCRIPT_UPDATES, TRANSCRIPT_PAGE_SIZE, SessionManager;
2015
2084
  var init_SessionManager = __esm({
2016
2085
  "server/src/session/SessionManager.ts"() {
2017
2086
  "use strict";
@@ -2025,11 +2094,8 @@ var init_SessionManager = __esm({
2025
2094
  init_TurnState();
2026
2095
  init_TurnRecorder();
2027
2096
  init_SubagentTracker();
2028
- init_subagentResult();
2029
- init_subagentFallbackMatch();
2030
2097
  init_kiroFiles();
2031
2098
  init_chatStore();
2032
- init_subagentLinks();
2033
2099
  CANCEL_GRACE_MS = 1e4;
2034
2100
  Session = class {
2035
2101
  sessionId;
@@ -2078,21 +2144,20 @@ var init_SessionManager = __esm({
2078
2144
  return event;
2079
2145
  }
2080
2146
  };
2147
+ TRANSCRIPT_UPDATES = /* @__PURE__ */ new Set(["agent_message_chunk", "agent_thought_chunk", "tool_call", "tool_call_update"]);
2081
2148
  TRANSCRIPT_PAGE_SIZE = 80;
2082
2149
  SessionManager = class {
2083
2150
  sessions = /* @__PURE__ */ new Map();
2084
2151
  log;
2085
2152
  store = new ChatStore();
2086
- subagentLinks = new SubagentLinkStore();
2087
- // Children the title fallback already tried and could not match. Matching hydrates the
2088
- // whole parent transcript (145 MB in one chat), so each child is tried once per process.
2089
- unmatchedChildren = /* @__PURE__ */ new Map();
2090
2153
  spawnProcess;
2091
2154
  cancelGraceMs;
2155
+ defaultModel;
2092
2156
  constructor(log, opts = {}) {
2093
2157
  this.log = log;
2094
2158
  this.spawnProcess = opts.spawn ?? ((o, l) => new KiroProcess(o, l));
2095
2159
  this.cancelGraceMs = opts.cancelGraceMs ?? CANCEL_GRACE_MS;
2160
+ this.defaultModel = opts.defaultModel ?? (async () => void 0);
2096
2161
  }
2097
2162
  /** What a session is called, from every read path. */
2098
2163
  titleOf(chat, parts) {
@@ -2100,7 +2165,7 @@ var init_SessionManager = __esm({
2100
2165
  override: chat.title ?? void 0,
2101
2166
  kiroTitle: parts.kiroTitle,
2102
2167
  firstPrompt: parts.firstPrompt,
2103
- folder: isManagedWorkspace(parts.cwd) ? void 0 : path11.basename(parts.cwd)
2168
+ folder: isManagedWorkspace(parts.cwd) ? void 0 : path12.basename(parts.cwd)
2104
2169
  });
2105
2170
  }
2106
2171
  renameChat(chatId, title) {
@@ -2158,7 +2223,7 @@ var init_SessionManager = __esm({
2158
2223
  }
2159
2224
  /** The reload itself, called with the session's reload claim held. */
2160
2225
  async replaceProcess(chatId, s) {
2161
- if (!await hasRecordedTurns(s.sessionId)) {
2226
+ if (!await isLoadable(s.sessionId)) {
2162
2227
  throw new Error(
2163
2228
  "kiro has not saved this session yet. Send a message first, then reload."
2164
2229
  );
@@ -2172,9 +2237,9 @@ var init_SessionManager = __esm({
2172
2237
  });
2173
2238
  }
2174
2239
  s.availableModes = [];
2240
+ invalidateAgents();
2175
2241
  await this.ensureProc(s);
2176
2242
  s.lastActivity = Date.now();
2177
- invalidateAgents();
2178
2243
  this.log.info({ sessionId: s.sessionId, cwd: s.cwd }, "session reloaded");
2179
2244
  return this.getDetail(chatId);
2180
2245
  }
@@ -2229,30 +2294,30 @@ var init_SessionManager = __esm({
2229
2294
  wire(s, proc) {
2230
2295
  proc.on("notification", (n) => {
2231
2296
  if (s.replaying) return;
2232
- if (n.method === KIRO_NOTIFICATIONS.subagentListUpdate) {
2233
- const params = n.params;
2234
- const resolved = s.subagents.apply(params.subagents);
2235
- for (const link of resolved) {
2236
- this.subagentLinks.record(link.sessionId, s.sessionId, link.toolCallId, link.stageName);
2237
- }
2238
- s.record({ kind: "subagents_changed", subagents: s.subagents.list() });
2297
+ if (n.method === KIRO_AGENT_NOT_FOUND) {
2298
+ this.log.warn({ sessionId: s.sessionId, detail: n.params }, "kiro could not use the requested agent");
2239
2299
  return;
2240
2300
  }
2241
- const notifSessionId = notificationSessionId(n);
2242
- if (!notifSessionId || notifSessionId === s.sessionId) {
2243
- const payload = mapNotification(n);
2244
- if (!payload) return;
2245
- if (payload.kind === "session_update") {
2246
- const call = subagentCallStages(payload.update);
2247
- if (call) s.subagents.callStarted(call.toolCallId, call.stages);
2248
- const update = payload.update;
2249
- if (update.sessionUpdate === "tool_call_update" && typeof update.toolCallId === "string" && (update.status === "completed" || update.status === "failed")) {
2250
- s.subagents.callFinished(update.toolCallId);
2251
- }
2252
- }
2253
- s.record(payload);
2301
+ if (n.method !== ACP_METHODS.sessionUpdate) return;
2302
+ const params = n.params;
2303
+ if (!params?.update || params.sessionId && params.sessionId !== s.sessionId) return;
2304
+ const { update } = params;
2305
+ const meta = kiroMeta(update);
2306
+ if (meta?.replay) return;
2307
+ if (update.sessionUpdate === "session_info_update") {
2308
+ const payload = sessionInfoEvent(s.sessionId, update);
2309
+ if (payload) s.record(payload);
2310
+ return;
2311
+ }
2312
+ if (!TRANSCRIPT_UPDATES.has(update.sessionUpdate)) return;
2313
+ const subagentId = meta?.agentSubtaskId;
2314
+ if (subagentId && meta.kind !== "agent-subtask") {
2315
+ const title = update.sessionUpdate === "tool_call" ? update.title : void 0;
2316
+ if (s.subagents.progressed(subagentId, title)) this.recordSubagents(s);
2254
2317
  return;
2255
2318
  }
2319
+ if (subagentId) this.trackSubagentCall(s, subagentId, update);
2320
+ s.record({ kind: "session_update", update });
2256
2321
  });
2257
2322
  proc.on("exit", (code, signal) => {
2258
2323
  if (s.proc !== proc) return;
@@ -2261,28 +2326,50 @@ var init_SessionManager = __esm({
2261
2326
  s.running = false;
2262
2327
  });
2263
2328
  }
2329
+ /** Follows the tool call that starts a subagent, from its start to its result. */
2330
+ trackSubagentCall(s, subagentId, update) {
2331
+ const call = update;
2332
+ if (typeof call.toolCallId !== "string") return;
2333
+ let changed = false;
2334
+ if (call.sessionUpdate === "tool_call") {
2335
+ const name = typeof call.rawInput?.name === "string" ? call.rawInput.name : call.title ?? subagentId;
2336
+ changed = s.subagents.started(subagentId, call.toolCallId, name);
2337
+ } else if (call.status === "completed" || call.status === "failed") {
2338
+ changed = s.subagents.finished(call.toolCallId, call.status === "completed");
2339
+ }
2340
+ if (changed) this.recordSubagents(s);
2341
+ }
2342
+ recordSubagents(s) {
2343
+ s.record({ kind: "subagents_changed", subagents: s.subagents.list() });
2344
+ }
2264
2345
  /** Gets or spawns the kiro process for a session. */
2265
2346
  async ensureProc(s) {
2266
2347
  if (s.proc) return s.proc;
2267
2348
  if (s.spawning) return s.spawning;
2268
2349
  s.spawning = (async () => {
2269
2350
  await this.ensureCapacity();
2270
- const proc = this.spawnProcess(
2271
- { cwd: s.cwd, agent: s.agentId, model: s.modelId },
2272
- this.log
2273
- );
2351
+ const proc = this.spawnProcess({ cwd: s.cwd }, this.log);
2274
2352
  this.wire(s, proc);
2275
2353
  await proc.initialize();
2276
2354
  let res;
2277
2355
  if (s.hasBeenLive) {
2278
2356
  s.replaying = true;
2279
2357
  try {
2280
- res = await proc.loadSession({ sessionId: s.sessionId, cwd: s.cwd, mcpServers: [] });
2358
+ res = await proc.loadSession({
2359
+ sessionId: s.sessionId,
2360
+ cwd: s.cwd,
2361
+ mcpServers: [],
2362
+ ...s.modelId ? { _meta: { kiro: { modelId: s.modelId } } } : {}
2363
+ });
2281
2364
  } finally {
2282
2365
  s.replaying = false;
2283
2366
  }
2284
2367
  } else {
2285
- res = await proc.newSession({ cwd: s.cwd, mcpServers: [] });
2368
+ res = await proc.newSession({
2369
+ cwd: s.cwd,
2370
+ mcpServers: [],
2371
+ _meta: { kiro: { modeId: s.agentId, modelId: s.modelId } }
2372
+ });
2286
2373
  }
2287
2374
  if (!s.hasBeenLive && res.sessionId !== s.sessionId) {
2288
2375
  this.sessions.delete(s.sessionId);
@@ -2292,6 +2379,8 @@ var init_SessionManager = __esm({
2292
2379
  s.availableModes = res.modes.availableModes;
2293
2380
  s.currentModeId = res.modes.currentModeId;
2294
2381
  s.agentId = res.modes.currentModeId ?? s.agentId;
2382
+ s.modelId = sessionModel(res) ?? s.modelId;
2383
+ rememberModes(res.modes.availableModes);
2295
2384
  s.markLive();
2296
2385
  s.proc = proc;
2297
2386
  s.spawning = void 0;
@@ -2332,7 +2421,7 @@ var init_SessionManager = __esm({
2332
2421
  const s = new Session(tempId, store, cwd);
2333
2422
  s.agentId = opts.agentId ?? config.defaultAgent;
2334
2423
  s.currentModeId = s.agentId;
2335
- s.modelId = opts.modelId;
2424
+ s.modelId = opts.modelId ?? await this.defaultModel();
2336
2425
  this.sessions.set(tempId, s);
2337
2426
  try {
2338
2427
  await this.ensureProc(s);
@@ -2342,7 +2431,7 @@ var init_SessionManager = __esm({
2342
2431
  }
2343
2432
  this.store.create(chatId);
2344
2433
  this.store.bindSession(chatId, s.sessionId);
2345
- const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path11.basename(s.cwd));
2434
+ const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path12.basename(s.cwd));
2346
2435
  if (name) {
2347
2436
  this.store.setTitle(chatId, name);
2348
2437
  s.title = name;
@@ -2400,6 +2489,7 @@ ${tail}` : err.message;
2400
2489
  s.cancelTimer = void 0;
2401
2490
  s.running = false;
2402
2491
  s.lastActivity = Date.now();
2492
+ if (s.subagents.turnEnded()) s.record({ kind: "subagents_changed", subagents: s.subagents.list() });
2403
2493
  });
2404
2494
  }
2405
2495
  saveEnding(chatId, ending) {
@@ -2451,12 +2541,29 @@ ${tail}` : err.message;
2451
2541
  s.modelId = modelId;
2452
2542
  s.lastActivity = Date.now();
2453
2543
  }
2544
+ /** Runs one of kiro's slash commands. Compaction is the only one Casper offers. It
2545
+ * returns once kiro has started; its progress arrives as compaction events. */
2454
2546
  async execCommand(chatId, command) {
2547
+ if (command.replace(/^\//, "") !== "compact") throw new Error(`Unsupported command: ${command}`);
2455
2548
  const s = await this.ensureOpen(chatId);
2456
2549
  await this.settleReload(s);
2550
+ if (s.turnState.get().compacting) throw new Error("The conversation is already being compacted");
2457
2551
  const proc = await this.ensureProc(s);
2458
- await proc.execCommand(s.sessionId, command);
2459
2552
  s.lastActivity = Date.now();
2553
+ const status = (type) => ({
2554
+ kind: "compaction",
2555
+ params: { sessionId: s.sessionId, status: { type }, summary: null }
2556
+ });
2557
+ s.record(status("started"));
2558
+ proc.compact(s.sessionId).then(
2559
+ () => {
2560
+ if (s.turnState.get().compacting && s.proc === proc) s.record(status("completed"));
2561
+ },
2562
+ (err) => {
2563
+ this.log.warn({ err, sessionId: s.sessionId }, "compaction failed");
2564
+ if (s.turnState.get().compacting && s.proc === proc) s.record(status("failed"));
2565
+ }
2566
+ );
2460
2567
  }
2461
2568
  /** The one place a ChatSummary is assembled, since kiro's file and Casper's
2462
2569
  * live state each hold part of the truth. */
@@ -2484,13 +2591,13 @@ ${tail}` : err.message;
2484
2591
  }
2485
2592
  async listChats() {
2486
2593
  const rows = this.store.all();
2487
- const files = await Promise.all(
2488
- rows.map((r) => r.sessionId ? readPersistedSession(r.sessionId) : null)
2594
+ const files = await readPersistedSessions(
2595
+ rows.map((r) => r.sessionId).filter((id) => !!id)
2489
2596
  );
2490
2597
  const out = [];
2491
- rows.forEach((row, i) => {
2598
+ rows.forEach((row) => {
2492
2599
  const live = row.sessionId ? this.sessions.get(row.sessionId) : void 0;
2493
- const persisted = files[i] ?? void 0;
2600
+ const persisted = row.sessionId ? files.get(row.sessionId) : void 0;
2494
2601
  if (live && isGhost(live, !!persisted)) {
2495
2602
  this.evict(live.sessionId);
2496
2603
  return;
@@ -2533,68 +2640,36 @@ ${tail}` : err.message;
2533
2640
  const end = Math.max(start, Math.min(start + limit, transcript.length));
2534
2641
  return transcript.slice(start, end);
2535
2642
  }
2536
- /**
2537
- * A chat's subagents: live ones from the tracker, joined with children found on disk.
2538
- * A child the tracker never saw falls back to a recorded link or title matching
2539
- * (subagentFallbackMatch.ts).
2540
- */
2643
+ /** A chat's subagents: the ones kiro recorded in the session's file, and live ones from
2644
+ * the tracker. A live row wins over the file's until the file records how it ended. */
2541
2645
  async getSubagents(chatId) {
2542
2646
  const sessionId = this.sessionIdOf(chatId);
2543
- const s = this.sessions.get(sessionId);
2544
- const live = s ? s.subagents.list() : [];
2545
- const liveToolCalls = new Set(live.map((a) => a.toolCallId).filter((id) => !!id));
2546
- const pending = s ? [...liveToolCalls].flatMap((id) => s.subagents.listForCall(id).filter((a) => a.status === "pending")) : [];
2547
- const onDisk = await listChildSessions(sessionId);
2548
- const links = this.subagentLinks.forParent(sessionId);
2549
- const liveIds = new Set(live.map((a) => a.sessionId));
2550
- const tried = this.unmatchedChildren.get(sessionId) ?? /* @__PURE__ */ new Set();
2551
- const unresolved = onDisk.filter(
2552
- (c) => !liveIds.has(c.sessionId) && !links.has(c.sessionId) && !tried.has(c.sessionId)
2553
- );
2554
- const fallback = unresolved.length ? matchSubagentsFallback(subagentCallsIn(await hydrateTranscript(sessionId)), unresolved) : [];
2555
- const fallbackByChild = new Map(fallback.map((m) => [m.sessionId, m]));
2556
- for (const m of fallback) this.subagentLinks.record(m.sessionId, sessionId, m.toolCallId, m.stageName);
2557
- for (const c of unresolved) if (!fallbackByChild.has(c.sessionId)) tried.add(c.sessionId);
2558
- this.unmatchedChildren.set(sessionId, tried);
2647
+ const live = this.sessions.get(sessionId)?.subagents.list() ?? [];
2559
2648
  const byId = /* @__PURE__ */ new Map();
2560
- for (const child of onDisk) {
2561
- const link = links.get(child.sessionId) ?? fallbackByChild.get(child.sessionId);
2562
- byId.set(child.sessionId, {
2563
- sessionId: child.sessionId,
2564
- stageName: link?.stageName ?? (child.title || child.sessionId),
2565
- toolCallId: link?.toolCallId,
2566
- status: "completed",
2567
- createdAt: child.createdAt,
2568
- updatedAt: child.updatedAt
2569
- });
2649
+ const finished = /* @__PURE__ */ new Set();
2650
+ for (const sub of await listSubExecutions(sessionId) ?? []) {
2651
+ byId.set(sub.sessionId, subExecutionSummary(sub));
2652
+ if (!sub.stopped) finished.add(sub.sessionId);
2570
2653
  }
2571
- for (const a of live) byId.set(a.sessionId, a);
2572
- const startedThenPending = [...byId.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2573
- return [...startedThenPending, ...pending];
2654
+ for (const a of live) if (!finished.has(a.sessionId)) byId.set(a.sessionId, a);
2655
+ return [...byId.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2574
2656
  }
2575
2657
  /** One subagent's own transcript, hydrated the same way a chat's is. */
2576
- // Polled while a subagent runs, so it reads only the child's own files.
2577
- async getSubagentDetail(chatId, childSessionId) {
2658
+ // Polled while a subagent runs, so it reads only the subagent's own files.
2659
+ async getSubagentDetail(chatId, subagentId) {
2578
2660
  const parentSessionId = this.sessionIdOf(chatId);
2579
- const child = await readChildSession(childSessionId, parentSessionId);
2580
- if (!child) throw new Error(`Unknown subagent: ${childSessionId}`);
2581
- const live = this.sessions.get(parentSessionId)?.subagents.list().find((a) => a.sessionId === childSessionId);
2582
- const link = this.subagentLinks.forParent(parentSessionId).get(childSessionId);
2583
- const summary = live ?? {
2584
- sessionId: childSessionId,
2585
- stageName: link?.stageName ?? (child.title || childSessionId),
2586
- toolCallId: link?.toolCallId,
2587
- status: "completed",
2588
- createdAt: child.createdAt,
2589
- updatedAt: child.updatedAt
2590
- };
2591
- const transcript = showSummaryAsAnswer(await hydrateTranscript(childSessionId));
2592
- return { subagent: summary, transcript };
2661
+ const live = this.sessions.get(parentSessionId)?.subagents.get(subagentId);
2662
+ const recorded = (await listSubExecutions(parentSessionId))?.find((x) => x.sessionId === subagentId);
2663
+ if (!live && !recorded) throw new Error(`Unknown subagent: ${subagentId}`);
2664
+ const transcript = await hydrateSubExecution(parentSessionId, subagentId) ?? [];
2665
+ const subagent = recorded && (!live || !recorded.stopped) ? subExecutionSummary(recorded) : live;
2666
+ return { subagent, transcript };
2593
2667
  }
2594
2668
  /** One projection, so a dormant session and a live one cannot disagree. */
2595
- buildDetail(chat, s, transcript, persisted) {
2669
+ buildDetail(chat, s, hydrated, persisted) {
2670
+ const { transcript, head } = this.resumePoint(s, hydrated);
2596
2671
  return {
2597
- summary: this.summaryOf(chat, s, persisted, transcript),
2672
+ summary: this.summaryOf(chat, s, persisted, hydrated),
2598
2673
  modes: s?.availableModes ?? [],
2599
2674
  currentModeId: s?.currentModeId ?? persisted?.agentId,
2600
2675
  transcript: transcript.slice(-TRANSCRIPT_PAGE_SIZE),
@@ -2603,16 +2678,19 @@ ${tail}` : err.message;
2603
2678
  ...emptyObservabilitySnapshot(),
2604
2679
  contextUsagePercentage: persisted?.contextUsagePercentage ?? 0
2605
2680
  },
2606
- head: s ? this.replayHead(s, transcript) : 0
2681
+ head
2607
2682
  };
2608
2683
  }
2609
2684
  /**
2610
- * The cursor a reconnecting client starts from: the store head, unless the in-flight
2611
- * turn isn't in kiro's jsonl yet, in which case rewind to its turn_started.
2685
+ * Where a reconnecting client picks up: the transcript, and the cursor its events replay
2686
+ * after. A turn in flight replays whole from its turn_started and is left out of the
2687
+ * transcript, since kiro writes the prompt at once but a tool call only once it ends, so
2688
+ * kiro's copy of the turn lacks whatever is still running.
2612
2689
  */
2613
- replayHead(s, transcript) {
2690
+ resumePoint(s, transcript) {
2691
+ if (!s) return { transcript, head: 0 };
2614
2692
  const head = s.store.head();
2615
- if (!s.running) return head;
2693
+ if (!s.running) return { transcript, head };
2616
2694
  const { events } = s.store.getSince(0);
2617
2695
  let started;
2618
2696
  for (let i = events.length - 1; i >= 0; i--) {
@@ -2621,18 +2699,19 @@ ${tail}` : err.message;
2621
2699
  break;
2622
2700
  }
2623
2701
  }
2624
- if (!started || started.payload.kind !== "turn_started") return head;
2702
+ if (!started || started.payload.kind !== "turn_started") return { transcript, head };
2625
2703
  const promptText = stripAttachmentsLine(
2626
2704
  started.payload.prompt.filter((b) => b.type === "text").map((b) => b.text).join("")
2627
2705
  ).trim();
2706
+ let cut = transcript.length;
2628
2707
  for (let i = transcript.length - 1; i >= 0; i--) {
2629
2708
  const it = transcript[i];
2630
2709
  if (it.type === "message" && it.message.role === "user") {
2631
- if (promptText && it.message.text.trim() === promptText) return head;
2710
+ if (promptText && it.message.text.trim() === promptText) cut = i;
2632
2711
  break;
2633
2712
  }
2634
2713
  }
2635
- return started.seq - 1;
2714
+ return { transcript: transcript.slice(0, cut), head: started.seq - 1 };
2636
2715
  }
2637
2716
  evict(sessionId) {
2638
2717
  const s = this.sessions.get(sessionId);
@@ -2666,6 +2745,70 @@ ${tail}` : err.message;
2666
2745
  }
2667
2746
  });
2668
2747
 
2748
+ // server/src/session/models.ts
2749
+ import { execFile as execFile2 } from "node:child_process";
2750
+ import { promisify as promisify2 } from "node:util";
2751
+ async function configuredDefault() {
2752
+ try {
2753
+ const { stdout } = await execFileAsync2(config.kiroBin, [
2754
+ "settings",
2755
+ "chat.defaultModel",
2756
+ "-f",
2757
+ "json"
2758
+ ]);
2759
+ const value = JSON.parse(stdout);
2760
+ return typeof value === "string" && value ? value : void 0;
2761
+ } catch {
2762
+ return void 0;
2763
+ }
2764
+ }
2765
+ async function listModels(log) {
2766
+ if (cache2) return cache2;
2767
+ const { stdout } = await execFileAsync2(
2768
+ config.kiroBin,
2769
+ ["chat", "--list-models", "-f", "json"],
2770
+ { maxBuffer: 4 * 1024 * 1024 }
2771
+ );
2772
+ const parsed = JSON.parse(stdout);
2773
+ const configured = await configuredDefault();
2774
+ const preferred = parsed.models.some((m) => m.model_id === configured) ? configured : parsed.default_model;
2775
+ cache2 = parsed.models.map((m, i) => ({
2776
+ modelId: m.model_id,
2777
+ modelName: m.model_name,
2778
+ description: m.description,
2779
+ contextWindowTokens: m.context_window_tokens,
2780
+ rateMultiplier: m.rate_multiplier,
2781
+ rateUnit: m.rate_unit,
2782
+ isDefault: preferred ? m.model_id === preferred : m.default_model ?? i === 0
2783
+ }));
2784
+ log.info({ count: cache2.length, default: preferred }, "loaded model list");
2785
+ return cache2;
2786
+ }
2787
+ async function defaultModelId(log) {
2788
+ try {
2789
+ return (await listModels(log)).find((m) => m.isDefault)?.modelId;
2790
+ } catch {
2791
+ return void 0;
2792
+ }
2793
+ }
2794
+ async function kiroVersion() {
2795
+ try {
2796
+ const { stdout } = await execFileAsync2(config.kiroBin, ["--version"]);
2797
+ return stdout.trim();
2798
+ } catch {
2799
+ return void 0;
2800
+ }
2801
+ }
2802
+ var execFileAsync2, cache2;
2803
+ var init_models = __esm({
2804
+ "server/src/session/models.ts"() {
2805
+ "use strict";
2806
+ init_config();
2807
+ execFileAsync2 = promisify2(execFile2);
2808
+ cache2 = null;
2809
+ }
2810
+ });
2811
+
2669
2812
  // server/src/util/rateLimit.ts
2670
2813
  var AttemptLimiter;
2671
2814
  var init_rateLimit = __esm({
@@ -2812,63 +2955,6 @@ var init_auth = __esm({
2812
2955
  }
2813
2956
  });
2814
2957
 
2815
- // server/src/session/models.ts
2816
- import { execFile as execFile2 } from "node:child_process";
2817
- import { promisify as promisify2 } from "node:util";
2818
- async function configuredDefault() {
2819
- try {
2820
- const { stdout } = await execFileAsync2(config.kiroBin, [
2821
- "settings",
2822
- "chat.defaultModel",
2823
- "-f",
2824
- "json"
2825
- ]);
2826
- const value = JSON.parse(stdout);
2827
- return typeof value === "string" && value ? value : void 0;
2828
- } catch {
2829
- return void 0;
2830
- }
2831
- }
2832
- async function listModels(log) {
2833
- if (cache2) return cache2;
2834
- const { stdout } = await execFileAsync2(
2835
- config.kiroBin,
2836
- ["chat", "--list-models", "-f", "json"],
2837
- { maxBuffer: 4 * 1024 * 1024 }
2838
- );
2839
- const parsed = JSON.parse(stdout);
2840
- const configured = await configuredDefault();
2841
- const preferred = parsed.models.some((m) => m.model_id === configured) ? configured : parsed.default_model;
2842
- cache2 = parsed.models.map((m, i) => ({
2843
- modelId: m.model_id,
2844
- modelName: m.model_name,
2845
- description: m.description,
2846
- contextWindowTokens: m.context_window_tokens,
2847
- rateMultiplier: m.rate_multiplier,
2848
- rateUnit: m.rate_unit,
2849
- isDefault: preferred ? m.model_id === preferred : m.default_model ?? i === 0
2850
- }));
2851
- log.info({ count: cache2.length, default: preferred }, "loaded model list");
2852
- return cache2;
2853
- }
2854
- async function kiroVersion() {
2855
- try {
2856
- const { stdout } = await execFileAsync2(config.kiroBin, ["--version"]);
2857
- return stdout.trim();
2858
- } catch {
2859
- return void 0;
2860
- }
2861
- }
2862
- var execFileAsync2, cache2;
2863
- var init_models = __esm({
2864
- "server/src/session/models.ts"() {
2865
- "use strict";
2866
- init_config();
2867
- execFileAsync2 = promisify2(execFile2);
2868
- cache2 = null;
2869
- }
2870
- });
2871
-
2872
2958
  // server/src/routes/models.ts
2873
2959
  function registerModelRoutes(app) {
2874
2960
  app.get("/api/models", async () => {
@@ -2898,8 +2984,8 @@ var init_agents2 = __esm({
2898
2984
  });
2899
2985
 
2900
2986
  // server/src/util/confinedFile.ts
2901
- import fs11 from "node:fs/promises";
2902
- import path12 from "node:path";
2987
+ import fs12 from "node:fs/promises";
2988
+ import path13 from "node:path";
2903
2989
  async function resolveConfined(input, spec) {
2904
2990
  let target = null;
2905
2991
  for (const root of spec.lexical) {
@@ -2915,7 +3001,7 @@ async function resolveConfined(input, spec) {
2915
3001
  if (!real) return { ok: false, ...await spec.notFound() };
2916
3002
  let stat;
2917
3003
  try {
2918
- stat = await fs11.stat(real);
3004
+ stat = await fs12.stat(real);
2919
3005
  } catch {
2920
3006
  return { ok: false, ...await spec.notFound() };
2921
3007
  }
@@ -2954,7 +3040,7 @@ async function resolveChatPath(sessions, sessionId, requestedPath, require2) {
2954
3040
  async function workspaceNotFound(cwd) {
2955
3041
  let missing;
2956
3042
  try {
2957
- missing = !(await fs11.stat(cwd)).isDirectory();
3043
+ missing = !(await fs12.stat(cwd)).isDirectory();
2958
3044
  } catch {
2959
3045
  missing = true;
2960
3046
  }
@@ -2974,7 +3060,7 @@ async function resolveAbsolutePath(input, require2) {
2974
3060
  });
2975
3061
  }
2976
3062
  async function classifyDirent(parentReal, entry, realRoots) {
2977
- const absolute = path12.join(parentReal, entry.name);
3063
+ const absolute = path13.join(parentReal, entry.name);
2978
3064
  if (entry.isDirectory()) return { kind: "directory", real: absolute };
2979
3065
  if (entry.isFile()) return { kind: "file", real: absolute };
2980
3066
  if (!entry.isSymbolicLink()) return null;
@@ -2982,7 +3068,7 @@ async function classifyDirent(parentReal, entry, realRoots) {
2982
3068
  const real = await realConfineToRoot(root, absolute);
2983
3069
  if (!real) continue;
2984
3070
  try {
2985
- const stat = await fs11.stat(real);
3071
+ const stat = await fs12.stat(real);
2986
3072
  if (stat.isDirectory()) return { kind: "directory", real };
2987
3073
  if (stat.isFile()) return { kind: "file", real };
2988
3074
  } catch {
@@ -3000,7 +3086,7 @@ var init_confinedFile = __esm({
3000
3086
  });
3001
3087
 
3002
3088
  // server/src/util/filekind.ts
3003
- import path13 from "node:path";
3089
+ import path14 from "node:path";
3004
3090
  function mimeForExt(ext) {
3005
3091
  return MIME_MAP[ext.toLowerCase()] ?? "application/octet-stream";
3006
3092
  }
@@ -3020,7 +3106,7 @@ function looksBinary(buf) {
3020
3106
  return control / buf.length > 0.3;
3021
3107
  }
3022
3108
  function classifyKind(name) {
3023
- const ext = path13.extname(name).toLowerCase();
3109
+ const ext = path14.extname(name).toLowerCase();
3024
3110
  if (ext in IMAGE_EXTS) return "image";
3025
3111
  if (TEXT_EXTS.has(ext)) return "text";
3026
3112
  return "binary";
@@ -3130,9 +3216,9 @@ var init_filekind = __esm({
3130
3216
  });
3131
3217
 
3132
3218
  // server/src/routes/filePreview.ts
3133
- import fs12 from "node:fs/promises";
3219
+ import fs13 from "node:fs/promises";
3134
3220
  import { createReadStream } from "node:fs";
3135
- import path14 from "node:path";
3221
+ import path15 from "node:path";
3136
3222
  function tooLargeForPreview(size, maxBytes) {
3137
3223
  return {
3138
3224
  error: `File too large for preview (${(size / 1024 / 1024).toFixed(1)} MB, max ${maxBytes / 1024 / 1024} MB)`
@@ -3159,7 +3245,7 @@ function hexdump(buf) {
3159
3245
  return lines.join("\n");
3160
3246
  }
3161
3247
  async function sendFilePreview(req, reply, realTarget, stat) {
3162
- const ext = path14.extname(realTarget).toLowerCase();
3248
+ const ext = path15.extname(realTarget).toLowerCase();
3163
3249
  const mime = mimeForExt(ext);
3164
3250
  const isImage2 = mime.startsWith("image/");
3165
3251
  const isPdf = mime === "application/pdf";
@@ -3204,7 +3290,7 @@ async function sendFilePreview(req, reply, realTarget, stat) {
3204
3290
  if (kind === "binary") {
3205
3291
  let fh;
3206
3292
  try {
3207
- fh = await fs12.open(realTarget, "r");
3293
+ fh = await fs13.open(realTarget, "r");
3208
3294
  const buf = Buffer.alloc(Math.min(HEXDUMP_BYTES, stat.size));
3209
3295
  const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
3210
3296
  const head = buf.subarray(0, bytesRead);
@@ -3248,9 +3334,9 @@ var init_filePreview = __esm({
3248
3334
  });
3249
3335
 
3250
3336
  // server/src/routes/fs.ts
3251
- import fs13 from "node:fs/promises";
3337
+ import fs14 from "node:fs/promises";
3252
3338
  import { createReadStream as createReadStream2 } from "node:fs";
3253
- import path15 from "node:path";
3339
+ import path16 from "node:path";
3254
3340
  function registerFsRoutes(app) {
3255
3341
  app.get(
3256
3342
  "/api/fs/dirs",
@@ -3258,10 +3344,10 @@ function registerFsRoutes(app) {
3258
3344
  const input = (req.query.path ?? "").trim();
3259
3345
  const base = config.defaultCwd;
3260
3346
  const endsWithSep = input.endsWith("/");
3261
- const resolved = input ? path15.resolve(base, input) : base;
3262
- const dir = endsWithSep || !input ? resolved : path15.dirname(resolved);
3263
- const prefix = endsWithSep || !input ? "" : path15.basename(resolved);
3264
- const targetKind = await fs13.stat(resolved).then((s) => s.isDirectory() ? "directory" : "file").catch(() => "missing");
3347
+ const resolved = input ? path16.resolve(base, input) : base;
3348
+ const dir = endsWithSep || !input ? resolved : path16.dirname(resolved);
3349
+ const prefix = endsWithSep || !input ? "" : path16.basename(resolved);
3350
+ const targetKind = await fs14.stat(resolved).then((s) => s.isDirectory() ? "directory" : "file").catch(() => "missing");
3265
3351
  const listing = await resolveAbsolutePath(dir, "directory");
3266
3352
  if (!listing.ok) {
3267
3353
  if (listing.status === 403) return replyWith(reply, listing);
@@ -3270,7 +3356,7 @@ function registerFsRoutes(app) {
3270
3356
  const realDir = listing.real;
3271
3357
  let entries = [];
3272
3358
  try {
3273
- const dirents = await fs13.readdir(realDir, { withFileTypes: true });
3359
+ const dirents = await fs14.readdir(realDir, { withFileTypes: true });
3274
3360
  const checks2 = await Promise.all(
3275
3361
  dirents.map(async (d) => {
3276
3362
  const target = await classifyDirent(realDir, d, absoluteRoots());
@@ -3281,7 +3367,7 @@ function registerFsRoutes(app) {
3281
3367
  entries = checks2.filter((name) => name !== null).filter((name) => name.toLowerCase().startsWith(prefix.toLowerCase())).sort((a, b) => {
3282
3368
  if (isDot(a) !== isDot(b)) return isDot(a) ? 1 : -1;
3283
3369
  return a.localeCompare(b);
3284
- }).slice(0, 500).map((name) => path15.join(dir, name));
3370
+ }).slice(0, 500).map((name) => path16.join(dir, name));
3285
3371
  } catch {
3286
3372
  entries = [];
3287
3373
  }
@@ -3296,7 +3382,7 @@ function registerFsRoutes(app) {
3296
3382
  reply.code(400);
3297
3383
  return { error: "path parameter is required" };
3298
3384
  }
3299
- if (!path15.isAbsolute(filePath)) {
3385
+ if (!path16.isAbsolute(filePath)) {
3300
3386
  reply.code(400);
3301
3387
  return { error: "path must be absolute" };
3302
3388
  }
@@ -3306,7 +3392,7 @@ function registerFsRoutes(app) {
3306
3392
  reply.header("Content-Type", "application/octet-stream");
3307
3393
  reply.header(
3308
3394
  "Content-Disposition",
3309
- `attachment; filename="${path15.basename(resolved.real).replace(/"/g, "")}"`
3395
+ `attachment; filename="${path16.basename(resolved.real).replace(/"/g, "")}"`
3310
3396
  );
3311
3397
  reply.header("Content-Length", resolved.stat.size);
3312
3398
  return reply.send(createReadStream2(resolved.real));
@@ -3477,9 +3563,9 @@ var init_subagents = __esm({
3477
3563
  });
3478
3564
 
3479
3565
  // server/src/routes/workspace.ts
3480
- import fs14 from "node:fs/promises";
3566
+ import fs15 from "node:fs/promises";
3481
3567
  import { createReadStream as createReadStream3 } from "node:fs";
3482
- import path16 from "node:path";
3568
+ import path17 from "node:path";
3483
3569
  function registerWorkspaceRoutes(app, manager) {
3484
3570
  app.get(
3485
3571
  "/api/chats/:id/tree",
@@ -3494,7 +3580,7 @@ function registerWorkspaceRoutes(app, manager) {
3494
3580
  const { cwd, relative, real: realTarget } = resolved;
3495
3581
  let dirents;
3496
3582
  try {
3497
- dirents = await fs14.readdir(realTarget, { withFileTypes: true, encoding: "utf8" });
3583
+ dirents = await fs15.readdir(realTarget, { withFileTypes: true, encoding: "utf8" });
3498
3584
  } catch {
3499
3585
  return replyWith(reply, await workspaceNotFound(cwd));
3500
3586
  }
@@ -3508,7 +3594,7 @@ function registerWorkspaceRoutes(app, manager) {
3508
3594
  continue;
3509
3595
  }
3510
3596
  try {
3511
- const stat = await fs14.stat(target.real);
3597
+ const stat = await fs15.stat(target.real);
3512
3598
  entries.push({
3513
3599
  name: d.name,
3514
3600
  path: entryRelative,
@@ -3537,8 +3623,8 @@ function registerWorkspaceRoutes(app, manager) {
3537
3623
  reply.code(413);
3538
3624
  return { error: `File too large (${(stat.size / 1024 / 1024).toFixed(1)} MB, max 100 MB)` };
3539
3625
  }
3540
- const ext = path16.extname(realTarget);
3541
- const filename = path16.basename(realTarget);
3626
+ const ext = path17.extname(realTarget);
3627
+ const filename = path17.basename(realTarget);
3542
3628
  const asciiName = filename.replace(/[^\w.\-]/g, "_");
3543
3629
  reply.header("Content-Type", mimeForExt(ext));
3544
3630
  reply.header(
@@ -3573,27 +3659,27 @@ var init_workspace = __esm({
3573
3659
 
3574
3660
  // server/src/routes/uploads.ts
3575
3661
  import { createReadStream as createReadStream4, createWriteStream } from "node:fs";
3576
- import fs15 from "node:fs/promises";
3577
- import path17 from "node:path";
3662
+ import fs16 from "node:fs/promises";
3663
+ import path18 from "node:path";
3578
3664
  import crypto4 from "node:crypto";
3579
3665
  import { execFile as execFile3 } from "node:child_process";
3580
3666
  import { pipeline } from "node:stream/promises";
3581
3667
  function sanitizeName(raw) {
3582
- const base = path17.basename(raw).replace(/[^\w.\- ]/g, "_").trim();
3668
+ const base = path18.basename(raw).replace(/[^\w.\- ]/g, "_").trim();
3583
3669
  if (!base || base === "." || base === "..") return `upload-${Date.now()}`;
3584
3670
  return base.replace(/^\.+/, "") || `upload-${Date.now()}`;
3585
3671
  }
3586
3672
  async function uniquePath(dir, name) {
3587
- const ext = path17.extname(name);
3673
+ const ext = path18.extname(name);
3588
3674
  const stem = name.slice(0, name.length - ext.length);
3589
3675
  let candidate = name;
3590
3676
  let n = 1;
3591
3677
  while (true) {
3592
3678
  try {
3593
- await fs15.access(path17.join(dir, candidate));
3679
+ await fs16.access(path18.join(dir, candidate));
3594
3680
  candidate = `${stem} (${n++})${ext}`;
3595
3681
  } catch {
3596
- return path17.join(dir, candidate);
3682
+ return path18.join(dir, candidate);
3597
3683
  }
3598
3684
  }
3599
3685
  }
@@ -3613,7 +3699,7 @@ function fileType(absPath) {
3613
3699
  async function sampleStrings(absPath, limit = 40) {
3614
3700
  let fh;
3615
3701
  try {
3616
- fh = await fs15.open(absPath, "r");
3702
+ fh = await fs16.open(absPath, "r");
3617
3703
  const buf = Buffer.alloc(STRINGS_SCAN_BYTES);
3618
3704
  const { bytesRead } = await fh.read(buf, 0, STRINGS_SCAN_BYTES, 0);
3619
3705
  const out = [];
@@ -3645,7 +3731,7 @@ function registerUploadRoutes(app) {
3645
3731
  return { error: "Invalid chat id" };
3646
3732
  }
3647
3733
  const uploadDir = chatUploadsDir(req.params.chatId);
3648
- await fs15.mkdir(uploadDir, { recursive: true, mode: 448 });
3734
+ await fs16.mkdir(uploadDir, { recursive: true, mode: 448 });
3649
3735
  const results = [];
3650
3736
  const parts = req.files();
3651
3737
  for await (const part of parts) {
@@ -3659,17 +3745,17 @@ function registerUploadRoutes(app) {
3659
3745
  return { error: `Failed to store ${name}` };
3660
3746
  }
3661
3747
  if (part.file.truncated) {
3662
- await fs15.rm(dest, { force: true });
3748
+ await fs16.rm(dest, { force: true });
3663
3749
  reply.code(413);
3664
3750
  return {
3665
3751
  error: `${name} exceeds the max upload size (${Math.round(config.maxUploadBytes / 1024 / 1024)} MB)`
3666
3752
  };
3667
3753
  }
3668
- const stat = await fs15.stat(dest);
3669
- const ext = path17.extname(dest);
3754
+ const stat = await fs16.stat(dest);
3755
+ const ext = path18.extname(dest);
3670
3756
  const kind = classifyKind(dest);
3671
3757
  const uploaded = {
3672
- name: path17.basename(dest),
3758
+ name: path18.basename(dest),
3673
3759
  path: dest,
3674
3760
  size: stat.size,
3675
3761
  mimeType: mimeForExt(ext),
@@ -3725,7 +3811,7 @@ var init_health = __esm({
3725
3811
  });
3726
3812
 
3727
3813
  // server/src/ws/dirWatchers.ts
3728
- import fs16 from "node:fs";
3814
+ import fs17 from "node:fs";
3729
3815
  function diffWatchSet(current, next) {
3730
3816
  const have = new Set(current);
3731
3817
  const want = new Set(next.slice(0, MAX_WATCHES));
@@ -3749,7 +3835,7 @@ function createDirWatchers(opts) {
3749
3835
  const target = await opts.resolve(relative);
3750
3836
  if (!target || closed) return;
3751
3837
  try {
3752
- const watcher = fs16.watch(target, { persistent: false }, () => {
3838
+ const watcher = fs17.watch(target, { persistent: false }, () => {
3753
3839
  const existing = timers.get(relative);
3754
3840
  if (existing) clearTimeout(existing);
3755
3841
  timers.set(
@@ -3807,7 +3893,7 @@ function handleConnection(socket, manager, chatId, startCursor) {
3807
3893
  return null;
3808
3894
  }
3809
3895
  },
3810
- onChange: (path20) => send(socket, { type: "fs_changed", path: path20 })
3896
+ onChange: (path21) => send(socket, { type: "fs_changed", path: path21 })
3811
3897
  });
3812
3898
  const forward = (event) => {
3813
3899
  if (event.seq <= cursor) return;
@@ -3940,7 +4026,7 @@ var init_gateway = __esm({
3940
4026
  });
3941
4027
 
3942
4028
  // server/src/app.ts
3943
- import fs17 from "node:fs";
4029
+ import fs18 from "node:fs";
3944
4030
  import Fastify from "fastify";
3945
4031
  import websocket from "@fastify/websocket";
3946
4032
  import fastifyStatic from "@fastify/static";
@@ -3959,7 +4045,7 @@ async function buildApp() {
3959
4045
  transport: isProd ? void 0 : { target: "pino/file", options: { destination: 2 } }
3960
4046
  }
3961
4047
  });
3962
- const manager = new SessionManager(logger);
4048
+ const manager = new SessionManager(logger, { defaultModel: () => defaultModelId(logger) });
3963
4049
  const startedAt = Date.now();
3964
4050
  await app.register(websocket, {
3965
4051
  options: { maxPayload: 16 * 1024 * 1024 }
@@ -3977,20 +4063,20 @@ async function buildApp() {
3977
4063
  registerWorkspaceRoutes(app, manager);
3978
4064
  registerUploadRoutes(app);
3979
4065
  registerWsGateway(app, manager);
3980
- if (fs17.existsSync(config.webDist)) {
4066
+ if (fs18.existsSync(config.webDist)) {
3981
4067
  await app.register(fastifyStatic, {
3982
4068
  root: config.webDist
3983
4069
  });
3984
4070
  app.addHook("onSend", async (req, reply) => {
3985
- const path20 = req.url.split("?")[0] ?? "";
3986
- if (path20 === "/manifest.json" || path20.startsWith("/icons/")) {
4071
+ const path21 = req.url.split("?")[0] ?? "";
4072
+ if (path21 === "/manifest.json" || path21.startsWith("/icons/")) {
3987
4073
  reply.header("cache-control", "public, max-age=300");
3988
4074
  }
3989
4075
  });
3990
4076
  app.setNotFoundHandler((req, reply) => {
3991
- const path20 = req.url.split("?")[0] ?? "";
3992
- const looksLikeAsset = path20.startsWith("/assets/") || /\.[a-zA-Z0-9]+$/.test(path20);
3993
- if (path20.startsWith("/api/") || path20.startsWith("/ws") || looksLikeAsset) {
4077
+ const path21 = req.url.split("?")[0] ?? "";
4078
+ const looksLikeAsset = path21.startsWith("/assets/") || /\.[a-zA-Z0-9]+$/.test(path21);
4079
+ if (path21.startsWith("/api/") || path21.startsWith("/ws") || looksLikeAsset) {
3994
4080
  reply.code(404).send({ error: "Not found" });
3995
4081
  return;
3996
4082
  }
@@ -4005,6 +4091,7 @@ var init_app = __esm({
4005
4091
  init_config();
4006
4092
  init_logger();
4007
4093
  init_SessionManager();
4094
+ init_models();
4008
4095
  init_auth();
4009
4096
  init_models2();
4010
4097
  init_agents2();
@@ -4061,19 +4148,22 @@ var init_server = __esm({
4061
4148
  // server/src/cli/doctor.ts
4062
4149
  var doctor_exports = {};
4063
4150
  __export(doctor_exports, {
4151
+ agentFormat: () => agentFormat,
4064
4152
  doctor: () => doctor,
4153
+ kiroMajor: () => kiroMajor,
4065
4154
  mcpWiring: () => mcpWiring
4066
4155
  });
4067
4156
  import { execFileSync as execFileSync3 } from "node:child_process";
4068
- import fs18 from "node:fs";
4069
- import path18 from "node:path";
4157
+ import fs19 from "node:fs";
4158
+ import os4 from "node:os";
4159
+ import path19 from "node:path";
4070
4160
  function which(bin) {
4071
- if (bin.includes("/")) return fs18.existsSync(bin) ? bin : void 0;
4072
- for (const dir of (process.env.PATH ?? "").split(path18.delimiter)) {
4161
+ if (bin.includes("/")) return fs19.existsSync(bin) ? bin : void 0;
4162
+ for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
4073
4163
  if (dir === "") continue;
4074
- const candidate = path18.join(dir, bin);
4164
+ const candidate = path19.join(dir, bin);
4075
4165
  try {
4076
- fs18.accessSync(candidate, fs18.constants.X_OK);
4166
+ fs19.accessSync(candidate, fs19.constants.X_OK);
4077
4167
  return candidate;
4078
4168
  } catch {
4079
4169
  }
@@ -4097,11 +4187,28 @@ function mcpWiring(agentJson, resolve) {
4097
4187
  return { ok: false, detail: `${command || "(no command)"} not found - widgets are unavailable` };
4098
4188
  }
4099
4189
  const script = args.find((a) => a.endsWith(".js"));
4100
- if (script && !fs18.existsSync(script)) {
4190
+ if (script && !fs19.existsSync(script)) {
4101
4191
  return { ok: false, detail: `${script} is gone - delete the agent file and restart to rewrite it` };
4102
4192
  }
4103
4193
  return { ok: true, detail: script ?? `${command} ${args.join(" ")}` };
4104
4194
  }
4195
+ function agentFormat(agentJson) {
4196
+ let agent;
4197
+ try {
4198
+ agent = JSON.parse(agentJson);
4199
+ } catch {
4200
+ return { ok: false, detail: "the agent file is not valid JSON" };
4201
+ }
4202
+ const v2Fields = ["allowedTools", "toolsSettings"].filter((f) => f in agent);
4203
+ return v2Fields.length === 0 ? { ok: true, detail: "in the kiro V3 format" } : {
4204
+ ok: false,
4205
+ detail: `has ${v2Fields.join(" and ")}, so kiro V3 skips it - delete the file and restart to rewrite it`
4206
+ };
4207
+ }
4208
+ function kiroMajor(versionOutput) {
4209
+ const m = /(\d+)\.\d+\.\d+/.exec(versionOutput);
4210
+ return m ? Number(m[1]) : void 0;
4211
+ }
4105
4212
  function checks() {
4106
4213
  const out = [];
4107
4214
  out.push({
@@ -4117,7 +4224,10 @@ function checks() {
4117
4224
  } catch {
4118
4225
  version = "(version unavailable)";
4119
4226
  }
4120
- out.push({ ok: true, name: "kiro-cli", detail: `${version} at ${kiro}` });
4227
+ const major = kiroMajor(version);
4228
+ out.push(
4229
+ major === void 0 || major >= 3 ? { ok: true, name: "kiro-cli", detail: `${version} at ${kiro}` } : { ok: false, name: "kiro-cli", detail: `${version} at ${kiro} - Casper needs kiro-cli 3.0 or later` }
4230
+ );
4121
4231
  } else {
4122
4232
  out.push({
4123
4233
  ok: false,
@@ -4129,7 +4239,7 @@ function checks() {
4129
4239
  const settings = readSettings(config.configFile);
4130
4240
  const hasToken = typeof settings.token === "string" && settings.token !== "";
4131
4241
  out.push(
4132
- fs18.existsSync(config.configFile) ? {
4242
+ fs19.existsSync(config.configFile) ? {
4133
4243
  ok: hasToken,
4134
4244
  name: "settings",
4135
4245
  detail: hasToken ? `${config.configFile} (token set)` : `${config.configFile} exists but has no token - auth is DISABLED`
@@ -4139,8 +4249,8 @@ function checks() {
4139
4249
  detail: `${config.configFile} not found; a token is generated on first run`
4140
4250
  }
4141
4251
  );
4142
- if (fs18.existsSync(config.configFile)) {
4143
- const mode = fs18.statSync(config.configFile).mode & 511;
4252
+ if (fs19.existsSync(config.configFile)) {
4253
+ const mode = fs19.statSync(config.configFile).mode & 511;
4144
4254
  out.push({
4145
4255
  ok: mode === 384,
4146
4256
  name: "settings mode",
@@ -4148,10 +4258,10 @@ function checks() {
4148
4258
  });
4149
4259
  }
4150
4260
  try {
4151
- fs18.mkdirSync(config.casperDataDir, { recursive: true });
4152
- fs18.accessSync(config.casperDataDir, fs18.constants.W_OK);
4153
- const db2 = path18.join(config.casperDataDir, "casper.db");
4154
- const size = fs18.existsSync(db2) ? `, casper.db ${(fs18.statSync(db2).size / 1024).toFixed(0)} KB` : "";
4261
+ fs19.mkdirSync(config.casperDataDir, { recursive: true });
4262
+ fs19.accessSync(config.casperDataDir, fs19.constants.W_OK);
4263
+ const db2 = path19.join(config.casperDataDir, "casper.db");
4264
+ const size = fs19.existsSync(db2) ? `, casper.db ${(fs19.statSync(db2).size / 1024).toFixed(0)} KB` : "";
4155
4265
  out.push({ ok: true, name: "data dir", detail: `${config.casperDataDir} writable${size}` });
4156
4266
  } catch (err) {
4157
4267
  out.push({
@@ -4162,22 +4272,25 @@ function checks() {
4162
4272
  });
4163
4273
  }
4164
4274
  out.push(
4165
- fs18.existsSync(config.webDist) ? { ok: true, name: "web app", detail: config.webDist } : {
4275
+ fs19.existsSync(config.webDist) ? { ok: true, name: "web app", detail: config.webDist } : {
4166
4276
  ok: false,
4167
4277
  fatal: true,
4168
4278
  name: "web app",
4169
4279
  detail: `${config.webDist} not found - the package is incomplete or CASPER_WEB_DIST is wrong`
4170
4280
  }
4171
4281
  );
4172
- const agent = path18.join(config.kiroSessionsDir, "..", "..", "agents", "casper.json");
4173
- const hasAgent = fs18.existsSync(agent);
4282
+ const agent = path19.join(os4.homedir(), ".kiro", "agents", "casper.json");
4283
+ const hasAgent = fs19.existsSync(agent);
4174
4284
  out.push({
4175
4285
  ok: hasAgent,
4176
4286
  name: "casper agent",
4177
4287
  detail: hasAgent ? agent : `${agent} missing - it is installed on first run`
4178
4288
  });
4179
4289
  if (hasAgent) {
4180
- const wiring = mcpWiring(fs18.readFileSync(agent, "utf8"), which);
4290
+ const json = fs19.readFileSync(agent, "utf8");
4291
+ const format = agentFormat(json);
4292
+ out.push({ ok: format.ok, name: "agent format", detail: format.detail });
4293
+ const wiring = mcpWiring(json, which);
4181
4294
  out.push({ ok: wiring.ok, name: "casper mcp", detail: wiring.detail });
4182
4295
  }
4183
4296
  return out;
@@ -4641,14 +4754,14 @@ var init_server2 = __esm({
4641
4754
  "server/src/mcp/server.ts"() {
4642
4755
  "use strict";
4643
4756
  init_protocol();
4644
- VERSION = true ? "0.15.6" : "dev";
4757
+ VERSION = true ? "0.16.0" : "dev";
4645
4758
  }
4646
4759
  });
4647
4760
 
4648
4761
  // server/src/cli/program.ts
4649
- import fs19 from "node:fs";
4650
- import os4 from "node:os";
4651
- import path19 from "node:path";
4762
+ import fs20 from "node:fs";
4763
+ import os5 from "node:os";
4764
+ import path20 from "node:path";
4652
4765
 
4653
4766
  // server/node_modules/commander/lib/error.js
4654
4767
  var CommanderError = class extends Error {
@@ -5239,8 +5352,8 @@ var Help = class {
5239
5352
  * @param {string} str
5240
5353
  * @returns {number}
5241
5354
  */
5242
- displayWidth(str2) {
5243
- return stripVTControlCharacters(str2).length;
5355
+ displayWidth(str3) {
5356
+ return stripVTControlCharacters(str3).length;
5244
5357
  }
5245
5358
  /**
5246
5359
  * Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
@@ -5248,11 +5361,11 @@ var Help = class {
5248
5361
  * @param {string} str
5249
5362
  * @returns {string}
5250
5363
  */
5251
- styleTitle(str2) {
5252
- return str2;
5364
+ styleTitle(str3) {
5365
+ return str3;
5253
5366
  }
5254
- styleUsage(str2) {
5255
- return str2.split(" ").map((word) => {
5367
+ styleUsage(str3) {
5368
+ return str3.split(" ").map((word) => {
5256
5369
  if (word === "[options]") return this.styleOptionText(word);
5257
5370
  if (word === "[command]") return this.styleSubcommandText(word);
5258
5371
  if (word[0] === "[" || word[0] === "<")
@@ -5260,46 +5373,46 @@ var Help = class {
5260
5373
  return this.styleCommandText(word);
5261
5374
  }).join(" ");
5262
5375
  }
5263
- styleCommandDescription(str2) {
5264
- return this.styleDescriptionText(str2);
5376
+ styleCommandDescription(str3) {
5377
+ return this.styleDescriptionText(str3);
5265
5378
  }
5266
- styleOptionDescription(str2) {
5267
- return this.styleDescriptionText(str2);
5379
+ styleOptionDescription(str3) {
5380
+ return this.styleDescriptionText(str3);
5268
5381
  }
5269
- styleSubcommandDescription(str2) {
5270
- return this.styleDescriptionText(str2);
5382
+ styleSubcommandDescription(str3) {
5383
+ return this.styleDescriptionText(str3);
5271
5384
  }
5272
- styleArgumentDescription(str2) {
5273
- return this.styleDescriptionText(str2);
5385
+ styleArgumentDescription(str3) {
5386
+ return this.styleDescriptionText(str3);
5274
5387
  }
5275
- styleDescriptionText(str2) {
5276
- return str2;
5388
+ styleDescriptionText(str3) {
5389
+ return str3;
5277
5390
  }
5278
- styleOptionTerm(str2) {
5279
- return this.styleOptionText(str2);
5391
+ styleOptionTerm(str3) {
5392
+ return this.styleOptionText(str3);
5280
5393
  }
5281
- styleSubcommandTerm(str2) {
5282
- return str2.split(" ").map((word) => {
5394
+ styleSubcommandTerm(str3) {
5395
+ return str3.split(" ").map((word) => {
5283
5396
  if (word === "[options]") return this.styleOptionText(word);
5284
5397
  if (word[0] === "[" || word[0] === "<")
5285
5398
  return this.styleArgumentText(word);
5286
5399
  return this.styleSubcommandText(word);
5287
5400
  }).join(" ");
5288
5401
  }
5289
- styleArgumentTerm(str2) {
5290
- return this.styleArgumentText(str2);
5402
+ styleArgumentTerm(str3) {
5403
+ return this.styleArgumentText(str3);
5291
5404
  }
5292
- styleOptionText(str2) {
5293
- return str2;
5405
+ styleOptionText(str3) {
5406
+ return str3;
5294
5407
  }
5295
- styleArgumentText(str2) {
5296
- return str2;
5408
+ styleArgumentText(str3) {
5409
+ return str3;
5297
5410
  }
5298
- styleSubcommandText(str2) {
5299
- return str2;
5411
+ styleSubcommandText(str3) {
5412
+ return str3;
5300
5413
  }
5301
- styleCommandText(str2) {
5302
- return str2;
5414
+ styleCommandText(str3) {
5415
+ return str3;
5303
5416
  }
5304
5417
  /**
5305
5418
  * Calculate the pad width from the maximum term length.
@@ -5322,8 +5435,8 @@ var Help = class {
5322
5435
  * @param {string} str
5323
5436
  * @returns {boolean}
5324
5437
  */
5325
- preformatted(str2) {
5326
- return /\n[^\S\r\n]/.test(str2);
5438
+ preformatted(str3) {
5439
+ return /\n[^\S\r\n]/.test(str3);
5327
5440
  }
5328
5441
  /**
5329
5442
  * Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
@@ -5369,9 +5482,9 @@ ${itemIndentStr}`);
5369
5482
  * @param {number} width
5370
5483
  * @returns {string}
5371
5484
  */
5372
- boxWrap(str2, width) {
5373
- if (width < this.minWidthToWrap) return str2;
5374
- const rawLines = str2.split(/\r\n|\n/);
5485
+ boxWrap(str3, width) {
5486
+ if (width < this.minWidthToWrap) return str3;
5487
+ const rawLines = str3.split(/\r\n|\n/);
5375
5488
  const chunkPattern = /[\s]*[^\s]+/g;
5376
5489
  const wrappedLines = [];
5377
5490
  rawLines.forEach((line) => {
@@ -5661,9 +5774,9 @@ var DualOptions = class {
5661
5774
  return option.negate === (negativeValue === value);
5662
5775
  }
5663
5776
  };
5664
- function camelcase(str2) {
5665
- return str2.split("-").reduce((str3, word) => {
5666
- return str3 + word[0].toUpperCase() + word.slice(1);
5777
+ function camelcase(str3) {
5778
+ return str3.split("-").reduce((str4, word) => {
5779
+ return str4 + word[0].toUpperCase() + word.slice(1);
5667
5780
  });
5668
5781
  }
5669
5782
  function splitOptionFlags(flags) {
@@ -5823,14 +5936,14 @@ var Command = class _Command extends EventEmitter {
5823
5936
  this._showSuggestionAfterError = true;
5824
5937
  this._savedState = null;
5825
5938
  this._outputConfiguration = {
5826
- writeOut: (str2) => process2.stdout.write(str2),
5827
- writeErr: (str2) => process2.stderr.write(str2),
5828
- outputError: (str2, write) => write(str2),
5939
+ writeOut: (str3) => process2.stdout.write(str3),
5940
+ writeErr: (str3) => process2.stderr.write(str3),
5941
+ outputError: (str3, write) => write(str3),
5829
5942
  getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : void 0,
5830
5943
  getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : void 0,
5831
5944
  getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
5832
5945
  getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
5833
- stripColor: (str2) => stripVTControlCharacters2(str2)
5946
+ stripColor: (str3) => stripVTControlCharacters2(str3)
5834
5947
  };
5835
5948
  this._hidden = false;
5836
5949
  this._helpOption = void 0;
@@ -7558,18 +7671,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
7558
7671
  * @param {string} [description]
7559
7672
  * @return {(this | string | undefined)} `this` command for chaining, or version string if no arguments
7560
7673
  */
7561
- version(str2, flags, description) {
7562
- if (str2 === void 0) return this._version;
7563
- this._version = str2;
7674
+ version(str3, flags, description) {
7675
+ if (str3 === void 0) return this._version;
7676
+ this._version = str3;
7564
7677
  flags = flags || "-V, --version";
7565
7678
  description = description || "output the version number";
7566
7679
  const versionOption = this.createOption(flags, description);
7567
7680
  this._versionOptionName = versionOption.attributeName();
7568
7681
  this._registerOption(versionOption);
7569
7682
  this.on("option:" + versionOption.name(), () => {
7570
- this._outputConfiguration.writeOut(`${str2}
7683
+ this._outputConfiguration.writeOut(`${str3}
7571
7684
  `);
7572
- this._exit(0, "commander.version", str2);
7685
+ this._exit(0, "commander.version", str3);
7573
7686
  });
7574
7687
  return this;
7575
7688
  }
@@ -7580,10 +7693,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
7580
7693
  * @param {object} [argsDescription]
7581
7694
  * @return {(string|Command)}
7582
7695
  */
7583
- description(str2, argsDescription) {
7584
- if (str2 === void 0 && argsDescription === void 0)
7696
+ description(str3, argsDescription) {
7697
+ if (str3 === void 0 && argsDescription === void 0)
7585
7698
  return this._description;
7586
- this._description = str2;
7699
+ this._description = str3;
7587
7700
  if (argsDescription) {
7588
7701
  this._argsDescription = argsDescription;
7589
7702
  }
@@ -7595,9 +7708,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7595
7708
  * @param {string} [str]
7596
7709
  * @return {(string|Command)}
7597
7710
  */
7598
- summary(str2) {
7599
- if (str2 === void 0) return this._summary;
7600
- this._summary = str2;
7711
+ summary(str3) {
7712
+ if (str3 === void 0) return this._summary;
7713
+ this._summary = str3;
7601
7714
  return this;
7602
7715
  }
7603
7716
  /**
@@ -7645,8 +7758,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
7645
7758
  * @param {string} [str]
7646
7759
  * @return {(string|Command)}
7647
7760
  */
7648
- usage(str2) {
7649
- if (str2 === void 0) {
7761
+ usage(str3) {
7762
+ if (str3 === void 0) {
7650
7763
  if (this._usage) return this._usage;
7651
7764
  const args = this.registeredArguments.map((arg) => {
7652
7765
  return humanReadableArgName(arg);
@@ -7657,7 +7770,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
7657
7770
  this.registeredArguments.length ? args : []
7658
7771
  ).join(" ");
7659
7772
  }
7660
- this._usage = str2;
7773
+ this._usage = str3;
7661
7774
  return this;
7662
7775
  }
7663
7776
  /**
@@ -7666,9 +7779,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7666
7779
  * @param {string} [str]
7667
7780
  * @return {(string|Command)}
7668
7781
  */
7669
- name(str2) {
7670
- if (str2 === void 0) return this._name;
7671
- this._name = str2;
7782
+ name(str3) {
7783
+ if (str3 === void 0) return this._name;
7784
+ this._name = str3;
7672
7785
  return this;
7673
7786
  }
7674
7787
  /**
@@ -7761,9 +7874,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7761
7874
  * @param {string} [path]
7762
7875
  * @return {(string|null|Command)}
7763
7876
  */
7764
- executableDir(path20) {
7765
- if (path20 === void 0) return this._executableDir;
7766
- this._executableDir = path20;
7877
+ executableDir(path21) {
7878
+ if (path21 === void 0) return this._executableDir;
7879
+ this._executableDir = path21;
7767
7880
  return this;
7768
7881
  }
7769
7882
  /**
@@ -7802,17 +7915,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
7802
7915
  let hasColors;
7803
7916
  let helpWidth;
7804
7917
  if (error) {
7805
- baseWrite = (str2) => this._outputConfiguration.writeErr(str2);
7918
+ baseWrite = (str3) => this._outputConfiguration.writeErr(str3);
7806
7919
  hasColors = this._outputConfiguration.getErrHasColors();
7807
7920
  helpWidth = this._outputConfiguration.getErrHelpWidth();
7808
7921
  } else {
7809
- baseWrite = (str2) => this._outputConfiguration.writeOut(str2);
7922
+ baseWrite = (str3) => this._outputConfiguration.writeOut(str3);
7810
7923
  hasColors = this._outputConfiguration.getOutHasColors();
7811
7924
  helpWidth = this._outputConfiguration.getOutHelpWidth();
7812
7925
  }
7813
- const write = (str2) => {
7814
- if (!hasColors) str2 = this._outputConfiguration.stripColor(str2);
7815
- return baseWrite(str2);
7926
+ const write = (str3) => {
7927
+ if (!hasColors) str3 = this._outputConfiguration.stripColor(str3);
7928
+ return baseWrite(str3);
7816
7929
  };
7817
7930
  return { error, write, hasColors, helpWidth };
7818
7931
  }
@@ -8021,7 +8134,7 @@ var program = new Command();
8021
8134
  // server/src/cli/program.ts
8022
8135
  init_paths();
8023
8136
  init_settings();
8024
- var VERSION2 = true ? "0.15.6" : "dev";
8137
+ var VERSION2 = true ? "0.16.0" : "dev";
8025
8138
  async function bootstrap() {
8026
8139
  const file = configFilePath();
8027
8140
  const settings = readSettings(file);
@@ -8034,7 +8147,7 @@ async function bootstrap() {
8034
8147
  `);
8035
8148
  }
8036
8149
  const { installAgentFile: installAgentFile2 } = await Promise.resolve().then(() => (init_agentFile(), agentFile_exports));
8037
- const agent = installAgentFile2(os4.homedir(), dataDirPath());
8150
+ const agent = installAgentFile2(os5.homedir(), dataDirPath());
8038
8151
  if (agent.action === "installed" || agent.action === "updated") {
8039
8152
  process.stdout.write(`casper agent ${agent.action}: ${agent.target}
8040
8153
  `);
@@ -8067,8 +8180,8 @@ async function resetToken(value) {
8067
8180
  updateSettings(file, { token });
8068
8181
  process.stdout.write(`wrote a new token to ${file}
8069
8182
  `);
8070
- const dbFile = path19.join(dataDirPath(), "casper.db");
8071
- if (fs19.existsSync(dbFile)) {
8183
+ const dbFile = path20.join(dataDirPath(), "casper.db");
8184
+ if (fs20.existsSync(dbFile)) {
8072
8185
  const { LoginStore: LoginStore2 } = await Promise.resolve().then(() => (init_logins(), logins_exports));
8073
8186
  new LoginStore2().revokeAll();
8074
8187
  process.stdout.write("revoked all device sessions\n");