@joeyshi12/casper 0.15.7 → 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 +830 -733
  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,302 +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];
1433
- }
1434
- return void 0;
1435
- }
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
- });
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;
1456
1477
  }
1457
- return resolved;
1478
+ return false;
1458
1479
  }
1459
- /** Called when the parent turn ends. The `subagent` tool call blocks the turn, so
1460
- * any subagent still working was stopped with it, and kiro does not always send a
1461
- * list_update saying so. Returns whether anything changed. */
1462
- turnEnded(now = () => (/* @__PURE__ */ new Date()).toISOString()) {
1463
- let changed = this.pendingCalls.length > 0;
1464
- this.pendingCalls.length = 0;
1465
- for (const s of this.byChild.values()) {
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()) {
1466
1485
  if (s.status !== "working") continue;
1467
1486
  s.status = "failed";
1468
1487
  s.activity = "Stopped";
1469
- s.updatedAt = now();
1488
+ s.updatedAt = this.now();
1470
1489
  changed = true;
1471
1490
  }
1472
1491
  return changed;
1473
1492
  }
1474
- /** Started stages plus a synthetic 'pending' row for each declared stage that
1475
- * hasn't started, so a stage waiting on a dependency still shows up. */
1476
- listForCall(toolCallId) {
1477
- const call = this.pendingCalls.find((c) => c.toolCallId === toolCallId);
1478
- const started = [...this.byChild.values()].filter((s) => s.toolCallId === toolCallId);
1479
- if (!call) return started.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1480
- const startedNames = new Set(started.map((s) => s.stageName));
1481
- const pending = call.stages.filter((s) => !startedNames.has(s.name)).map((s) => ({
1482
- sessionId: `pending:${toolCallId}:${s.name}`,
1483
- stageName: s.name,
1484
- toolCallId,
1485
- status: "pending",
1486
- activity: s.depends_on?.length ? `Waits for ${s.depends_on.join(" and ")}` : void 0,
1487
- createdAt: "",
1488
- updatedAt: ""
1489
- }));
1490
- return [...started.sort((a, b) => a.createdAt.localeCompare(b.createdAt)), ...pending];
1491
- }
1493
+ /** Copies, so a recorded event keeps the state it was recorded with. */
1492
1494
  list() {
1493
- 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));
1494
1496
  }
1495
- get(sessionId) {
1496
- return this.byChild.get(sessionId);
1497
+ get(subagentId) {
1498
+ const s = this.byId.get(subagentId);
1499
+ return s && { ...s };
1497
1500
  }
1498
1501
  };
1499
1502
  }
1500
1503
  });
1501
1504
 
1502
- // server/src/session/subagentResult.ts
1503
- function showSummaryAsAnswer(transcript) {
1504
- const out = [];
1505
- for (const item of transcript) {
1506
- if (item.type === "message" && item.message.role === "assistant" && item.message.text.trim() === INTERRUPTED) {
1507
- continue;
1508
- }
1509
- if (item.type === "tool_call" && item.tool.name === "summary") {
1510
- const input = item.tool.input;
1511
- const result = typeof input?.taskResult === "string" ? input.taskResult.trim() : "";
1512
- if (result) out.push({ type: "message", message: { id: item.tool.id, role: "assistant", text: result } });
1513
- continue;
1514
- }
1515
- out.push(item);
1516
- }
1517
- return out;
1518
- }
1519
- var INTERRUPTED;
1520
- var init_subagentResult = __esm({
1521
- "server/src/session/subagentResult.ts"() {
1522
- "use strict";
1523
- INTERRUPTED = "Tool uses were interrupted, waiting for the next user prompt";
1524
- }
1525
- });
1526
-
1527
- // server/src/session/subagentFallbackMatch.ts
1528
- function isStageInput(v) {
1529
- return typeof v === "object" && v !== null && typeof v.name === "string";
1530
- }
1531
- function subagentCallInput(tool) {
1532
- if (tool.name !== "subagent") return null;
1533
- const input = tool.input;
1534
- if (!input || !Array.isArray(input.stages)) return null;
1535
- const stages = input.stages.filter(isStageInput);
1536
- return { task: typeof input.task === "string" ? input.task : void 0, stages };
1537
- }
1538
- function expectedTitle(call, stage) {
1539
- const task = call.task ?? "";
1540
- const expanded = stage.prompt_template ? stage.prompt_template.replace("{task}", task) : task;
1541
- return expanded.slice(0, TITLE_MAX_LENGTH);
1542
- }
1543
- function subagentCallsIn(transcript) {
1544
- const calls = [];
1545
- transcript.forEach((item, itemIndex) => {
1546
- if (item.type !== "tool_call") return;
1547
- const input = subagentCallInput(item.tool);
1548
- if (!input) return;
1549
- calls.push({ toolCallId: item.tool.id, itemIndex, input });
1550
- });
1551
- return calls;
1552
- }
1553
- function matchSubagentsFallback(calls, children) {
1554
- const sortedCalls = [...calls].sort((a, b) => a.itemIndex - b.itemIndex);
1555
- const unclaimed = [...children].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
1556
- const matches = [];
1557
- for (const call of sortedCalls) {
1558
- for (const stage of call.input.stages) {
1559
- const want = expectedTitle(call.input, stage);
1560
- if (!want) continue;
1561
- const i = unclaimed.findIndex((c) => want.startsWith(c.title) || c.title.startsWith(want));
1562
- if (i < 0) continue;
1563
- const child = unclaimed.splice(i, 1)[0];
1564
- matches.push({ sessionId: child.sessionId, toolCallId: call.toolCallId, stageName: stage.name });
1565
- }
1566
- }
1567
- return matches;
1568
- }
1569
- var TITLE_MAX_LENGTH;
1570
- var init_subagentFallbackMatch = __esm({
1571
- "server/src/session/subagentFallbackMatch.ts"() {
1572
- "use strict";
1573
- TITLE_MAX_LENGTH = 150;
1574
- }
1575
- });
1576
-
1577
- // server/src/session/kiroFiles.ts
1505
+ // server/src/session/kiroV3Files.ts
1578
1506
  import fs9 from "node:fs/promises";
1579
1507
  import path10 from "node:path";
1580
- function isContentBlock(v) {
1581
- return isRecord(v) && typeof v.kind === "string";
1582
- }
1583
- function toolUseData(b) {
1584
- if (b.kind !== "toolUse" || !isRecord(b.data)) return null;
1585
- const { toolUseId, name, input } = b.data;
1586
- if (typeof toolUseId !== "string") return null;
1587
- return { toolUseId, name: typeof name === "string" ? name : void 0, input };
1588
- }
1589
- function toolResultData(b) {
1590
- if (b.kind !== "toolResult" || !isRecord(b.data)) return null;
1591
- const { toolUseId, status, content } = b.data;
1592
- if (typeof toolUseId !== "string") return null;
1593
- return { toolUseId, status: typeof status === "string" ? status : void 0, content };
1594
- }
1595
- function contentBlocks(content) {
1596
- return Array.isArray(content) ? content.filter(isContentBlock) : [];
1597
- }
1598
- function blockText(c) {
1599
- if (typeof c.data === "string") return c.data;
1600
- if (isRecord(c.data) && typeof c.data.text === "string") return c.data.text;
1601
- 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();
1602
1512
  }
1603
- function renderableBlocks(content) {
1604
- 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
+ }
1605
1521
  }
1606
- function summarize(j) {
1607
- const state = j.session_state;
1608
- const turns = state?.conversation_metadata?.user_turn_metadatas ?? [];
1609
- const contextUsagePercentage = state?.rts_model_state?.context_usage_percentage ?? turns[turns.length - 1]?.context_usage_percentage;
1610
- return {
1611
- sessionId: j.session_id,
1612
- title: j.title?.trim() ?? "",
1613
- cwd: j.cwd,
1614
- createdAt: j.created_at,
1615
- updatedAt: j.updated_at,
1616
- agentId: state?.agent_name,
1617
- modelId: state?.rts_model_state?.model_info?.model_id,
1618
- contextUsagePercentage
1619
- };
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;
1620
1537
  }
1621
- async function deletePersistedSession(sessionId) {
1622
- if (!isValidSessionId(sessionId)) return;
1623
- const targets = [
1624
- path10.join(config.kiroSessionsDir, `${sessionId}.json`),
1625
- path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`),
1626
- path10.join(config.kiroSessionsDir, `${sessionId}.history`),
1627
- path10.join(config.kiroSessionsDir, `${sessionId}.lock`),
1628
- path10.join(config.kiroSessionsDir, sessionId)
1629
- ];
1630
- 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);
1631
1552
  }
1632
- async function readPersistedSession(sessionId) {
1633
- if (!isValidSessionId(sessionId)) return null;
1553
+ function contextUsageOf(line) {
1554
+ if (!line.includes('"contextUsage"')) return void 0;
1634
1555
  try {
1635
- const raw = await fs9.readFile(
1636
- path10.join(config.kiroSessionsDir, `${sessionId}.json`),
1637
- "utf8"
1638
- );
1639
- 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;
1640
1559
  } catch {
1641
- return null;
1560
+ return void 0;
1642
1561
  }
1643
1562
  }
1644
- async function withLastWrite(child) {
1563
+ async function lastContextUsage(file) {
1564
+ let handle2;
1645
1565
  try {
1646
- const st = await fs9.stat(path10.join(config.kiroSessionsDir, `${child.sessionId}.jsonl`));
1647
- const mtime = st.mtime.toISOString();
1648
- return mtime > child.updatedAt ? { ...child, updatedAt: mtime } : child;
1566
+ handle2 = await fs9.open(file, "r");
1649
1567
  } catch {
1650
- return child;
1568
+ return void 0;
1651
1569
  }
1652
- }
1653
- async function readChildSession(childSessionId, parentSessionId) {
1654
- if (!isValidSessionId(childSessionId)) return null;
1655
1570
  try {
1656
- const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, `${childSessionId}.json`), "utf8");
1657
- const j = JSON.parse(raw);
1658
- return j.parent_session_id === parentSessionId ? await withLastWrite({ ...summarize(j), parentSessionId }) : null;
1659
- } catch {
1660
- 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();
1661
1589
  }
1662
1590
  }
1663
- async function listChildSessions(parentSessionId) {
1664
- if (!isValidSessionId(parentSessionId)) return [];
1665
- let names;
1591
+ async function readV3Session(dir) {
1592
+ let j;
1666
1593
  try {
1667
- names = await fs9.readdir(config.kiroSessionsDir);
1594
+ j = JSON.parse(await fs9.readFile(path10.join(dir, "session.json"), "utf8"));
1668
1595
  } catch {
1669
- return [];
1596
+ return null;
1670
1597
  }
1671
- const out = [];
1672
- await Promise.all(
1673
- names.filter((n) => n.endsWith(".json")).map(async (n) => {
1674
- try {
1675
- const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, n), "utf8");
1676
- const j = JSON.parse(raw);
1677
- if (j.parent_session_id === parentSessionId) {
1678
- out.push(await withLastWrite({ ...summarize(j), parentSessionId }));
1679
- }
1680
- } catch {
1681
- }
1682
- })
1683
- );
1684
- return out;
1685
- }
1686
- async function promptCount(sessionId) {
1687
- const entries = await readJsonlEntries(sessionId);
1688
- 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
+ };
1689
1610
  }
1690
- async function readJsonlEntries(sessionId) {
1691
- if (!isValidSessionId(sessionId)) return [];
1611
+ async function readLines(file, filter) {
1692
1612
  let raw;
1693
1613
  try {
1694
- raw = await fs9.readFile(path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`), "utf8");
1614
+ raw = await fs9.readFile(file, "utf8");
1695
1615
  } catch {
1696
1616
  return [];
1697
1617
  }
1698
- const entries = [];
1618
+ const out = [];
1699
1619
  for (const line of raw.split("\n")) {
1700
- const trimmed = line.trim();
1701
- if (!trimmed) continue;
1620
+ if (!line.trim() || filter && !line.includes(filter)) continue;
1702
1621
  try {
1703
- 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);
1704
1624
  } catch {
1705
1625
  }
1706
1626
  }
1707
- return entries;
1627
+ return out;
1708
1628
  }
1709
- async function hasRecordedTurns(sessionId) {
1710
- if (!isValidSessionId(sessionId)) return false;
1711
- try {
1712
- const st = await fs9.stat(path10.join(config.kiroSessionsDir, `${sessionId}.jsonl`));
1713
- return st.size > 0;
1714
- } catch {
1715
- 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
+ }
1716
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
+ }
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;
1717
1818
  }
1718
1819
  function endingItems(e) {
1719
1820
  const prefix = `te${e.id}-`;
@@ -1725,93 +1826,71 @@ function endingItems(e) {
1725
1826
  );
1726
1827
  return items;
1727
1828
  }
1728
- async function hydrateTranscript(sessionId, attachments, endings = []) {
1829
+ function mergeTranscript(entries, attachments, endings) {
1729
1830
  const items = [];
1730
1831
  let userOrdinal = 0;
1731
- const toolsById = /* @__PURE__ */ new Map();
1732
1832
  const waiting = [...endings];
1733
1833
  let replaced = false;
1734
1834
  const unrecorded = (e) => [
1735
1835
  { type: "message", message: { id: `te${e.id}-u`, role: "user", text: e.prompt, timestamp: e.startedAt } },
1736
1836
  ...endingItems(e)
1737
1837
  ];
1738
- for (const entry of await readJsonlEntries(sessionId)) {
1739
- const content = contentBlocks(entry.data.content);
1740
- const textOf = (kind) => content.filter((c) => c.kind === kind).map(blockText).join("");
1741
- const baseId = entry.data.message_id ?? `${items.length}`;
1742
- const ts = entry.data.meta?.timestamp;
1743
- const pushMsg = (msg) => items.push({ type: "message", message: msg });
1744
- if (entry.kind === "Prompt") {
1745
- replaced = false;
1746
- const text2 = stripAttachmentsLine(textOf("text"));
1747
- let matched;
1748
- if (ts !== void 0) {
1749
- const at = ts * 1e3;
1750
- matched = waiting.find(
1751
- (e) => e.prompt.trim() === text2.trim() && at >= e.startedAt - STAMP_PAD_MS && at <= e.endedAt + STAMP_PAD_MS
1752
- );
1753
- while (waiting.length && waiting[0] !== matched && waiting[0].endedAt < at - STAMP_PAD_MS) {
1754
- items.push(...unrecorded(waiting.shift()));
1755
- }
1756
- if (matched) waiting.splice(waiting.indexOf(matched), 1);
1757
- }
1758
- const attached = attachments?.get(userOrdinal);
1759
- userOrdinal++;
1760
- if (text2.trim() || attached?.length)
1761
- pushMsg({ id: `u-${baseId}`, role: "user", text: text2, timestamp: ts, attachments: attached });
1762
- if (matched) {
1763
- items.push(...endingItems(matched));
1764
- replaced = true;
1765
- }
1766
- } else if (entry.kind === "AssistantMessage") {
1767
- if (replaced) continue;
1768
- const thinking = textOf("thinking");
1769
- if (thinking.trim())
1770
- pushMsg({ id: `t-${baseId}`, role: "thinking", text: thinking.trim(), timestamp: ts });
1771
- const text2 = textOf("text");
1772
- if (text2.trim())
1773
- pushMsg({ id: `a-${baseId}`, role: "assistant", text: text2, timestamp: ts });
1774
- for (const c of content) {
1775
- const d = toolUseData(c);
1776
- if (!d) continue;
1777
- const tool = {
1778
- id: d.toolUseId,
1779
- name: d.name,
1780
- title: d.name ?? d.toolUseId,
1781
- status: "completed",
1782
- input: d.input,
1783
- content: []
1784
- };
1785
- toolsById.set(d.toolUseId, tool);
1786
- items.push({ type: "tool_call", tool });
1787
- }
1788
- } else if (entry.kind === "ToolResults") {
1789
- if (replaced) continue;
1790
- for (const c of content) {
1791
- const d = toolResultData(c);
1792
- if (!d) continue;
1793
- const tool = toolsById.get(d.toolUseId);
1794
- if (!tool) continue;
1795
- tool.status = d.status === "error" ? "failed" : "completed";
1796
- 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()));
1797
1853
  }
1798
- } else if (entry.kind === "Compaction") {
1799
- const summary = entry.data.summary ?? "";
1800
- if (summary.trim())
1801
- 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;
1802
1864
  }
1803
1865
  }
1804
1866
  for (const e of waiting) items.push(...unrecorded(e));
1805
1867
  return items;
1806
1868
  }
1807
- 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;
1808
1888
  var init_kiroFiles = __esm({
1809
1889
  "server/src/session/kiroFiles.ts"() {
1810
1890
  "use strict";
1811
1891
  init_dist();
1812
- init_config();
1813
1892
  init_paths2();
1814
- isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
1893
+ init_kiroV3Files();
1815
1894
  STAMP_PAD_MS = 2e3;
1816
1895
  }
1817
1896
  });
@@ -1933,50 +2012,18 @@ var init_chatStore = __esm({
1933
2012
  }
1934
2013
  });
1935
2014
 
1936
- // server/src/session/subagentLinks.ts
1937
- var SubagentLinkStore;
1938
- var init_subagentLinks = __esm({
1939
- "server/src/session/subagentLinks.ts"() {
1940
- "use strict";
1941
- init_db();
1942
- SubagentLinkStore = class {
1943
- /** Record (or correct) one child's link. Idempotent: a repeat call with the same
1944
- * values is a no-op, and the row always reflects the latest call to resolve it. */
1945
- record(childSessionId, parentSessionId, toolCallId, stageName) {
1946
- db().prepare(
1947
- `INSERT INTO subagent_links (child_session_id, parent_session_id, tool_call_id, stage_name)
1948
- VALUES (?, ?, ?, ?)
1949
- ON CONFLICT(child_session_id) DO UPDATE SET
1950
- parent_session_id = excluded.parent_session_id,
1951
- tool_call_id = excluded.tool_call_id,
1952
- stage_name = excluded.stage_name`
1953
- ).run(childSessionId, parentSessionId, toolCallId, stageName);
1954
- }
1955
- /** Every link recorded for one parent session's children, keyed by child session id. */
1956
- forParent(parentSessionId) {
1957
- const rows = db().prepare(
1958
- "SELECT child_session_id, tool_call_id, stage_name FROM subagent_links WHERE parent_session_id = ?"
1959
- ).all(parentSessionId);
1960
- const out = /* @__PURE__ */ new Map();
1961
- for (const r of rows) out.set(r.child_session_id, { toolCallId: r.tool_call_id, stageName: r.stage_name });
1962
- return out;
1963
- }
1964
- };
1965
- }
1966
- });
1967
-
1968
2015
  // server/src/session/SessionManager.ts
1969
- import fs10 from "node:fs";
1970
- import path11 from "node:path";
2016
+ import fs11 from "node:fs";
2017
+ import path12 from "node:path";
1971
2018
  function resolveCwd(input) {
1972
2019
  const raw = input?.trim();
1973
- const abs = raw ? path11.resolve(config.defaultCwd, raw) : config.defaultCwd;
2020
+ const abs = raw ? path12.resolve(config.defaultCwd, raw) : config.defaultCwd;
1974
2021
  if (!isWithinRoot(config.fileRoot, abs)) {
1975
2022
  throw new Error(`Working directory is outside the allowed root: ${abs}`);
1976
2023
  }
1977
2024
  let stat;
1978
2025
  try {
1979
- stat = fs10.statSync(abs);
2026
+ stat = fs11.statSync(abs);
1980
2027
  } catch {
1981
2028
  stat = void 0;
1982
2029
  }
@@ -1984,37 +2031,44 @@ function resolveCwd(input) {
1984
2031
  throw new Error(`Working directory path is a file, not a directory: ${abs}`);
1985
2032
  }
1986
2033
  if (!stat) {
1987
- fs10.mkdirSync(abs, { recursive: true });
2034
+ fs11.mkdirSync(abs, { recursive: true });
1988
2035
  }
1989
2036
  return abs;
1990
2037
  }
1991
- function mapNotification(n) {
1992
- switch (n.method) {
1993
- case "session/update":
1994
- return { kind: "session_update", update: n.params.update };
1995
- case KIRO_NOTIFICATIONS.metadata:
1996
- return { kind: "metadata", params: n.params };
1997
- case KIRO_NOTIFICATIONS.compactionStatus:
1998
- 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 } };
1999
2055
  default:
2000
2056
  return null;
2001
2057
  }
2002
2058
  }
2003
- function notificationSessionId(n) {
2004
- const params = n.params;
2005
- return params?.sessionId;
2059
+ function sessionModel(res) {
2060
+ return res.configOptions?.find((o) => o.id === "model")?.currentValue;
2006
2061
  }
2007
- function subagentCallStages(update) {
2008
- if (update.sessionUpdate !== "tool_call") return null;
2009
- const meta = update._meta;
2010
- if (meta?.kiro?.toolName !== "subagent") return null;
2011
- const toolCallId = update.toolCallId;
2012
- const rawInput = update.rawInput;
2013
- if (typeof toolCallId !== "string" || !Array.isArray(rawInput?.stages)) return null;
2014
- const stages = rawInput.stages.filter(
2015
- (s) => typeof s === "object" && s !== null && typeof s.name === "string"
2016
- );
2017
- 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
+ };
2018
2072
  }
2019
2073
  function newerOf(a, b) {
2020
2074
  return a && a.localeCompare(b) > 0 ? a : b;
@@ -2026,7 +2080,7 @@ function firstPromptText(transcript) {
2026
2080
  function isGhost(s, hasFile) {
2027
2081
  return !hasFile && s.hasBeenLive && !s.proc;
2028
2082
  }
2029
- var CANCEL_GRACE_MS, Session, TRANSCRIPT_PAGE_SIZE, SessionManager;
2083
+ var CANCEL_GRACE_MS, Session, TRANSCRIPT_UPDATES, TRANSCRIPT_PAGE_SIZE, SessionManager;
2030
2084
  var init_SessionManager = __esm({
2031
2085
  "server/src/session/SessionManager.ts"() {
2032
2086
  "use strict";
@@ -2040,11 +2094,8 @@ var init_SessionManager = __esm({
2040
2094
  init_TurnState();
2041
2095
  init_TurnRecorder();
2042
2096
  init_SubagentTracker();
2043
- init_subagentResult();
2044
- init_subagentFallbackMatch();
2045
2097
  init_kiroFiles();
2046
2098
  init_chatStore();
2047
- init_subagentLinks();
2048
2099
  CANCEL_GRACE_MS = 1e4;
2049
2100
  Session = class {
2050
2101
  sessionId;
@@ -2093,21 +2144,20 @@ var init_SessionManager = __esm({
2093
2144
  return event;
2094
2145
  }
2095
2146
  };
2147
+ TRANSCRIPT_UPDATES = /* @__PURE__ */ new Set(["agent_message_chunk", "agent_thought_chunk", "tool_call", "tool_call_update"]);
2096
2148
  TRANSCRIPT_PAGE_SIZE = 80;
2097
2149
  SessionManager = class {
2098
2150
  sessions = /* @__PURE__ */ new Map();
2099
2151
  log;
2100
2152
  store = new ChatStore();
2101
- subagentLinks = new SubagentLinkStore();
2102
- // Children the title fallback already tried and could not match. Matching hydrates the
2103
- // whole parent transcript (145 MB in one chat), so each child is tried once per process.
2104
- unmatchedChildren = /* @__PURE__ */ new Map();
2105
2153
  spawnProcess;
2106
2154
  cancelGraceMs;
2155
+ defaultModel;
2107
2156
  constructor(log, opts = {}) {
2108
2157
  this.log = log;
2109
2158
  this.spawnProcess = opts.spawn ?? ((o, l) => new KiroProcess(o, l));
2110
2159
  this.cancelGraceMs = opts.cancelGraceMs ?? CANCEL_GRACE_MS;
2160
+ this.defaultModel = opts.defaultModel ?? (async () => void 0);
2111
2161
  }
2112
2162
  /** What a session is called, from every read path. */
2113
2163
  titleOf(chat, parts) {
@@ -2115,7 +2165,7 @@ var init_SessionManager = __esm({
2115
2165
  override: chat.title ?? void 0,
2116
2166
  kiroTitle: parts.kiroTitle,
2117
2167
  firstPrompt: parts.firstPrompt,
2118
- folder: isManagedWorkspace(parts.cwd) ? void 0 : path11.basename(parts.cwd)
2168
+ folder: isManagedWorkspace(parts.cwd) ? void 0 : path12.basename(parts.cwd)
2119
2169
  });
2120
2170
  }
2121
2171
  renameChat(chatId, title) {
@@ -2173,7 +2223,7 @@ var init_SessionManager = __esm({
2173
2223
  }
2174
2224
  /** The reload itself, called with the session's reload claim held. */
2175
2225
  async replaceProcess(chatId, s) {
2176
- if (!await hasRecordedTurns(s.sessionId)) {
2226
+ if (!await isLoadable(s.sessionId)) {
2177
2227
  throw new Error(
2178
2228
  "kiro has not saved this session yet. Send a message first, then reload."
2179
2229
  );
@@ -2187,9 +2237,9 @@ var init_SessionManager = __esm({
2187
2237
  });
2188
2238
  }
2189
2239
  s.availableModes = [];
2240
+ invalidateAgents();
2190
2241
  await this.ensureProc(s);
2191
2242
  s.lastActivity = Date.now();
2192
- invalidateAgents();
2193
2243
  this.log.info({ sessionId: s.sessionId, cwd: s.cwd }, "session reloaded");
2194
2244
  return this.getDetail(chatId);
2195
2245
  }
@@ -2244,30 +2294,30 @@ var init_SessionManager = __esm({
2244
2294
  wire(s, proc) {
2245
2295
  proc.on("notification", (n) => {
2246
2296
  if (s.replaying) return;
2247
- if (n.method === KIRO_NOTIFICATIONS.subagentListUpdate) {
2248
- const params = n.params;
2249
- const resolved = s.subagents.apply(params.subagents);
2250
- for (const link of resolved) {
2251
- this.subagentLinks.record(link.sessionId, s.sessionId, link.toolCallId, link.stageName);
2252
- }
2253
- 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");
2254
2299
  return;
2255
2300
  }
2256
- const notifSessionId = notificationSessionId(n);
2257
- if (!notifSessionId || notifSessionId === s.sessionId) {
2258
- const payload = mapNotification(n);
2259
- if (!payload) return;
2260
- if (payload.kind === "session_update") {
2261
- const call = subagentCallStages(payload.update);
2262
- if (call) s.subagents.callStarted(call.toolCallId, call.stages);
2263
- const update = payload.update;
2264
- if (update.sessionUpdate === "tool_call_update" && typeof update.toolCallId === "string" && (update.status === "completed" || update.status === "failed")) {
2265
- s.subagents.callFinished(update.toolCallId);
2266
- }
2267
- }
2268
- 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);
2269
2317
  return;
2270
2318
  }
2319
+ if (subagentId) this.trackSubagentCall(s, subagentId, update);
2320
+ s.record({ kind: "session_update", update });
2271
2321
  });
2272
2322
  proc.on("exit", (code, signal) => {
2273
2323
  if (s.proc !== proc) return;
@@ -2276,28 +2326,50 @@ var init_SessionManager = __esm({
2276
2326
  s.running = false;
2277
2327
  });
2278
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
+ }
2279
2345
  /** Gets or spawns the kiro process for a session. */
2280
2346
  async ensureProc(s) {
2281
2347
  if (s.proc) return s.proc;
2282
2348
  if (s.spawning) return s.spawning;
2283
2349
  s.spawning = (async () => {
2284
2350
  await this.ensureCapacity();
2285
- const proc = this.spawnProcess(
2286
- { cwd: s.cwd, agent: s.agentId, model: s.modelId },
2287
- this.log
2288
- );
2351
+ const proc = this.spawnProcess({ cwd: s.cwd }, this.log);
2289
2352
  this.wire(s, proc);
2290
2353
  await proc.initialize();
2291
2354
  let res;
2292
2355
  if (s.hasBeenLive) {
2293
2356
  s.replaying = true;
2294
2357
  try {
2295
- 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
+ });
2296
2364
  } finally {
2297
2365
  s.replaying = false;
2298
2366
  }
2299
2367
  } else {
2300
- 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
+ });
2301
2373
  }
2302
2374
  if (!s.hasBeenLive && res.sessionId !== s.sessionId) {
2303
2375
  this.sessions.delete(s.sessionId);
@@ -2307,6 +2379,8 @@ var init_SessionManager = __esm({
2307
2379
  s.availableModes = res.modes.availableModes;
2308
2380
  s.currentModeId = res.modes.currentModeId;
2309
2381
  s.agentId = res.modes.currentModeId ?? s.agentId;
2382
+ s.modelId = sessionModel(res) ?? s.modelId;
2383
+ rememberModes(res.modes.availableModes);
2310
2384
  s.markLive();
2311
2385
  s.proc = proc;
2312
2386
  s.spawning = void 0;
@@ -2347,7 +2421,7 @@ var init_SessionManager = __esm({
2347
2421
  const s = new Session(tempId, store, cwd);
2348
2422
  s.agentId = opts.agentId ?? config.defaultAgent;
2349
2423
  s.currentModeId = s.agentId;
2350
- s.modelId = opts.modelId;
2424
+ s.modelId = opts.modelId ?? await this.defaultModel();
2351
2425
  this.sessions.set(tempId, s);
2352
2426
  try {
2353
2427
  await this.ensureProc(s);
@@ -2357,7 +2431,7 @@ var init_SessionManager = __esm({
2357
2431
  }
2358
2432
  this.store.create(chatId);
2359
2433
  this.store.bindSession(chatId, s.sessionId);
2360
- const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path11.basename(s.cwd));
2434
+ const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path12.basename(s.cwd));
2361
2435
  if (name) {
2362
2436
  this.store.setTitle(chatId, name);
2363
2437
  s.title = name;
@@ -2467,12 +2541,29 @@ ${tail}` : err.message;
2467
2541
  s.modelId = modelId;
2468
2542
  s.lastActivity = Date.now();
2469
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. */
2470
2546
  async execCommand(chatId, command) {
2547
+ if (command.replace(/^\//, "") !== "compact") throw new Error(`Unsupported command: ${command}`);
2471
2548
  const s = await this.ensureOpen(chatId);
2472
2549
  await this.settleReload(s);
2550
+ if (s.turnState.get().compacting) throw new Error("The conversation is already being compacted");
2473
2551
  const proc = await this.ensureProc(s);
2474
- await proc.execCommand(s.sessionId, command);
2475
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
+ );
2476
2567
  }
2477
2568
  /** The one place a ChatSummary is assembled, since kiro's file and Casper's
2478
2569
  * live state each hold part of the truth. */
@@ -2500,13 +2591,13 @@ ${tail}` : err.message;
2500
2591
  }
2501
2592
  async listChats() {
2502
2593
  const rows = this.store.all();
2503
- const files = await Promise.all(
2504
- rows.map((r) => r.sessionId ? readPersistedSession(r.sessionId) : null)
2594
+ const files = await readPersistedSessions(
2595
+ rows.map((r) => r.sessionId).filter((id) => !!id)
2505
2596
  );
2506
2597
  const out = [];
2507
- rows.forEach((row, i) => {
2598
+ rows.forEach((row) => {
2508
2599
  const live = row.sessionId ? this.sessions.get(row.sessionId) : void 0;
2509
- const persisted = files[i] ?? void 0;
2600
+ const persisted = row.sessionId ? files.get(row.sessionId) : void 0;
2510
2601
  if (live && isGhost(live, !!persisted)) {
2511
2602
  this.evict(live.sessionId);
2512
2603
  return;
@@ -2549,68 +2640,36 @@ ${tail}` : err.message;
2549
2640
  const end = Math.max(start, Math.min(start + limit, transcript.length));
2550
2641
  return transcript.slice(start, end);
2551
2642
  }
2552
- /**
2553
- * A chat's subagents: live ones from the tracker, joined with children found on disk.
2554
- * A child the tracker never saw falls back to a recorded link or title matching
2555
- * (subagentFallbackMatch.ts).
2556
- */
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. */
2557
2645
  async getSubagents(chatId) {
2558
2646
  const sessionId = this.sessionIdOf(chatId);
2559
- const s = this.sessions.get(sessionId);
2560
- const live = s ? s.subagents.list() : [];
2561
- const liveToolCalls = new Set(live.map((a) => a.toolCallId).filter((id) => !!id));
2562
- const pending = s ? [...liveToolCalls].flatMap((id) => s.subagents.listForCall(id).filter((a) => a.status === "pending")) : [];
2563
- const onDisk = await listChildSessions(sessionId);
2564
- const links = this.subagentLinks.forParent(sessionId);
2565
- const liveIds = new Set(live.map((a) => a.sessionId));
2566
- const tried = this.unmatchedChildren.get(sessionId) ?? /* @__PURE__ */ new Set();
2567
- const unresolved = onDisk.filter(
2568
- (c) => !liveIds.has(c.sessionId) && !links.has(c.sessionId) && !tried.has(c.sessionId)
2569
- );
2570
- const fallback = unresolved.length ? matchSubagentsFallback(subagentCallsIn(await hydrateTranscript(sessionId)), unresolved) : [];
2571
- const fallbackByChild = new Map(fallback.map((m) => [m.sessionId, m]));
2572
- for (const m of fallback) this.subagentLinks.record(m.sessionId, sessionId, m.toolCallId, m.stageName);
2573
- for (const c of unresolved) if (!fallbackByChild.has(c.sessionId)) tried.add(c.sessionId);
2574
- this.unmatchedChildren.set(sessionId, tried);
2647
+ const live = this.sessions.get(sessionId)?.subagents.list() ?? [];
2575
2648
  const byId = /* @__PURE__ */ new Map();
2576
- for (const child of onDisk) {
2577
- const link = links.get(child.sessionId) ?? fallbackByChild.get(child.sessionId);
2578
- byId.set(child.sessionId, {
2579
- sessionId: child.sessionId,
2580
- stageName: link?.stageName ?? (child.title || child.sessionId),
2581
- toolCallId: link?.toolCallId,
2582
- status: "completed",
2583
- createdAt: child.createdAt,
2584
- updatedAt: child.updatedAt
2585
- });
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);
2586
2653
  }
2587
- for (const a of live) byId.set(a.sessionId, a);
2588
- const startedThenPending = [...byId.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
2589
- 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));
2590
2656
  }
2591
2657
  /** One subagent's own transcript, hydrated the same way a chat's is. */
2592
- // Polled while a subagent runs, so it reads only the child's own files.
2593
- async getSubagentDetail(chatId, childSessionId) {
2658
+ // Polled while a subagent runs, so it reads only the subagent's own files.
2659
+ async getSubagentDetail(chatId, subagentId) {
2594
2660
  const parentSessionId = this.sessionIdOf(chatId);
2595
- const child = await readChildSession(childSessionId, parentSessionId);
2596
- if (!child) throw new Error(`Unknown subagent: ${childSessionId}`);
2597
- const live = this.sessions.get(parentSessionId)?.subagents.list().find((a) => a.sessionId === childSessionId);
2598
- const link = this.subagentLinks.forParent(parentSessionId).get(childSessionId);
2599
- const summary = live ?? {
2600
- sessionId: childSessionId,
2601
- stageName: link?.stageName ?? (child.title || childSessionId),
2602
- toolCallId: link?.toolCallId,
2603
- status: "completed",
2604
- createdAt: child.createdAt,
2605
- updatedAt: child.updatedAt
2606
- };
2607
- const transcript = showSummaryAsAnswer(await hydrateTranscript(childSessionId));
2608
- 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 };
2609
2667
  }
2610
2668
  /** One projection, so a dormant session and a live one cannot disagree. */
2611
- buildDetail(chat, s, transcript, persisted) {
2669
+ buildDetail(chat, s, hydrated, persisted) {
2670
+ const { transcript, head } = this.resumePoint(s, hydrated);
2612
2671
  return {
2613
- summary: this.summaryOf(chat, s, persisted, transcript),
2672
+ summary: this.summaryOf(chat, s, persisted, hydrated),
2614
2673
  modes: s?.availableModes ?? [],
2615
2674
  currentModeId: s?.currentModeId ?? persisted?.agentId,
2616
2675
  transcript: transcript.slice(-TRANSCRIPT_PAGE_SIZE),
@@ -2619,16 +2678,19 @@ ${tail}` : err.message;
2619
2678
  ...emptyObservabilitySnapshot(),
2620
2679
  contextUsagePercentage: persisted?.contextUsagePercentage ?? 0
2621
2680
  },
2622
- head: s ? this.replayHead(s, transcript) : 0
2681
+ head
2623
2682
  };
2624
2683
  }
2625
2684
  /**
2626
- * The cursor a reconnecting client starts from: the store head, unless the in-flight
2627
- * 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.
2628
2689
  */
2629
- replayHead(s, transcript) {
2690
+ resumePoint(s, transcript) {
2691
+ if (!s) return { transcript, head: 0 };
2630
2692
  const head = s.store.head();
2631
- if (!s.running) return head;
2693
+ if (!s.running) return { transcript, head };
2632
2694
  const { events } = s.store.getSince(0);
2633
2695
  let started;
2634
2696
  for (let i = events.length - 1; i >= 0; i--) {
@@ -2637,18 +2699,19 @@ ${tail}` : err.message;
2637
2699
  break;
2638
2700
  }
2639
2701
  }
2640
- if (!started || started.payload.kind !== "turn_started") return head;
2702
+ if (!started || started.payload.kind !== "turn_started") return { transcript, head };
2641
2703
  const promptText = stripAttachmentsLine(
2642
2704
  started.payload.prompt.filter((b) => b.type === "text").map((b) => b.text).join("")
2643
2705
  ).trim();
2706
+ let cut = transcript.length;
2644
2707
  for (let i = transcript.length - 1; i >= 0; i--) {
2645
2708
  const it = transcript[i];
2646
2709
  if (it.type === "message" && it.message.role === "user") {
2647
- if (promptText && it.message.text.trim() === promptText) return head;
2710
+ if (promptText && it.message.text.trim() === promptText) cut = i;
2648
2711
  break;
2649
2712
  }
2650
2713
  }
2651
- return started.seq - 1;
2714
+ return { transcript: transcript.slice(0, cut), head: started.seq - 1 };
2652
2715
  }
2653
2716
  evict(sessionId) {
2654
2717
  const s = this.sessions.get(sessionId);
@@ -2682,6 +2745,70 @@ ${tail}` : err.message;
2682
2745
  }
2683
2746
  });
2684
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
+
2685
2812
  // server/src/util/rateLimit.ts
2686
2813
  var AttemptLimiter;
2687
2814
  var init_rateLimit = __esm({
@@ -2828,63 +2955,6 @@ var init_auth = __esm({
2828
2955
  }
2829
2956
  });
2830
2957
 
2831
- // server/src/session/models.ts
2832
- import { execFile as execFile2 } from "node:child_process";
2833
- import { promisify as promisify2 } from "node:util";
2834
- async function configuredDefault() {
2835
- try {
2836
- const { stdout } = await execFileAsync2(config.kiroBin, [
2837
- "settings",
2838
- "chat.defaultModel",
2839
- "-f",
2840
- "json"
2841
- ]);
2842
- const value = JSON.parse(stdout);
2843
- return typeof value === "string" && value ? value : void 0;
2844
- } catch {
2845
- return void 0;
2846
- }
2847
- }
2848
- async function listModels(log) {
2849
- if (cache2) return cache2;
2850
- const { stdout } = await execFileAsync2(
2851
- config.kiroBin,
2852
- ["chat", "--list-models", "-f", "json"],
2853
- { maxBuffer: 4 * 1024 * 1024 }
2854
- );
2855
- const parsed = JSON.parse(stdout);
2856
- const configured = await configuredDefault();
2857
- const preferred = parsed.models.some((m) => m.model_id === configured) ? configured : parsed.default_model;
2858
- cache2 = parsed.models.map((m, i) => ({
2859
- modelId: m.model_id,
2860
- modelName: m.model_name,
2861
- description: m.description,
2862
- contextWindowTokens: m.context_window_tokens,
2863
- rateMultiplier: m.rate_multiplier,
2864
- rateUnit: m.rate_unit,
2865
- isDefault: preferred ? m.model_id === preferred : m.default_model ?? i === 0
2866
- }));
2867
- log.info({ count: cache2.length, default: preferred }, "loaded model list");
2868
- return cache2;
2869
- }
2870
- async function kiroVersion() {
2871
- try {
2872
- const { stdout } = await execFileAsync2(config.kiroBin, ["--version"]);
2873
- return stdout.trim();
2874
- } catch {
2875
- return void 0;
2876
- }
2877
- }
2878
- var execFileAsync2, cache2;
2879
- var init_models = __esm({
2880
- "server/src/session/models.ts"() {
2881
- "use strict";
2882
- init_config();
2883
- execFileAsync2 = promisify2(execFile2);
2884
- cache2 = null;
2885
- }
2886
- });
2887
-
2888
2958
  // server/src/routes/models.ts
2889
2959
  function registerModelRoutes(app) {
2890
2960
  app.get("/api/models", async () => {
@@ -2914,8 +2984,8 @@ var init_agents2 = __esm({
2914
2984
  });
2915
2985
 
2916
2986
  // server/src/util/confinedFile.ts
2917
- import fs11 from "node:fs/promises";
2918
- import path12 from "node:path";
2987
+ import fs12 from "node:fs/promises";
2988
+ import path13 from "node:path";
2919
2989
  async function resolveConfined(input, spec) {
2920
2990
  let target = null;
2921
2991
  for (const root of spec.lexical) {
@@ -2931,7 +3001,7 @@ async function resolveConfined(input, spec) {
2931
3001
  if (!real) return { ok: false, ...await spec.notFound() };
2932
3002
  let stat;
2933
3003
  try {
2934
- stat = await fs11.stat(real);
3004
+ stat = await fs12.stat(real);
2935
3005
  } catch {
2936
3006
  return { ok: false, ...await spec.notFound() };
2937
3007
  }
@@ -2970,7 +3040,7 @@ async function resolveChatPath(sessions, sessionId, requestedPath, require2) {
2970
3040
  async function workspaceNotFound(cwd) {
2971
3041
  let missing;
2972
3042
  try {
2973
- missing = !(await fs11.stat(cwd)).isDirectory();
3043
+ missing = !(await fs12.stat(cwd)).isDirectory();
2974
3044
  } catch {
2975
3045
  missing = true;
2976
3046
  }
@@ -2990,7 +3060,7 @@ async function resolveAbsolutePath(input, require2) {
2990
3060
  });
2991
3061
  }
2992
3062
  async function classifyDirent(parentReal, entry, realRoots) {
2993
- const absolute = path12.join(parentReal, entry.name);
3063
+ const absolute = path13.join(parentReal, entry.name);
2994
3064
  if (entry.isDirectory()) return { kind: "directory", real: absolute };
2995
3065
  if (entry.isFile()) return { kind: "file", real: absolute };
2996
3066
  if (!entry.isSymbolicLink()) return null;
@@ -2998,7 +3068,7 @@ async function classifyDirent(parentReal, entry, realRoots) {
2998
3068
  const real = await realConfineToRoot(root, absolute);
2999
3069
  if (!real) continue;
3000
3070
  try {
3001
- const stat = await fs11.stat(real);
3071
+ const stat = await fs12.stat(real);
3002
3072
  if (stat.isDirectory()) return { kind: "directory", real };
3003
3073
  if (stat.isFile()) return { kind: "file", real };
3004
3074
  } catch {
@@ -3016,7 +3086,7 @@ var init_confinedFile = __esm({
3016
3086
  });
3017
3087
 
3018
3088
  // server/src/util/filekind.ts
3019
- import path13 from "node:path";
3089
+ import path14 from "node:path";
3020
3090
  function mimeForExt(ext) {
3021
3091
  return MIME_MAP[ext.toLowerCase()] ?? "application/octet-stream";
3022
3092
  }
@@ -3036,7 +3106,7 @@ function looksBinary(buf) {
3036
3106
  return control / buf.length > 0.3;
3037
3107
  }
3038
3108
  function classifyKind(name) {
3039
- const ext = path13.extname(name).toLowerCase();
3109
+ const ext = path14.extname(name).toLowerCase();
3040
3110
  if (ext in IMAGE_EXTS) return "image";
3041
3111
  if (TEXT_EXTS.has(ext)) return "text";
3042
3112
  return "binary";
@@ -3146,9 +3216,9 @@ var init_filekind = __esm({
3146
3216
  });
3147
3217
 
3148
3218
  // server/src/routes/filePreview.ts
3149
- import fs12 from "node:fs/promises";
3219
+ import fs13 from "node:fs/promises";
3150
3220
  import { createReadStream } from "node:fs";
3151
- import path14 from "node:path";
3221
+ import path15 from "node:path";
3152
3222
  function tooLargeForPreview(size, maxBytes) {
3153
3223
  return {
3154
3224
  error: `File too large for preview (${(size / 1024 / 1024).toFixed(1)} MB, max ${maxBytes / 1024 / 1024} MB)`
@@ -3175,7 +3245,7 @@ function hexdump(buf) {
3175
3245
  return lines.join("\n");
3176
3246
  }
3177
3247
  async function sendFilePreview(req, reply, realTarget, stat) {
3178
- const ext = path14.extname(realTarget).toLowerCase();
3248
+ const ext = path15.extname(realTarget).toLowerCase();
3179
3249
  const mime = mimeForExt(ext);
3180
3250
  const isImage2 = mime.startsWith("image/");
3181
3251
  const isPdf = mime === "application/pdf";
@@ -3220,7 +3290,7 @@ async function sendFilePreview(req, reply, realTarget, stat) {
3220
3290
  if (kind === "binary") {
3221
3291
  let fh;
3222
3292
  try {
3223
- fh = await fs12.open(realTarget, "r");
3293
+ fh = await fs13.open(realTarget, "r");
3224
3294
  const buf = Buffer.alloc(Math.min(HEXDUMP_BYTES, stat.size));
3225
3295
  const { bytesRead } = await fh.read(buf, 0, buf.length, 0);
3226
3296
  const head = buf.subarray(0, bytesRead);
@@ -3264,9 +3334,9 @@ var init_filePreview = __esm({
3264
3334
  });
3265
3335
 
3266
3336
  // server/src/routes/fs.ts
3267
- import fs13 from "node:fs/promises";
3337
+ import fs14 from "node:fs/promises";
3268
3338
  import { createReadStream as createReadStream2 } from "node:fs";
3269
- import path15 from "node:path";
3339
+ import path16 from "node:path";
3270
3340
  function registerFsRoutes(app) {
3271
3341
  app.get(
3272
3342
  "/api/fs/dirs",
@@ -3274,10 +3344,10 @@ function registerFsRoutes(app) {
3274
3344
  const input = (req.query.path ?? "").trim();
3275
3345
  const base = config.defaultCwd;
3276
3346
  const endsWithSep = input.endsWith("/");
3277
- const resolved = input ? path15.resolve(base, input) : base;
3278
- const dir = endsWithSep || !input ? resolved : path15.dirname(resolved);
3279
- const prefix = endsWithSep || !input ? "" : path15.basename(resolved);
3280
- 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");
3281
3351
  const listing = await resolveAbsolutePath(dir, "directory");
3282
3352
  if (!listing.ok) {
3283
3353
  if (listing.status === 403) return replyWith(reply, listing);
@@ -3286,7 +3356,7 @@ function registerFsRoutes(app) {
3286
3356
  const realDir = listing.real;
3287
3357
  let entries = [];
3288
3358
  try {
3289
- const dirents = await fs13.readdir(realDir, { withFileTypes: true });
3359
+ const dirents = await fs14.readdir(realDir, { withFileTypes: true });
3290
3360
  const checks2 = await Promise.all(
3291
3361
  dirents.map(async (d) => {
3292
3362
  const target = await classifyDirent(realDir, d, absoluteRoots());
@@ -3297,7 +3367,7 @@ function registerFsRoutes(app) {
3297
3367
  entries = checks2.filter((name) => name !== null).filter((name) => name.toLowerCase().startsWith(prefix.toLowerCase())).sort((a, b) => {
3298
3368
  if (isDot(a) !== isDot(b)) return isDot(a) ? 1 : -1;
3299
3369
  return a.localeCompare(b);
3300
- }).slice(0, 500).map((name) => path15.join(dir, name));
3370
+ }).slice(0, 500).map((name) => path16.join(dir, name));
3301
3371
  } catch {
3302
3372
  entries = [];
3303
3373
  }
@@ -3312,7 +3382,7 @@ function registerFsRoutes(app) {
3312
3382
  reply.code(400);
3313
3383
  return { error: "path parameter is required" };
3314
3384
  }
3315
- if (!path15.isAbsolute(filePath)) {
3385
+ if (!path16.isAbsolute(filePath)) {
3316
3386
  reply.code(400);
3317
3387
  return { error: "path must be absolute" };
3318
3388
  }
@@ -3322,7 +3392,7 @@ function registerFsRoutes(app) {
3322
3392
  reply.header("Content-Type", "application/octet-stream");
3323
3393
  reply.header(
3324
3394
  "Content-Disposition",
3325
- `attachment; filename="${path15.basename(resolved.real).replace(/"/g, "")}"`
3395
+ `attachment; filename="${path16.basename(resolved.real).replace(/"/g, "")}"`
3326
3396
  );
3327
3397
  reply.header("Content-Length", resolved.stat.size);
3328
3398
  return reply.send(createReadStream2(resolved.real));
@@ -3493,9 +3563,9 @@ var init_subagents = __esm({
3493
3563
  });
3494
3564
 
3495
3565
  // server/src/routes/workspace.ts
3496
- import fs14 from "node:fs/promises";
3566
+ import fs15 from "node:fs/promises";
3497
3567
  import { createReadStream as createReadStream3 } from "node:fs";
3498
- import path16 from "node:path";
3568
+ import path17 from "node:path";
3499
3569
  function registerWorkspaceRoutes(app, manager) {
3500
3570
  app.get(
3501
3571
  "/api/chats/:id/tree",
@@ -3510,7 +3580,7 @@ function registerWorkspaceRoutes(app, manager) {
3510
3580
  const { cwd, relative, real: realTarget } = resolved;
3511
3581
  let dirents;
3512
3582
  try {
3513
- dirents = await fs14.readdir(realTarget, { withFileTypes: true, encoding: "utf8" });
3583
+ dirents = await fs15.readdir(realTarget, { withFileTypes: true, encoding: "utf8" });
3514
3584
  } catch {
3515
3585
  return replyWith(reply, await workspaceNotFound(cwd));
3516
3586
  }
@@ -3524,7 +3594,7 @@ function registerWorkspaceRoutes(app, manager) {
3524
3594
  continue;
3525
3595
  }
3526
3596
  try {
3527
- const stat = await fs14.stat(target.real);
3597
+ const stat = await fs15.stat(target.real);
3528
3598
  entries.push({
3529
3599
  name: d.name,
3530
3600
  path: entryRelative,
@@ -3553,8 +3623,8 @@ function registerWorkspaceRoutes(app, manager) {
3553
3623
  reply.code(413);
3554
3624
  return { error: `File too large (${(stat.size / 1024 / 1024).toFixed(1)} MB, max 100 MB)` };
3555
3625
  }
3556
- const ext = path16.extname(realTarget);
3557
- const filename = path16.basename(realTarget);
3626
+ const ext = path17.extname(realTarget);
3627
+ const filename = path17.basename(realTarget);
3558
3628
  const asciiName = filename.replace(/[^\w.\-]/g, "_");
3559
3629
  reply.header("Content-Type", mimeForExt(ext));
3560
3630
  reply.header(
@@ -3589,27 +3659,27 @@ var init_workspace = __esm({
3589
3659
 
3590
3660
  // server/src/routes/uploads.ts
3591
3661
  import { createReadStream as createReadStream4, createWriteStream } from "node:fs";
3592
- import fs15 from "node:fs/promises";
3593
- import path17 from "node:path";
3662
+ import fs16 from "node:fs/promises";
3663
+ import path18 from "node:path";
3594
3664
  import crypto4 from "node:crypto";
3595
3665
  import { execFile as execFile3 } from "node:child_process";
3596
3666
  import { pipeline } from "node:stream/promises";
3597
3667
  function sanitizeName(raw) {
3598
- const base = path17.basename(raw).replace(/[^\w.\- ]/g, "_").trim();
3668
+ const base = path18.basename(raw).replace(/[^\w.\- ]/g, "_").trim();
3599
3669
  if (!base || base === "." || base === "..") return `upload-${Date.now()}`;
3600
3670
  return base.replace(/^\.+/, "") || `upload-${Date.now()}`;
3601
3671
  }
3602
3672
  async function uniquePath(dir, name) {
3603
- const ext = path17.extname(name);
3673
+ const ext = path18.extname(name);
3604
3674
  const stem = name.slice(0, name.length - ext.length);
3605
3675
  let candidate = name;
3606
3676
  let n = 1;
3607
3677
  while (true) {
3608
3678
  try {
3609
- await fs15.access(path17.join(dir, candidate));
3679
+ await fs16.access(path18.join(dir, candidate));
3610
3680
  candidate = `${stem} (${n++})${ext}`;
3611
3681
  } catch {
3612
- return path17.join(dir, candidate);
3682
+ return path18.join(dir, candidate);
3613
3683
  }
3614
3684
  }
3615
3685
  }
@@ -3629,7 +3699,7 @@ function fileType(absPath) {
3629
3699
  async function sampleStrings(absPath, limit = 40) {
3630
3700
  let fh;
3631
3701
  try {
3632
- fh = await fs15.open(absPath, "r");
3702
+ fh = await fs16.open(absPath, "r");
3633
3703
  const buf = Buffer.alloc(STRINGS_SCAN_BYTES);
3634
3704
  const { bytesRead } = await fh.read(buf, 0, STRINGS_SCAN_BYTES, 0);
3635
3705
  const out = [];
@@ -3661,7 +3731,7 @@ function registerUploadRoutes(app) {
3661
3731
  return { error: "Invalid chat id" };
3662
3732
  }
3663
3733
  const uploadDir = chatUploadsDir(req.params.chatId);
3664
- await fs15.mkdir(uploadDir, { recursive: true, mode: 448 });
3734
+ await fs16.mkdir(uploadDir, { recursive: true, mode: 448 });
3665
3735
  const results = [];
3666
3736
  const parts = req.files();
3667
3737
  for await (const part of parts) {
@@ -3675,17 +3745,17 @@ function registerUploadRoutes(app) {
3675
3745
  return { error: `Failed to store ${name}` };
3676
3746
  }
3677
3747
  if (part.file.truncated) {
3678
- await fs15.rm(dest, { force: true });
3748
+ await fs16.rm(dest, { force: true });
3679
3749
  reply.code(413);
3680
3750
  return {
3681
3751
  error: `${name} exceeds the max upload size (${Math.round(config.maxUploadBytes / 1024 / 1024)} MB)`
3682
3752
  };
3683
3753
  }
3684
- const stat = await fs15.stat(dest);
3685
- const ext = path17.extname(dest);
3754
+ const stat = await fs16.stat(dest);
3755
+ const ext = path18.extname(dest);
3686
3756
  const kind = classifyKind(dest);
3687
3757
  const uploaded = {
3688
- name: path17.basename(dest),
3758
+ name: path18.basename(dest),
3689
3759
  path: dest,
3690
3760
  size: stat.size,
3691
3761
  mimeType: mimeForExt(ext),
@@ -3741,7 +3811,7 @@ var init_health = __esm({
3741
3811
  });
3742
3812
 
3743
3813
  // server/src/ws/dirWatchers.ts
3744
- import fs16 from "node:fs";
3814
+ import fs17 from "node:fs";
3745
3815
  function diffWatchSet(current, next) {
3746
3816
  const have = new Set(current);
3747
3817
  const want = new Set(next.slice(0, MAX_WATCHES));
@@ -3765,7 +3835,7 @@ function createDirWatchers(opts) {
3765
3835
  const target = await opts.resolve(relative);
3766
3836
  if (!target || closed) return;
3767
3837
  try {
3768
- const watcher = fs16.watch(target, { persistent: false }, () => {
3838
+ const watcher = fs17.watch(target, { persistent: false }, () => {
3769
3839
  const existing = timers.get(relative);
3770
3840
  if (existing) clearTimeout(existing);
3771
3841
  timers.set(
@@ -3823,7 +3893,7 @@ function handleConnection(socket, manager, chatId, startCursor) {
3823
3893
  return null;
3824
3894
  }
3825
3895
  },
3826
- onChange: (path20) => send(socket, { type: "fs_changed", path: path20 })
3896
+ onChange: (path21) => send(socket, { type: "fs_changed", path: path21 })
3827
3897
  });
3828
3898
  const forward = (event) => {
3829
3899
  if (event.seq <= cursor) return;
@@ -3956,7 +4026,7 @@ var init_gateway = __esm({
3956
4026
  });
3957
4027
 
3958
4028
  // server/src/app.ts
3959
- import fs17 from "node:fs";
4029
+ import fs18 from "node:fs";
3960
4030
  import Fastify from "fastify";
3961
4031
  import websocket from "@fastify/websocket";
3962
4032
  import fastifyStatic from "@fastify/static";
@@ -3975,7 +4045,7 @@ async function buildApp() {
3975
4045
  transport: isProd ? void 0 : { target: "pino/file", options: { destination: 2 } }
3976
4046
  }
3977
4047
  });
3978
- const manager = new SessionManager(logger);
4048
+ const manager = new SessionManager(logger, { defaultModel: () => defaultModelId(logger) });
3979
4049
  const startedAt = Date.now();
3980
4050
  await app.register(websocket, {
3981
4051
  options: { maxPayload: 16 * 1024 * 1024 }
@@ -3993,20 +4063,20 @@ async function buildApp() {
3993
4063
  registerWorkspaceRoutes(app, manager);
3994
4064
  registerUploadRoutes(app);
3995
4065
  registerWsGateway(app, manager);
3996
- if (fs17.existsSync(config.webDist)) {
4066
+ if (fs18.existsSync(config.webDist)) {
3997
4067
  await app.register(fastifyStatic, {
3998
4068
  root: config.webDist
3999
4069
  });
4000
4070
  app.addHook("onSend", async (req, reply) => {
4001
- const path20 = req.url.split("?")[0] ?? "";
4002
- if (path20 === "/manifest.json" || path20.startsWith("/icons/")) {
4071
+ const path21 = req.url.split("?")[0] ?? "";
4072
+ if (path21 === "/manifest.json" || path21.startsWith("/icons/")) {
4003
4073
  reply.header("cache-control", "public, max-age=300");
4004
4074
  }
4005
4075
  });
4006
4076
  app.setNotFoundHandler((req, reply) => {
4007
- const path20 = req.url.split("?")[0] ?? "";
4008
- const looksLikeAsset = path20.startsWith("/assets/") || /\.[a-zA-Z0-9]+$/.test(path20);
4009
- 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) {
4010
4080
  reply.code(404).send({ error: "Not found" });
4011
4081
  return;
4012
4082
  }
@@ -4021,6 +4091,7 @@ var init_app = __esm({
4021
4091
  init_config();
4022
4092
  init_logger();
4023
4093
  init_SessionManager();
4094
+ init_models();
4024
4095
  init_auth();
4025
4096
  init_models2();
4026
4097
  init_agents2();
@@ -4077,19 +4148,22 @@ var init_server = __esm({
4077
4148
  // server/src/cli/doctor.ts
4078
4149
  var doctor_exports = {};
4079
4150
  __export(doctor_exports, {
4151
+ agentFormat: () => agentFormat,
4080
4152
  doctor: () => doctor,
4153
+ kiroMajor: () => kiroMajor,
4081
4154
  mcpWiring: () => mcpWiring
4082
4155
  });
4083
4156
  import { execFileSync as execFileSync3 } from "node:child_process";
4084
- import fs18 from "node:fs";
4085
- import path18 from "node:path";
4157
+ import fs19 from "node:fs";
4158
+ import os4 from "node:os";
4159
+ import path19 from "node:path";
4086
4160
  function which(bin) {
4087
- if (bin.includes("/")) return fs18.existsSync(bin) ? bin : void 0;
4088
- 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)) {
4089
4163
  if (dir === "") continue;
4090
- const candidate = path18.join(dir, bin);
4164
+ const candidate = path19.join(dir, bin);
4091
4165
  try {
4092
- fs18.accessSync(candidate, fs18.constants.X_OK);
4166
+ fs19.accessSync(candidate, fs19.constants.X_OK);
4093
4167
  return candidate;
4094
4168
  } catch {
4095
4169
  }
@@ -4113,11 +4187,28 @@ function mcpWiring(agentJson, resolve) {
4113
4187
  return { ok: false, detail: `${command || "(no command)"} not found - widgets are unavailable` };
4114
4188
  }
4115
4189
  const script = args.find((a) => a.endsWith(".js"));
4116
- if (script && !fs18.existsSync(script)) {
4190
+ if (script && !fs19.existsSync(script)) {
4117
4191
  return { ok: false, detail: `${script} is gone - delete the agent file and restart to rewrite it` };
4118
4192
  }
4119
4193
  return { ok: true, detail: script ?? `${command} ${args.join(" ")}` };
4120
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
+ }
4121
4212
  function checks() {
4122
4213
  const out = [];
4123
4214
  out.push({
@@ -4133,7 +4224,10 @@ function checks() {
4133
4224
  } catch {
4134
4225
  version = "(version unavailable)";
4135
4226
  }
4136
- 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
+ );
4137
4231
  } else {
4138
4232
  out.push({
4139
4233
  ok: false,
@@ -4145,7 +4239,7 @@ function checks() {
4145
4239
  const settings = readSettings(config.configFile);
4146
4240
  const hasToken = typeof settings.token === "string" && settings.token !== "";
4147
4241
  out.push(
4148
- fs18.existsSync(config.configFile) ? {
4242
+ fs19.existsSync(config.configFile) ? {
4149
4243
  ok: hasToken,
4150
4244
  name: "settings",
4151
4245
  detail: hasToken ? `${config.configFile} (token set)` : `${config.configFile} exists but has no token - auth is DISABLED`
@@ -4155,8 +4249,8 @@ function checks() {
4155
4249
  detail: `${config.configFile} not found; a token is generated on first run`
4156
4250
  }
4157
4251
  );
4158
- if (fs18.existsSync(config.configFile)) {
4159
- const mode = fs18.statSync(config.configFile).mode & 511;
4252
+ if (fs19.existsSync(config.configFile)) {
4253
+ const mode = fs19.statSync(config.configFile).mode & 511;
4160
4254
  out.push({
4161
4255
  ok: mode === 384,
4162
4256
  name: "settings mode",
@@ -4164,10 +4258,10 @@ function checks() {
4164
4258
  });
4165
4259
  }
4166
4260
  try {
4167
- fs18.mkdirSync(config.casperDataDir, { recursive: true });
4168
- fs18.accessSync(config.casperDataDir, fs18.constants.W_OK);
4169
- const db2 = path18.join(config.casperDataDir, "casper.db");
4170
- 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` : "";
4171
4265
  out.push({ ok: true, name: "data dir", detail: `${config.casperDataDir} writable${size}` });
4172
4266
  } catch (err) {
4173
4267
  out.push({
@@ -4178,22 +4272,25 @@ function checks() {
4178
4272
  });
4179
4273
  }
4180
4274
  out.push(
4181
- 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 } : {
4182
4276
  ok: false,
4183
4277
  fatal: true,
4184
4278
  name: "web app",
4185
4279
  detail: `${config.webDist} not found - the package is incomplete or CASPER_WEB_DIST is wrong`
4186
4280
  }
4187
4281
  );
4188
- const agent = path18.join(config.kiroSessionsDir, "..", "..", "agents", "casper.json");
4189
- const hasAgent = fs18.existsSync(agent);
4282
+ const agent = path19.join(os4.homedir(), ".kiro", "agents", "casper.json");
4283
+ const hasAgent = fs19.existsSync(agent);
4190
4284
  out.push({
4191
4285
  ok: hasAgent,
4192
4286
  name: "casper agent",
4193
4287
  detail: hasAgent ? agent : `${agent} missing - it is installed on first run`
4194
4288
  });
4195
4289
  if (hasAgent) {
4196
- 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);
4197
4294
  out.push({ ok: wiring.ok, name: "casper mcp", detail: wiring.detail });
4198
4295
  }
4199
4296
  return out;
@@ -4657,14 +4754,14 @@ var init_server2 = __esm({
4657
4754
  "server/src/mcp/server.ts"() {
4658
4755
  "use strict";
4659
4756
  init_protocol();
4660
- VERSION = true ? "0.15.7" : "dev";
4757
+ VERSION = true ? "0.16.0" : "dev";
4661
4758
  }
4662
4759
  });
4663
4760
 
4664
4761
  // server/src/cli/program.ts
4665
- import fs19 from "node:fs";
4666
- import os4 from "node:os";
4667
- import path19 from "node:path";
4762
+ import fs20 from "node:fs";
4763
+ import os5 from "node:os";
4764
+ import path20 from "node:path";
4668
4765
 
4669
4766
  // server/node_modules/commander/lib/error.js
4670
4767
  var CommanderError = class extends Error {
@@ -5255,8 +5352,8 @@ var Help = class {
5255
5352
  * @param {string} str
5256
5353
  * @returns {number}
5257
5354
  */
5258
- displayWidth(str2) {
5259
- return stripVTControlCharacters(str2).length;
5355
+ displayWidth(str3) {
5356
+ return stripVTControlCharacters(str3).length;
5260
5357
  }
5261
5358
  /**
5262
5359
  * Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
@@ -5264,11 +5361,11 @@ var Help = class {
5264
5361
  * @param {string} str
5265
5362
  * @returns {string}
5266
5363
  */
5267
- styleTitle(str2) {
5268
- return str2;
5364
+ styleTitle(str3) {
5365
+ return str3;
5269
5366
  }
5270
- styleUsage(str2) {
5271
- return str2.split(" ").map((word) => {
5367
+ styleUsage(str3) {
5368
+ return str3.split(" ").map((word) => {
5272
5369
  if (word === "[options]") return this.styleOptionText(word);
5273
5370
  if (word === "[command]") return this.styleSubcommandText(word);
5274
5371
  if (word[0] === "[" || word[0] === "<")
@@ -5276,46 +5373,46 @@ var Help = class {
5276
5373
  return this.styleCommandText(word);
5277
5374
  }).join(" ");
5278
5375
  }
5279
- styleCommandDescription(str2) {
5280
- return this.styleDescriptionText(str2);
5376
+ styleCommandDescription(str3) {
5377
+ return this.styleDescriptionText(str3);
5281
5378
  }
5282
- styleOptionDescription(str2) {
5283
- return this.styleDescriptionText(str2);
5379
+ styleOptionDescription(str3) {
5380
+ return this.styleDescriptionText(str3);
5284
5381
  }
5285
- styleSubcommandDescription(str2) {
5286
- return this.styleDescriptionText(str2);
5382
+ styleSubcommandDescription(str3) {
5383
+ return this.styleDescriptionText(str3);
5287
5384
  }
5288
- styleArgumentDescription(str2) {
5289
- return this.styleDescriptionText(str2);
5385
+ styleArgumentDescription(str3) {
5386
+ return this.styleDescriptionText(str3);
5290
5387
  }
5291
- styleDescriptionText(str2) {
5292
- return str2;
5388
+ styleDescriptionText(str3) {
5389
+ return str3;
5293
5390
  }
5294
- styleOptionTerm(str2) {
5295
- return this.styleOptionText(str2);
5391
+ styleOptionTerm(str3) {
5392
+ return this.styleOptionText(str3);
5296
5393
  }
5297
- styleSubcommandTerm(str2) {
5298
- return str2.split(" ").map((word) => {
5394
+ styleSubcommandTerm(str3) {
5395
+ return str3.split(" ").map((word) => {
5299
5396
  if (word === "[options]") return this.styleOptionText(word);
5300
5397
  if (word[0] === "[" || word[0] === "<")
5301
5398
  return this.styleArgumentText(word);
5302
5399
  return this.styleSubcommandText(word);
5303
5400
  }).join(" ");
5304
5401
  }
5305
- styleArgumentTerm(str2) {
5306
- return this.styleArgumentText(str2);
5402
+ styleArgumentTerm(str3) {
5403
+ return this.styleArgumentText(str3);
5307
5404
  }
5308
- styleOptionText(str2) {
5309
- return str2;
5405
+ styleOptionText(str3) {
5406
+ return str3;
5310
5407
  }
5311
- styleArgumentText(str2) {
5312
- return str2;
5408
+ styleArgumentText(str3) {
5409
+ return str3;
5313
5410
  }
5314
- styleSubcommandText(str2) {
5315
- return str2;
5411
+ styleSubcommandText(str3) {
5412
+ return str3;
5316
5413
  }
5317
- styleCommandText(str2) {
5318
- return str2;
5414
+ styleCommandText(str3) {
5415
+ return str3;
5319
5416
  }
5320
5417
  /**
5321
5418
  * Calculate the pad width from the maximum term length.
@@ -5338,8 +5435,8 @@ var Help = class {
5338
5435
  * @param {string} str
5339
5436
  * @returns {boolean}
5340
5437
  */
5341
- preformatted(str2) {
5342
- return /\n[^\S\r\n]/.test(str2);
5438
+ preformatted(str3) {
5439
+ return /\n[^\S\r\n]/.test(str3);
5343
5440
  }
5344
5441
  /**
5345
5442
  * Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
@@ -5385,9 +5482,9 @@ ${itemIndentStr}`);
5385
5482
  * @param {number} width
5386
5483
  * @returns {string}
5387
5484
  */
5388
- boxWrap(str2, width) {
5389
- if (width < this.minWidthToWrap) return str2;
5390
- 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/);
5391
5488
  const chunkPattern = /[\s]*[^\s]+/g;
5392
5489
  const wrappedLines = [];
5393
5490
  rawLines.forEach((line) => {
@@ -5677,9 +5774,9 @@ var DualOptions = class {
5677
5774
  return option.negate === (negativeValue === value);
5678
5775
  }
5679
5776
  };
5680
- function camelcase(str2) {
5681
- return str2.split("-").reduce((str3, word) => {
5682
- 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);
5683
5780
  });
5684
5781
  }
5685
5782
  function splitOptionFlags(flags) {
@@ -5839,14 +5936,14 @@ var Command = class _Command extends EventEmitter {
5839
5936
  this._showSuggestionAfterError = true;
5840
5937
  this._savedState = null;
5841
5938
  this._outputConfiguration = {
5842
- writeOut: (str2) => process2.stdout.write(str2),
5843
- writeErr: (str2) => process2.stderr.write(str2),
5844
- 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),
5845
5942
  getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : void 0,
5846
5943
  getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : void 0,
5847
5944
  getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
5848
5945
  getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
5849
- stripColor: (str2) => stripVTControlCharacters2(str2)
5946
+ stripColor: (str3) => stripVTControlCharacters2(str3)
5850
5947
  };
5851
5948
  this._hidden = false;
5852
5949
  this._helpOption = void 0;
@@ -7574,18 +7671,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
7574
7671
  * @param {string} [description]
7575
7672
  * @return {(this | string | undefined)} `this` command for chaining, or version string if no arguments
7576
7673
  */
7577
- version(str2, flags, description) {
7578
- if (str2 === void 0) return this._version;
7579
- this._version = str2;
7674
+ version(str3, flags, description) {
7675
+ if (str3 === void 0) return this._version;
7676
+ this._version = str3;
7580
7677
  flags = flags || "-V, --version";
7581
7678
  description = description || "output the version number";
7582
7679
  const versionOption = this.createOption(flags, description);
7583
7680
  this._versionOptionName = versionOption.attributeName();
7584
7681
  this._registerOption(versionOption);
7585
7682
  this.on("option:" + versionOption.name(), () => {
7586
- this._outputConfiguration.writeOut(`${str2}
7683
+ this._outputConfiguration.writeOut(`${str3}
7587
7684
  `);
7588
- this._exit(0, "commander.version", str2);
7685
+ this._exit(0, "commander.version", str3);
7589
7686
  });
7590
7687
  return this;
7591
7688
  }
@@ -7596,10 +7693,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
7596
7693
  * @param {object} [argsDescription]
7597
7694
  * @return {(string|Command)}
7598
7695
  */
7599
- description(str2, argsDescription) {
7600
- if (str2 === void 0 && argsDescription === void 0)
7696
+ description(str3, argsDescription) {
7697
+ if (str3 === void 0 && argsDescription === void 0)
7601
7698
  return this._description;
7602
- this._description = str2;
7699
+ this._description = str3;
7603
7700
  if (argsDescription) {
7604
7701
  this._argsDescription = argsDescription;
7605
7702
  }
@@ -7611,9 +7708,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7611
7708
  * @param {string} [str]
7612
7709
  * @return {(string|Command)}
7613
7710
  */
7614
- summary(str2) {
7615
- if (str2 === void 0) return this._summary;
7616
- this._summary = str2;
7711
+ summary(str3) {
7712
+ if (str3 === void 0) return this._summary;
7713
+ this._summary = str3;
7617
7714
  return this;
7618
7715
  }
7619
7716
  /**
@@ -7661,8 +7758,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
7661
7758
  * @param {string} [str]
7662
7759
  * @return {(string|Command)}
7663
7760
  */
7664
- usage(str2) {
7665
- if (str2 === void 0) {
7761
+ usage(str3) {
7762
+ if (str3 === void 0) {
7666
7763
  if (this._usage) return this._usage;
7667
7764
  const args = this.registeredArguments.map((arg) => {
7668
7765
  return humanReadableArgName(arg);
@@ -7673,7 +7770,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
7673
7770
  this.registeredArguments.length ? args : []
7674
7771
  ).join(" ");
7675
7772
  }
7676
- this._usage = str2;
7773
+ this._usage = str3;
7677
7774
  return this;
7678
7775
  }
7679
7776
  /**
@@ -7682,9 +7779,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7682
7779
  * @param {string} [str]
7683
7780
  * @return {(string|Command)}
7684
7781
  */
7685
- name(str2) {
7686
- if (str2 === void 0) return this._name;
7687
- this._name = str2;
7782
+ name(str3) {
7783
+ if (str3 === void 0) return this._name;
7784
+ this._name = str3;
7688
7785
  return this;
7689
7786
  }
7690
7787
  /**
@@ -7777,9 +7874,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
7777
7874
  * @param {string} [path]
7778
7875
  * @return {(string|null|Command)}
7779
7876
  */
7780
- executableDir(path20) {
7781
- if (path20 === void 0) return this._executableDir;
7782
- this._executableDir = path20;
7877
+ executableDir(path21) {
7878
+ if (path21 === void 0) return this._executableDir;
7879
+ this._executableDir = path21;
7783
7880
  return this;
7784
7881
  }
7785
7882
  /**
@@ -7818,17 +7915,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
7818
7915
  let hasColors;
7819
7916
  let helpWidth;
7820
7917
  if (error) {
7821
- baseWrite = (str2) => this._outputConfiguration.writeErr(str2);
7918
+ baseWrite = (str3) => this._outputConfiguration.writeErr(str3);
7822
7919
  hasColors = this._outputConfiguration.getErrHasColors();
7823
7920
  helpWidth = this._outputConfiguration.getErrHelpWidth();
7824
7921
  } else {
7825
- baseWrite = (str2) => this._outputConfiguration.writeOut(str2);
7922
+ baseWrite = (str3) => this._outputConfiguration.writeOut(str3);
7826
7923
  hasColors = this._outputConfiguration.getOutHasColors();
7827
7924
  helpWidth = this._outputConfiguration.getOutHelpWidth();
7828
7925
  }
7829
- const write = (str2) => {
7830
- if (!hasColors) str2 = this._outputConfiguration.stripColor(str2);
7831
- return baseWrite(str2);
7926
+ const write = (str3) => {
7927
+ if (!hasColors) str3 = this._outputConfiguration.stripColor(str3);
7928
+ return baseWrite(str3);
7832
7929
  };
7833
7930
  return { error, write, hasColors, helpWidth };
7834
7931
  }
@@ -8037,7 +8134,7 @@ var program = new Command();
8037
8134
  // server/src/cli/program.ts
8038
8135
  init_paths();
8039
8136
  init_settings();
8040
- var VERSION2 = true ? "0.15.7" : "dev";
8137
+ var VERSION2 = true ? "0.16.0" : "dev";
8041
8138
  async function bootstrap() {
8042
8139
  const file = configFilePath();
8043
8140
  const settings = readSettings(file);
@@ -8050,7 +8147,7 @@ async function bootstrap() {
8050
8147
  `);
8051
8148
  }
8052
8149
  const { installAgentFile: installAgentFile2 } = await Promise.resolve().then(() => (init_agentFile(), agentFile_exports));
8053
- const agent = installAgentFile2(os4.homedir(), dataDirPath());
8150
+ const agent = installAgentFile2(os5.homedir(), dataDirPath());
8054
8151
  if (agent.action === "installed" || agent.action === "updated") {
8055
8152
  process.stdout.write(`casper agent ${agent.action}: ${agent.target}
8056
8153
  `);
@@ -8083,8 +8180,8 @@ async function resetToken(value) {
8083
8180
  updateSettings(file, { token });
8084
8181
  process.stdout.write(`wrote a new token to ${file}
8085
8182
  `);
8086
- const dbFile = path19.join(dataDirPath(), "casper.db");
8087
- if (fs19.existsSync(dbFile)) {
8183
+ const dbFile = path20.join(dataDirPath(), "casper.db");
8184
+ if (fs20.existsSync(dbFile)) {
8088
8185
  const { LoginStore: LoginStore2 } = await Promise.resolve().then(() => (init_logins(), logins_exports));
8089
8186
  new LoginStore2().revokeAll();
8090
8187
  process.stdout.write("revoked all device sessions\n");