@joeyshi12/casper 0.12.0 → 0.13.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 (70) hide show
  1. package/dist/mcp.js +1 -1
  2. package/dist/server.js +212 -220
  3. package/dist/web/assets/{abnfDiagram-N423BO3Z-4Ko3ozfJ.js → abnfDiagram-N423BO3Z-Ckdr5R00.js} +1 -1
  4. package/dist/web/assets/{arc-Ch7Ehjbn.js → arc-RD0bGtxd.js} +1 -1
  5. package/dist/web/assets/{architectureDiagram-T3A2C74G-DcWU5yRD.js → architectureDiagram-T3A2C74G-CHkgCulK.js} +1 -1
  6. package/dist/web/assets/{blockDiagram-VBNYF7ZC-BUzWG1_k.js → blockDiagram-VBNYF7ZC-bs7XxVhZ.js} +1 -1
  7. package/dist/web/assets/{c4Diagram-5PPSVZJV-Cbk3RgdU.js → c4Diagram-5PPSVZJV-sipmg1A6.js} +1 -1
  8. package/dist/web/assets/channel-D8Rxtl4g.js +1 -0
  9. package/dist/web/assets/{chunk-2GRJ4B5K-B0WXdW3m.js → chunk-2GRJ4B5K-Dk5uhiWq.js} +1 -1
  10. package/dist/web/assets/{chunk-2Q5K7J3B-Dr5AcgBH.js → chunk-2Q5K7J3B-RLD9D6CE.js} +1 -1
  11. package/dist/web/assets/{chunk-5RXB4S5H-NRZvLElM.js → chunk-5RXB4S5H-Bisltr8Y.js} +1 -1
  12. package/dist/web/assets/{chunk-5VM5RSS4-B2ezMICZ.js → chunk-5VM5RSS4-BFuA55Yw.js} +1 -1
  13. package/dist/web/assets/{chunk-6Q2QTUOP-0_kTL1pm.js → chunk-6Q2QTUOP-Dgi0Ckwy.js} +1 -1
  14. package/dist/web/assets/{chunk-GF5L2VYU-ChJJ-3FT.js → chunk-GF5L2VYU-sLBx1TNb.js} +1 -1
  15. package/dist/web/assets/{chunk-JWPE2WC7-B15-wRYl.js → chunk-JWPE2WC7-BoIB6XgD.js} +1 -1
  16. package/dist/web/assets/{chunk-KBJHAD2P-DsR7v9qB.js → chunk-KBJHAD2P-DE2z1pFd.js} +1 -1
  17. package/dist/web/assets/{chunk-RYQCIY6F-BkT8T354.js → chunk-RYQCIY6F-S_juj3_n.js} +1 -1
  18. package/dist/web/assets/{chunk-XXDRQBXY-CRB4wwou.js → chunk-XXDRQBXY-gCHzWtzi.js} +1 -1
  19. package/dist/web/assets/classDiagram-JCYQIIEL-Ch31vHtu.js +1 -0
  20. package/dist/web/assets/classDiagram-v2-OCEON4UE-Ch31vHtu.js +1 -0
  21. package/dist/web/assets/{cose-bilkent-JH36ORCC-BESlNRq3.js → cose-bilkent-JH36ORCC-Dp9lkOsx.js} +1 -1
  22. package/dist/web/assets/{cynefin-VYW2F7L2-BPfoy8I9.js → cynefin-VYW2F7L2-OwRF9q4d.js} +1 -1
  23. package/dist/web/assets/{cynefinDiagram-MW4NZA55-Byn6JJg0.js → cynefinDiagram-MW4NZA55-DQsh1pP_.js} +1 -1
  24. package/dist/web/assets/{dagre-VZM6K2ZE-CF6UO64P.js → dagre-VZM6K2ZE-BOwA_-xr.js} +1 -1
  25. package/dist/web/assets/{diagram-7IWD3JNH-DgIzI-sU.js → diagram-7IWD3JNH-DCcvWrT9.js} +1 -1
  26. package/dist/web/assets/{diagram-B4RE2ZJO-DTXfP5om.js → diagram-B4RE2ZJO-1TUDDf93.js} +1 -1
  27. package/dist/web/assets/{diagram-LBJQPF4R-CTLNzser.js → diagram-LBJQPF4R-CQ9CHasC.js} +1 -1
  28. package/dist/web/assets/{diagram-Q27KOJAE-M38db-G2.js → diagram-Q27KOJAE-CnzspMbf.js} +1 -1
  29. package/dist/web/assets/{diagram-UB23O5K3-CG-vZyFB.js → diagram-UB23O5K3-DwSm7ENR.js} +1 -1
  30. package/dist/web/assets/{ebnfDiagram-BXEA7PRR-CsJ9VNb2.js → ebnfDiagram-BXEA7PRR-B88O4qYG.js} +1 -1
  31. package/dist/web/assets/{erDiagram-JOGREHBK-3dK0h9cf.js → erDiagram-JOGREHBK-RVTjJMdw.js} +1 -1
  32. package/dist/web/assets/{flowDiagram-UKHOOZJN-DHTGEHfr.js → flowDiagram-UKHOOZJN-CAGMFzJU.js} +1 -1
  33. package/dist/web/assets/{ganttDiagram-PKOTCBZU-DiLysF1Q.js → ganttDiagram-PKOTCBZU-CEvl6Pim.js} +1 -1
  34. package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-2uthJlH6.js → gitGraphDiagram-DS77QQ5N-CH6kGWAl.js} +1 -1
  35. package/dist/web/assets/{index-DbaSy_a8.js → index-B1jqxtEG.js} +1 -1
  36. package/dist/web/assets/index-Bm1zLB-5.js +249 -0
  37. package/dist/web/assets/index-bZk3Q_Yv.css +1 -0
  38. package/dist/web/assets/{infoDiagram-6WML65LV-B35ot4_O.js → infoDiagram-6WML65LV-DAcfWiSl.js} +1 -1
  39. package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-C6nTJ8A6.js → ishikawaDiagram-WSZJBQD7-C0CNHY8o.js} +1 -1
  40. package/dist/web/assets/{journeyDiagram-NVQOT4AX-Bmt5Dt3Z.js → journeyDiagram-NVQOT4AX-Dkf1b7P5.js} +1 -1
  41. package/dist/web/assets/{kanban-definition-27J2QSJJ-DkGk9WrO.js → kanban-definition-27J2QSJJ-DEyWowZE.js} +1 -1
  42. package/dist/web/assets/{linear-D9RsKviS.js → linear-BIoTAhlf.js} +1 -1
  43. package/dist/web/assets/{mermaid.core-BkEkrWDL.js → mermaid.core-DTmrl8Wn.js} +4 -4
  44. package/dist/web/assets/{mindmap-definition-FAOFIHXS-7XkMFJ0C.js → mindmap-definition-FAOFIHXS-mw_tNbPO.js} +1 -1
  45. package/dist/web/assets/{pegDiagram-VL7TDLO6-KpO4Zp9v.js → pegDiagram-VL7TDLO6-BhX4qWsU.js} +1 -1
  46. package/dist/web/assets/{pieDiagram-7S7Q4E2Y-CnZkxeeK.js → pieDiagram-7S7Q4E2Y-D8y4mlrR.js} +1 -1
  47. package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-Jej_KG8V.js → quadrantDiagram-CIZ2JOQS-eRi0JVMP.js} +1 -1
  48. package/dist/web/assets/{railroadDiagram-AXF67PYL-BfteVM0C.js → railroadDiagram-AXF67PYL-BtWC75OM.js} +1 -1
  49. package/dist/web/assets/{requirementDiagram-LRYGKXZP-BTSeDpTM.js → requirementDiagram-LRYGKXZP-QqWK5Q2W.js} +1 -1
  50. package/dist/web/assets/{sankeyDiagram-W5VNT64P-CeB7UW0v.js → sankeyDiagram-W5VNT64P-DWPZuz_o.js} +1 -1
  51. package/dist/web/assets/{sequenceDiagram-SI44F4Z6-BeOLM9UY.js → sequenceDiagram-SI44F4Z6-BjtTZ0wA.js} +1 -1
  52. package/dist/web/assets/{sizeCapture-X5ZJPWSS-7aezJBJp.js → sizeCapture-X5ZJPWSS-CHpioWdU.js} +1 -1
  53. package/dist/web/assets/{stateDiagram-OKZ733FA-DmJkX_vQ.js → stateDiagram-OKZ733FA-CUyQMj6M.js} +1 -1
  54. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-BStIRqFz.js +1 -0
  55. package/dist/web/assets/{swimlanes-SLNWSIFB-CmnhwW2u.js → swimlanes-SLNWSIFB-DDbnLkK8.js} +2 -2
  56. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-D-e3uCkk.js +8 -0
  57. package/dist/web/assets/{timeline-definition-Z64GVDOM-DtxMhUPD.js → timeline-definition-Z64GVDOM-Cxc91WrS.js} +1 -1
  58. package/dist/web/assets/{vennDiagram-T6HMQDX7-DO34MvMM.js → vennDiagram-T6HMQDX7-CjOhqkxb.js} +1 -1
  59. package/dist/web/assets/{wardleyDiagram-T6FBY63Y-s2zhjdd1.js → wardleyDiagram-T6FBY63Y-4Ag9jAUE.js} +1 -1
  60. package/dist/web/assets/{xychartDiagram-ELKLHX3M-CnCQs0NK.js → xychartDiagram-ELKLHX3M-BbQy0Bji.js} +1 -1
  61. package/dist/web/index.html +2 -2
  62. package/dist/web/sw.js +1 -1
  63. package/package.json +1 -1
  64. package/dist/web/assets/channel-DSuKNwf1.js +0 -1
  65. package/dist/web/assets/classDiagram-JCYQIIEL-DY_Arw9F.js +0 -1
  66. package/dist/web/assets/classDiagram-v2-OCEON4UE-DY_Arw9F.js +0 -1
  67. package/dist/web/assets/index-D3zBm-7r.js +0 -249
  68. package/dist/web/assets/index-D_ANHT9G.css +0 -1
  69. package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CkEEUkE9.js +0 -1
  70. package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DaOmpb-r.js +0 -8
package/dist/server.js CHANGED
@@ -391,13 +391,11 @@ var init_db = __esm({
391
391
  "use strict";
392
392
  init_config();
393
393
  SCHEMA = `
394
- CREATE TABLE IF NOT EXISTS sessions (
395
- session_id TEXT PRIMARY KEY,
394
+ CREATE TABLE IF NOT EXISTS chats (
395
+ chat_id TEXT PRIMARY KEY,
396
+ session_id TEXT UNIQUE,
396
397
  title TEXT,
397
- cwd TEXT,
398
- -- The chat that owns this session's uploads (see chats.ts). NULL for a session Casper did
399
- -- not create; getChatId names those after the session.
400
- chat_id TEXT
398
+ cwd TEXT
401
399
  );
402
400
  CREATE TABLE IF NOT EXISTS logins (
403
401
  id TEXT PRIMARY KEY,
@@ -408,13 +406,13 @@ CREATE TABLE IF NOT EXISTS logins (
408
406
  );
409
407
  CREATE INDEX IF NOT EXISTS logins_hash ON logins (hash);
410
408
  CREATE TABLE IF NOT EXISTS message_attachments (
411
- session_id TEXT NOT NULL,
412
- ordinal INTEGER NOT NULL,
413
- path TEXT NOT NULL,
414
- name TEXT NOT NULL,
415
- size INTEGER NOT NULL,
416
- kind TEXT NOT NULL,
417
- PRIMARY KEY (session_id, ordinal, path)
409
+ chat_id TEXT NOT NULL,
410
+ ordinal INTEGER NOT NULL,
411
+ path TEXT NOT NULL,
412
+ name TEXT NOT NULL,
413
+ size INTEGER NOT NULL,
414
+ kind TEXT NOT NULL,
415
+ PRIMARY KEY (chat_id, ordinal, path)
418
416
  );
419
417
  `;
420
418
  }
@@ -867,7 +865,7 @@ function isValidSessionId(id) {
867
865
  return /^[A-Za-z0-9._-]+$/.test(id) && id !== "." && !id.includes("..");
868
866
  }
869
867
  function isValidChatId(id) {
870
- return !!id && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
868
+ return !!id && isValidSessionId(id);
871
869
  }
872
870
  var realRootCache;
873
871
  var init_paths2 = __esm({
@@ -1192,13 +1190,13 @@ var init_EventStore = __esm({
1192
1190
  "use strict";
1193
1191
  init_config();
1194
1192
  EventStore = class extends EventEmitter4 {
1195
- sessionId;
1193
+ chatId;
1196
1194
  buffer = [];
1197
1195
  capacity;
1198
1196
  seq = 0;
1199
- constructor(sessionId) {
1197
+ constructor(chatId) {
1200
1198
  super();
1201
- this.sessionId = sessionId;
1199
+ this.chatId = chatId;
1202
1200
  this.capacity = config.eventBufferSize;
1203
1201
  }
1204
1202
  /** Append an event, assign it the next seq, and fan it out. */
@@ -1207,7 +1205,7 @@ var init_EventStore = __esm({
1207
1205
  const event = {
1208
1206
  seq: this.seq,
1209
1207
  ts: Date.now(),
1210
- sessionId: this.sessionId,
1208
+ chatId: this.chatId,
1211
1209
  payload
1212
1210
  };
1213
1211
  this.buffer.push(event);
@@ -1325,43 +1323,16 @@ function summarize(j) {
1325
1323
  const contextUsagePercentage = state?.rts_model_state?.context_usage_percentage ?? turns[turns.length - 1]?.context_usage_percentage;
1326
1324
  return {
1327
1325
  sessionId: j.session_id,
1328
- // kiro's file knows nothing about chats; summaryOf replaces this with the recorded id
1329
- // when there is one, and this matches what getChatId names a session without a row.
1330
- chatId: j.session_id,
1331
1326
  // Left empty when kiro has not named it; resolveSessionTitle decides what to show.
1332
1327
  title: j.title?.trim() ?? "",
1333
1328
  cwd: j.cwd,
1334
1329
  createdAt: j.created_at,
1335
1330
  updatedAt: j.updated_at,
1336
- liveness: "dormant",
1337
1331
  agentId: state?.agent_name,
1338
1332
  modelId: state?.rts_model_state?.model_info?.model_id,
1339
- running: false,
1340
1333
  contextUsagePercentage
1341
1334
  };
1342
1335
  }
1343
- async function listPersistedSessions(log) {
1344
- let files;
1345
- try {
1346
- files = await fs9.readdir(config.kiroSessionsDir);
1347
- } catch {
1348
- return [];
1349
- }
1350
- const jsonFiles = files.filter((f) => f.endsWith(".json"));
1351
- const summaries = [];
1352
- await Promise.all(
1353
- jsonFiles.map(async (f) => {
1354
- try {
1355
- const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, f), "utf8");
1356
- summaries.push(summarize(JSON.parse(raw)));
1357
- } catch (err) {
1358
- log.debug({ err, f }, "kiroFiles: skipping unreadable session file");
1359
- }
1360
- })
1361
- );
1362
- summaries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1363
- return summaries;
1364
- }
1365
1336
  async function deletePersistedSession(sessionId) {
1366
1337
  if (!isValidSessionId(sessionId)) return;
1367
1338
  const targets = [
@@ -1482,54 +1453,66 @@ var init_kiroFiles = __esm({
1482
1453
  }
1483
1454
  });
1484
1455
 
1485
- // server/src/session/sessionStore.ts
1486
- var SessionStore;
1487
- var init_sessionStore = __esm({
1488
- "server/src/session/sessionStore.ts"() {
1456
+ // server/src/session/chatStore.ts
1457
+ var ChatStore;
1458
+ var init_chatStore = __esm({
1459
+ "server/src/session/chatStore.ts"() {
1489
1460
  "use strict";
1490
1461
  init_db();
1491
- SessionStore = class {
1492
- getTitle(sessionId) {
1493
- return this.read(sessionId, "title");
1462
+ ChatStore = class {
1463
+ /** Every chat. Ordering is kiro's business, applied once the file is joined on. */
1464
+ all() {
1465
+ return db().prepare("SELECT chat_id, session_id, title, cwd FROM chats").all().map((r) => ({ chatId: r.chat_id, sessionId: r.session_id, title: r.title, cwd: r.cwd }));
1494
1466
  }
1495
- setTitle(sessionId, title) {
1496
- this.write(sessionId, "title", title);
1467
+ get(chatId) {
1468
+ const r = db().prepare("SELECT chat_id, session_id, title, cwd FROM chats WHERE chat_id = ?").get(chatId);
1469
+ return r ? { chatId: r.chat_id, sessionId: r.session_id, title: r.title, cwd: r.cwd } : void 0;
1497
1470
  }
1498
- getCwd(sessionId) {
1499
- return this.read(sessionId, "cwd");
1500
- }
1501
- setCwd(sessionId, cwd) {
1502
- this.write(sessionId, "cwd", cwd);
1471
+ create(chatId) {
1472
+ db().prepare("INSERT INTO chats (chat_id) VALUES (?) ON CONFLICT(chat_id) DO NOTHING").run(chatId);
1503
1473
  }
1504
1474
  /**
1505
- * The chat that owns this session's uploads. A session Casper did not create has no row -
1506
- * one started with kiro-cli directly still shows up in the list - so it is named after the
1507
- * session, which gives it one stable directory rather than a new one per call.
1475
+ * kiro names the session once it starts one; bind it to the chat that owns it. A session
1476
+ * belongs to exactly one chat, so any earlier claim on it is released.
1508
1477
  */
1509
- getChatId(sessionId) {
1510
- return this.read(sessionId, "chat_id") ?? sessionId;
1478
+ bindSession(chatId, sessionId) {
1479
+ db().prepare("UPDATE chats SET session_id = NULL WHERE session_id = ? AND chat_id <> ?").run(sessionId, chatId);
1480
+ this.write(chatId, "session_id", sessionId);
1481
+ }
1482
+ sessionIdForChat(chatId) {
1483
+ const row = db().prepare("SELECT session_id FROM chats WHERE chat_id = ?").get(chatId);
1484
+ return row?.session_id ?? void 0;
1485
+ }
1486
+ getTitle(chatId) {
1487
+ return this.read(chatId, "title");
1488
+ }
1489
+ setTitle(chatId, title) {
1490
+ this.write(chatId, "title", title);
1511
1491
  }
1512
- setChatId(sessionId, chatId) {
1513
- this.write(sessionId, "chat_id", chatId);
1492
+ getCwd(chatId) {
1493
+ return this.read(chatId, "cwd");
1494
+ }
1495
+ setCwd(chatId, cwd) {
1496
+ this.write(chatId, "cwd", cwd);
1514
1497
  }
1515
1498
  /** Record what was attached to one prompt. See db.ts for why the key is an ordinal. */
1516
- setAttachments(sessionId, ordinal, files) {
1499
+ setAttachments(chatId, ordinal, files) {
1517
1500
  if (files.length === 0) return;
1518
1501
  const stmt = db().prepare(
1519
- `INSERT INTO message_attachments (session_id, ordinal, path, name, size, kind)
1502
+ `INSERT INTO message_attachments (chat_id, ordinal, path, name, size, kind)
1520
1503
  VALUES (?, ?, ?, ?, ?, ?)
1521
- ON CONFLICT(session_id, ordinal, path) DO NOTHING`
1504
+ ON CONFLICT(chat_id, ordinal, path) DO NOTHING`
1522
1505
  );
1523
1506
  for (const f of files) {
1524
- stmt.run(sessionId, ordinal, f.path, f.name, f.size, f.kind);
1507
+ stmt.run(chatId, ordinal, f.path, f.name, f.size, f.kind);
1525
1508
  }
1526
1509
  }
1527
- /** Every attachment in a session, grouped by the message it belongs to. */
1528
- attachmentsBySession(sessionId) {
1510
+ /** Every attachment in a chat, grouped by the message it belongs to. */
1511
+ attachmentsByChat(chatId) {
1529
1512
  const rows = db().prepare(
1530
1513
  `SELECT ordinal, path, name, size, kind FROM message_attachments
1531
- WHERE session_id = ? ORDER BY ordinal, path`
1532
- ).all(sessionId);
1514
+ WHERE chat_id = ? ORDER BY ordinal, path`
1515
+ ).all(chatId);
1533
1516
  const out = /* @__PURE__ */ new Map();
1534
1517
  for (const r of rows) {
1535
1518
  const list = out.get(r.ordinal) ?? [];
@@ -1543,20 +1526,19 @@ var init_sessionStore = __esm({
1543
1526
  }
1544
1527
  return out;
1545
1528
  }
1546
- /** Forget every override for a session, when it's permanently deleted. */
1547
- remove(sessionId) {
1548
- db().prepare("DELETE FROM sessions WHERE session_id = ?").run(sessionId);
1549
- db().prepare("DELETE FROM message_attachments WHERE session_id = ?").run(sessionId);
1529
+ remove(chatId) {
1530
+ db().prepare("DELETE FROM chats WHERE chat_id = ?").run(chatId);
1531
+ db().prepare("DELETE FROM message_attachments WHERE chat_id = ?").run(chatId);
1550
1532
  }
1551
- read(sessionId, column) {
1552
- const row = db().prepare(`SELECT ${column} AS value FROM sessions WHERE session_id = ?`).get(sessionId);
1533
+ read(chatId, column) {
1534
+ const row = db().prepare(`SELECT ${column} AS value FROM chats WHERE chat_id = ?`).get(chatId);
1553
1535
  return row?.value ?? void 0;
1554
1536
  }
1555
- write(sessionId, column, value) {
1537
+ write(chatId, column, value) {
1556
1538
  db().prepare(
1557
- `INSERT INTO sessions (session_id, ${column}) VALUES (?, ?)
1558
- ON CONFLICT(session_id) DO UPDATE SET ${column} = excluded.${column}`
1559
- ).run(sessionId, value);
1539
+ `INSERT INTO chats (chat_id, ${column}) VALUES (?, ?)
1540
+ ON CONFLICT(chat_id) DO UPDATE SET ${column} = excluded.${column}`
1541
+ ).run(chatId, value);
1560
1542
  }
1561
1543
  };
1562
1544
  }
@@ -1620,14 +1602,12 @@ var init_SessionManager = __esm({
1620
1602
  init_EventStore();
1621
1603
  init_TurnState();
1622
1604
  init_kiroFiles();
1623
- init_sessionStore();
1605
+ init_chatStore();
1624
1606
  Session = class {
1625
1607
  sessionId;
1626
1608
  store;
1627
1609
  turnState = new TurnState();
1628
1610
  cwd;
1629
- /** The chat that owns this session's uploads. See chats.ts. */
1630
- chatId;
1631
1611
  agentId;
1632
1612
  modelId;
1633
1613
  currentModeId;
@@ -1681,7 +1661,7 @@ var init_SessionManager = __esm({
1681
1661
  SessionManager = class {
1682
1662
  sessions = /* @__PURE__ */ new Map();
1683
1663
  log;
1684
- store = new SessionStore();
1664
+ store = new ChatStore();
1685
1665
  spawnProcess;
1686
1666
  constructor(log, opts = {}) {
1687
1667
  this.log = log;
@@ -1691,34 +1671,33 @@ var init_SessionManager = __esm({
1691
1671
  * What a session is called, from every read path. The folder is skipped for a workspace of
1692
1672
  * ours, whose name is a uuid.
1693
1673
  */
1694
- titleOf(sessionId, parts) {
1674
+ titleOf(chat, parts) {
1695
1675
  return resolveSessionTitle({
1696
- override: this.store.getTitle(sessionId),
1676
+ override: chat.title ?? void 0,
1697
1677
  kiroTitle: parts.kiroTitle,
1698
1678
  firstPrompt: parts.firstPrompt,
1699
1679
  folder: isManagedWorkspace(parts.cwd) ? void 0 : path11.basename(parts.cwd)
1700
1680
  });
1701
1681
  }
1702
- /** Set a user title override for a session. */
1703
- renameSession(sessionId, title) {
1682
+ renameChat(chatId, title) {
1704
1683
  const clean = sanitizeTitle(title);
1705
- this.store.setTitle(sessionId, clean);
1706
- const s = this.sessions.get(sessionId);
1684
+ this.store.setTitle(chatId, clean);
1685
+ const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
1707
1686
  if (s) s.title = clean || s.title;
1708
1687
  }
1709
1688
  /**
1710
1689
  * Re-point a session at a different working directory. Any live process was spawned with the
1711
1690
  * old cwd, so it is disposed and the next turn respawns in the new one, transcript intact.
1712
1691
  */
1713
- async setSessionCwd(sessionId, input) {
1692
+ async setChatCwd(chatId, input) {
1714
1693
  const resolved = resolveCwd(input);
1715
- const s = await this.ensureOpen(sessionId);
1716
- this.store.setCwd(sessionId, resolved);
1694
+ const s = await this.ensureOpen(chatId);
1695
+ this.store.setCwd(chatId, resolved);
1717
1696
  if (s.cwd !== resolved) {
1718
1697
  s.cwd = resolved;
1719
1698
  s.proc?.dispose();
1720
1699
  s.proc = void 0;
1721
- this.log.info({ sessionId, cwd: resolved }, "session working directory changed");
1700
+ this.log.info({ chatId, cwd: resolved }, "chat working directory changed");
1722
1701
  }
1723
1702
  return resolved;
1724
1703
  }
@@ -1731,8 +1710,8 @@ var init_SessionManager = __esm({
1731
1710
  * The old child must be awaited out first: kiro flushes its session file on shutdown and
1732
1711
  * the replacement reads that file to reload the conversation.
1733
1712
  */
1734
- async reloadSession(sessionId) {
1735
- const s = await this.ensureOpen(sessionId);
1713
+ async reloadChat(chatId) {
1714
+ const s = await this.ensureOpen(chatId);
1736
1715
  if (s.running) {
1737
1716
  throw new Error("Cannot reload while a turn is running");
1738
1717
  }
@@ -1747,14 +1726,14 @@ var init_SessionManager = __esm({
1747
1726
  ready = resolve;
1748
1727
  });
1749
1728
  try {
1750
- return await this.replaceProcess(s);
1729
+ return await this.replaceProcess(chatId, s);
1751
1730
  } finally {
1752
1731
  s.reloading = void 0;
1753
1732
  ready();
1754
1733
  }
1755
1734
  }
1756
1735
  /** The reload itself. Only ever called with the session's reload claim held. */
1757
- async replaceProcess(s) {
1736
+ async replaceProcess(chatId, s) {
1758
1737
  if (!await hasRecordedTurns(s.sessionId)) {
1759
1738
  throw new Error(
1760
1739
  "kiro has not saved this session yet. Send a message first, then reload."
@@ -1773,7 +1752,7 @@ var init_SessionManager = __esm({
1773
1752
  s.lastActivity = Date.now();
1774
1753
  invalidateAgents();
1775
1754
  this.log.info({ sessionId: s.sessionId, cwd: s.cwd }, "session reloaded");
1776
- return this.getDetail(s.sessionId);
1755
+ return this.getDetail(chatId);
1777
1756
  }
1778
1757
  /**
1779
1758
  * Wait out a reload before touching the session's process. Without this an action
@@ -1790,33 +1769,34 @@ var init_SessionManager = __esm({
1790
1769
  // -------------------------------------------------------------------------
1791
1770
  // Event subscription - works for any opened session, spawned or not.
1792
1771
  // -------------------------------------------------------------------------
1793
- onEvent(sessionId, cb) {
1794
- const s = this.sessions.get(sessionId);
1772
+ onEvent(chatId, cb) {
1773
+ const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
1795
1774
  if (!s) return null;
1796
1775
  s.store.on("event", cb);
1797
1776
  return () => s.store.off("event", cb);
1798
1777
  }
1799
- getStore(sessionId) {
1800
- return this.sessions.get(sessionId)?.store;
1778
+ getStore(chatId) {
1779
+ return this.sessions.get(this.store.sessionIdForChat(chatId) ?? "")?.store;
1801
1780
  }
1802
- /** Get a session's working directory. Opens the session in memory if needed. */
1803
- async getSessionCwd(sessionId) {
1804
- const s = await this.ensureOpen(sessionId);
1781
+ /** A chat's working directory. Opens it in memory if needed. */
1782
+ async getChatCwd(chatId) {
1783
+ const s = await this.ensureOpen(chatId);
1805
1784
  return s.cwd;
1806
1785
  }
1807
- /** Open a session in memory (store + metadata) WITHOUT spawning a process. */
1808
- async ensureOpen(sessionId) {
1786
+ /** Open a chat's session in memory (store + metadata) WITHOUT spawning a process. */
1787
+ async ensureOpen(chatId) {
1788
+ const sessionId = this.sessionIdOf(chatId);
1809
1789
  const existing = this.sessions.get(sessionId);
1810
1790
  if (existing) return existing;
1811
1791
  const persisted = await readPersistedSession(sessionId);
1812
1792
  if (!persisted) throw new Error(`Unknown session: ${sessionId}`);
1813
- const effectiveCwd = this.store.getCwd(sessionId) ?? persisted.cwd;
1793
+ const effectiveCwd = this.store.getCwd(chatId) ?? persisted.cwd;
1814
1794
  if (!isWithinRoot(config.fileRoot, effectiveCwd)) {
1815
1795
  throw new Error(
1816
1796
  `Session working directory is outside the allowed root: ${effectiveCwd}`
1817
1797
  );
1818
1798
  }
1819
- const store = new EventStore(sessionId);
1799
+ const store = new EventStore(chatId);
1820
1800
  const s = new Session(sessionId, store, effectiveCwd);
1821
1801
  s.title = persisted.title;
1822
1802
  s.agentId = persisted.agentId;
@@ -1912,11 +1892,11 @@ var init_SessionManager = __esm({
1912
1892
  // Creating / opening
1913
1893
  // -------------------------------------------------------------------------
1914
1894
  /** Create a new session. Spawns immediately so we get a real kiro sessionId. */
1915
- async createSession(opts) {
1895
+ async createChat(opts) {
1916
1896
  const chatId = isValidChatId(opts.chatId) ? opts.chatId : crypto.randomUUID();
1917
1897
  const cwd = opts.freshWorkspace ? createChatWorkspace(chatId) : resolveCwd(opts.cwd);
1918
1898
  const tempId = `pending-${Date.now()}-${Math.floor(this.sessions.size)}`;
1919
- const store = new EventStore(tempId);
1899
+ const store = new EventStore(chatId);
1920
1900
  const s = new Session(tempId, store, cwd);
1921
1901
  s.agentId = opts.agentId ?? config.defaultAgent;
1922
1902
  s.currentModeId = s.agentId;
@@ -1928,20 +1908,20 @@ var init_SessionManager = __esm({
1928
1908
  this.evict(s.sessionId);
1929
1909
  throw err;
1930
1910
  }
1931
- this.store.setChatId(s.sessionId, chatId);
1932
- s.chatId = chatId;
1911
+ this.store.create(chatId);
1912
+ this.store.bindSession(chatId, s.sessionId);
1933
1913
  const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path11.basename(s.cwd));
1934
1914
  if (name) {
1935
- this.store.setTitle(s.sessionId, name);
1915
+ this.store.setTitle(chatId, name);
1936
1916
  s.title = name;
1937
1917
  }
1938
- return this.buildDetail(s, []);
1918
+ return this.buildDetail(this.store.get(chatId), s, []);
1939
1919
  }
1940
1920
  // -------------------------------------------------------------------------
1941
1921
  // Actions - these spawn the process lazily.
1942
1922
  // -------------------------------------------------------------------------
1943
- async runPrompt(sessionId, content, attachments) {
1944
- const s = await this.ensureOpen(sessionId);
1923
+ async runPrompt(chatId, content, attachments) {
1924
+ const s = await this.ensureOpen(chatId);
1945
1925
  await this.settleReload(s);
1946
1926
  if (s.running) throw new Error("A turn is already running for this session");
1947
1927
  s.running = true;
@@ -1953,17 +1933,17 @@ var init_SessionManager = __esm({
1953
1933
  s.running = false;
1954
1934
  throw err;
1955
1935
  }
1956
- if (!this.store.getTitle(s.sessionId)) {
1936
+ if (!this.store.getTitle(chatId)) {
1957
1937
  const title = titleFromPrompt(content);
1958
1938
  if (title) {
1959
- this.store.setTitle(s.sessionId, title);
1939
+ this.store.setTitle(chatId, title);
1960
1940
  s.title = title;
1961
1941
  }
1962
1942
  }
1963
1943
  let recorded;
1964
1944
  if (attachments?.length) {
1965
1945
  const ordinal = await promptCount(s.sessionId);
1966
- this.store.setAttachments(s.sessionId, ordinal, attachments);
1946
+ this.store.setAttachments(chatId, ordinal, attachments);
1967
1947
  recorded = attachments;
1968
1948
  }
1969
1949
  s.record({ kind: "turn_started", prompt: content, attachments: recorded });
@@ -1980,12 +1960,12 @@ ${tail}` : err.message;
1980
1960
  s.lastActivity = Date.now();
1981
1961
  });
1982
1962
  }
1983
- cancel(sessionId) {
1984
- const s = this.sessions.get(sessionId);
1963
+ cancel(chatId) {
1964
+ const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
1985
1965
  s?.proc?.cancel(s.sessionId);
1986
1966
  }
1987
- async setMode(sessionId, modeId) {
1988
- const s = await this.ensureOpen(sessionId);
1967
+ async setMode(chatId, modeId) {
1968
+ const s = await this.ensureOpen(chatId);
1989
1969
  await this.settleReload(s);
1990
1970
  const proc = await this.ensureProc(s);
1991
1971
  await proc.setMode(s.sessionId, modeId);
@@ -1993,16 +1973,16 @@ ${tail}` : err.message;
1993
1973
  s.agentId = modeId;
1994
1974
  s.lastActivity = Date.now();
1995
1975
  }
1996
- async setModel(sessionId, modelId) {
1997
- const s = await this.ensureOpen(sessionId);
1976
+ async setModel(chatId, modelId) {
1977
+ const s = await this.ensureOpen(chatId);
1998
1978
  await this.settleReload(s);
1999
1979
  const proc = await this.ensureProc(s);
2000
1980
  await proc.setModel(s.sessionId, modelId);
2001
1981
  s.modelId = modelId;
2002
1982
  s.lastActivity = Date.now();
2003
1983
  }
2004
- async execCommand(sessionId, command) {
2005
- const s = await this.ensureOpen(sessionId);
1984
+ async execCommand(chatId, command) {
1985
+ const s = await this.ensureOpen(chatId);
2006
1986
  await this.settleReload(s);
2007
1987
  const proc = await this.ensureProc(s);
2008
1988
  await proc.execCommand(s.sessionId, command);
@@ -2012,27 +1992,26 @@ ${tail}` : err.message;
2012
1992
  // Listing / detail - never spawns.
2013
1993
  // -------------------------------------------------------------------------
2014
1994
  /**
2015
- * The one place a SessionSummary is assembled: kiro's file and Casper's live state each hold
1995
+ * The one place a ChatSummary is assembled: kiro's file and Casper's live state each hold
2016
1996
  * part of the truth, so every read path projects them through here. `persisted` is absent for
2017
1997
  * a session with no file yet, `live` for a dormant one, and callers guarantee one of them.
2018
1998
  */
2019
- summaryOf(live, persisted, transcript) {
2020
- const sessionId = live?.sessionId ?? persisted.sessionId;
1999
+ summaryOf(chat, live, persisted, transcript) {
2021
2000
  const snap = live?.turnState.get();
2022
- const cwd = live?.cwd ?? this.store.getCwd(sessionId) ?? persisted.cwd;
2001
+ const cwd = live?.cwd ?? chat.cwd ?? persisted?.cwd ?? config.defaultCwd;
2023
2002
  return {
2024
- sessionId,
2025
- chatId: live?.chatId ?? this.store.getChatId(sessionId),
2026
- title: this.titleOf(sessionId, {
2003
+ chatId: chat.chatId,
2004
+ sessionId: live?.sessionId ?? chat.sessionId ?? void 0,
2005
+ title: this.titleOf(chat, {
2027
2006
  // kiro's file is what kiro called it; the live copy is only a cache of it.
2028
2007
  kiroTitle: persisted?.title || live?.title,
2029
2008
  firstPrompt: transcript && firstPromptText(transcript),
2030
2009
  cwd
2031
2010
  }),
2032
2011
  cwd,
2033
- createdAt: persisted?.createdAt ?? live.createdAt,
2012
+ createdAt: persisted?.createdAt ?? live?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
2034
2013
  // kiro's file and our own activity move separately.
2035
- updatedAt: live ? newerOf(persisted?.updatedAt, live.updatedAt) : persisted.updatedAt,
2014
+ updatedAt: live ? newerOf(persisted?.updatedAt, live.updatedAt) : persisted?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
2036
2015
  liveness: live?.proc ? "live" : "dormant",
2037
2016
  agentId: live?.agentId ?? persisted?.agentId,
2038
2017
  modelId: live?.modelId ?? persisted?.modelId,
@@ -2042,49 +2021,60 @@ ${tail}` : err.message;
2042
2021
  contextUsagePercentage: snap?.contextUsagePercentage || persisted?.contextUsagePercentage
2043
2022
  };
2044
2023
  }
2045
- async listSessions() {
2046
- const persisted = await listPersistedSessions(this.log);
2047
- const files = new Map(persisted.map((p) => [p.sessionId, p]));
2048
- const byId = /* @__PURE__ */ new Map();
2049
- for (const p of persisted) {
2050
- byId.set(p.sessionId, this.summaryOf(void 0, p));
2051
- }
2052
- for (const s of this.sessions.values()) {
2053
- if (isGhost(s, files.has(s.sessionId))) {
2054
- this.evict(s.sessionId);
2055
- continue;
2024
+ async listChats() {
2025
+ const rows = this.store.all();
2026
+ const files = await Promise.all(
2027
+ rows.map((r) => r.sessionId ? readPersistedSession(r.sessionId) : null)
2028
+ );
2029
+ const out = [];
2030
+ rows.forEach((row, i) => {
2031
+ const live = row.sessionId ? this.sessions.get(row.sessionId) : void 0;
2032
+ const persisted = files[i] ?? void 0;
2033
+ if (live && isGhost(live, !!persisted)) {
2034
+ this.evict(live.sessionId);
2035
+ return;
2056
2036
  }
2057
- byId.set(s.sessionId, this.summaryOf(s, files.get(s.sessionId)));
2058
- }
2059
- return [...byId.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
2060
- }
2061
- async getDetail(sessionId) {
2037
+ if (!persisted && !live) return;
2038
+ out.push(this.summaryOf(row, live, persisted));
2039
+ });
2040
+ return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
2041
+ }
2042
+ /** kiro's session id for a chat, or a throw if the chat is unknown. */
2043
+ sessionIdOf(chatId) {
2044
+ const sessionId = this.store.sessionIdForChat(chatId);
2045
+ if (!sessionId) throw new Error(`Unknown chat: ${chatId}`);
2046
+ return sessionId;
2047
+ }
2048
+ async getDetail(chatId) {
2049
+ const chat = this.store.get(chatId);
2050
+ if (!chat) throw new Error(`Unknown chat: ${chatId}`);
2051
+ const sessionId = chat.sessionId;
2062
2052
  const [transcript, persisted] = await Promise.all([
2063
- hydrateTranscript(sessionId, this.store.attachmentsBySession(sessionId)),
2064
- readPersistedSession(sessionId)
2053
+ sessionId ? hydrateTranscript(sessionId, this.store.attachmentsByChat(chatId)) : Promise.resolve([]),
2054
+ sessionId ? readPersistedSession(sessionId) : Promise.resolve(null)
2065
2055
  ]);
2066
- const s = this.sessions.get(sessionId);
2067
- if (!s && !persisted) throw new Error(`Unknown session: ${sessionId}`);
2068
- return this.buildDetail(s, transcript, persisted ?? void 0);
2056
+ const s = sessionId ? this.sessions.get(sessionId) : void 0;
2057
+ if (!s && !persisted) throw new Error(`Unknown chat: ${chatId}`);
2058
+ return this.buildDetail(chat, s, transcript, persisted ?? void 0);
2069
2059
  }
2070
2060
  /**
2071
2061
  * A slice of the transcript for lazy "load older on scroll up". Re-hydrates
2072
2062
  * from disk (fast: ~150ms even for a multi-MB session) and returns the
2073
2063
  * requested window. offset/limit are clamped to the transcript bounds.
2074
2064
  */
2075
- async getTranscriptPage(sessionId, offset, limit) {
2065
+ async getTranscriptPage(chatId, offset, limit) {
2076
2066
  const transcript = await hydrateTranscript(
2077
- sessionId,
2078
- this.store.attachmentsBySession(sessionId)
2067
+ this.sessionIdOf(chatId),
2068
+ this.store.attachmentsByChat(chatId)
2079
2069
  );
2080
2070
  const start = Math.max(0, Math.min(offset, transcript.length));
2081
2071
  const end = Math.max(start, Math.min(start + limit, transcript.length));
2082
2072
  return transcript.slice(start, end);
2083
2073
  }
2084
2074
  /** One projection, so a dormant session and a live one cannot disagree. */
2085
- buildDetail(s, transcript, persisted) {
2075
+ buildDetail(chat, s, transcript, persisted) {
2086
2076
  return {
2087
- summary: this.summaryOf(s, persisted, transcript),
2077
+ summary: this.summaryOf(chat, s, persisted, transcript),
2088
2078
  modes: s?.availableModes ?? [],
2089
2079
  currentModeId: s?.currentModeId ?? persisted?.agentId,
2090
2080
  // Only the tail is sent on load; replayHead/title use the full transcript.
@@ -2138,16 +2128,18 @@ ${tail}` : err.message;
2138
2128
  }
2139
2129
  // Permanently delete a session: evict it from memory, remove its on-disk
2140
2130
  // files, and drop any title override.
2141
- async deleteSession(sessionId) {
2142
- const s = this.sessions.get(sessionId);
2131
+ async deleteChat(chatId) {
2132
+ const sessionId = this.store.sessionIdForChat(chatId);
2133
+ const s = sessionId ? this.sessions.get(sessionId) : void 0;
2143
2134
  if (s?.proc) {
2144
2135
  await s.proc.disposeAndWait().catch(() => {
2145
2136
  });
2146
2137
  s.proc = void 0;
2147
2138
  s.running = false;
2148
2139
  }
2140
+ this.store.remove(chatId);
2141
+ if (!sessionId) return;
2149
2142
  this.evict(sessionId);
2150
- this.store.remove(sessionId);
2151
2143
  await deletePersistedSession(sessionId);
2152
2144
  setTimeout(() => void deletePersistedSession(sessionId).catch(() => {
2153
2145
  }), 2500).unref?.();
@@ -2424,12 +2416,12 @@ function replyWith(reply, failure) {
2424
2416
  reply.code(failure.status);
2425
2417
  return { error: failure.error };
2426
2418
  }
2427
- async function resolveSessionPath(sessions, sessionId, requestedPath, require2) {
2419
+ async function resolveChatPath(sessions, sessionId, requestedPath, require2) {
2428
2420
  let cwd;
2429
2421
  try {
2430
- cwd = await sessions.getSessionCwd(sessionId);
2422
+ cwd = await sessions.getChatCwd(sessionId);
2431
2423
  } catch {
2432
- return { ok: false, status: 404, error: "Session not found" };
2424
+ return { ok: false, status: 404, error: "Chat not found" };
2433
2425
  }
2434
2426
  const relative = (requestedPath ?? "").replace(/^\/+/, "");
2435
2427
  if (require2 === "file" && !relative) {
@@ -2817,15 +2809,15 @@ var init_fs = __esm({
2817
2809
  }
2818
2810
  });
2819
2811
 
2820
- // server/src/routes/sessions.ts
2821
- function registerSessionRoutes(app, manager) {
2822
- app.get("/api/sessions", async () => {
2823
- return { sessions: await manager.listSessions() };
2812
+ // server/src/routes/chats.ts
2813
+ function registerChatRoutes(app, manager) {
2814
+ app.get("/api/chats", async () => {
2815
+ return { chats: await manager.listChats() };
2824
2816
  });
2825
- app.post("/api/sessions", async (req, reply) => {
2817
+ app.post("/api/chats", async (req, reply) => {
2826
2818
  const body = req.body ?? {};
2827
2819
  try {
2828
- return await manager.createSession({
2820
+ return await manager.createChat({
2829
2821
  cwd: body.cwd,
2830
2822
  agentId: body.agentId,
2831
2823
  modelId: body.modelId,
@@ -2841,7 +2833,7 @@ function registerSessionRoutes(app, manager) {
2841
2833
  return { error: err.message };
2842
2834
  }
2843
2835
  });
2844
- app.get("/api/sessions/:id", async (req, reply) => {
2836
+ app.get("/api/chats/:id", async (req, reply) => {
2845
2837
  try {
2846
2838
  return await manager.getDetail(req.params.id);
2847
2839
  } catch (err) {
@@ -2850,7 +2842,7 @@ function registerSessionRoutes(app, manager) {
2850
2842
  }
2851
2843
  });
2852
2844
  app.get(
2853
- "/api/sessions/:id/transcript",
2845
+ "/api/chats/:id/transcript",
2854
2846
  async (req, reply) => {
2855
2847
  const offset = Number.parseInt(req.query.offset ?? "", 10);
2856
2848
  const limit = Number.parseInt(req.query.limit ?? "", 10);
@@ -2867,7 +2859,7 @@ function registerSessionRoutes(app, manager) {
2867
2859
  }
2868
2860
  );
2869
2861
  app.post(
2870
- "/api/sessions/:id/prompt",
2862
+ "/api/chats/:id/prompt",
2871
2863
  async (req, reply) => {
2872
2864
  try {
2873
2865
  await manager.runPrompt(req.params.id, req.body.prompt, req.body.attachments);
@@ -2878,33 +2870,33 @@ function registerSessionRoutes(app, manager) {
2878
2870
  }
2879
2871
  }
2880
2872
  );
2881
- app.post("/api/sessions/:id/cancel", async (req) => {
2873
+ app.post("/api/chats/:id/cancel", async (req) => {
2882
2874
  manager.cancel(req.params.id);
2883
2875
  return { ok: true };
2884
2876
  });
2885
2877
  app.post(
2886
- "/api/sessions/:id/model",
2878
+ "/api/chats/:id/model",
2887
2879
  async (req) => {
2888
2880
  await manager.setModel(req.params.id, req.body.modelId);
2889
2881
  return { ok: true };
2890
2882
  }
2891
2883
  );
2892
2884
  app.post(
2893
- "/api/sessions/:id/mode",
2885
+ "/api/chats/:id/mode",
2894
2886
  async (req) => {
2895
2887
  await manager.setMode(req.params.id, req.body.modeId);
2896
2888
  return { ok: true };
2897
2889
  }
2898
2890
  );
2899
2891
  app.post(
2900
- "/api/sessions/:id/rename",
2892
+ "/api/chats/:id/rename",
2901
2893
  async (req) => {
2902
- manager.renameSession(req.params.id, req.body.title);
2894
+ manager.renameChat(req.params.id, req.body.title);
2903
2895
  return { ok: true };
2904
2896
  }
2905
2897
  );
2906
2898
  app.post(
2907
- "/api/sessions/:id/cwd",
2899
+ "/api/chats/:id/cwd",
2908
2900
  async (req, reply) => {
2909
2901
  const cwd = (req.body?.cwd ?? "").trim();
2910
2902
  if (!cwd) {
@@ -2912,28 +2904,28 @@ function registerSessionRoutes(app, manager) {
2912
2904
  return { error: "cwd is required" };
2913
2905
  }
2914
2906
  try {
2915
- return { ok: true, cwd: await manager.setSessionCwd(req.params.id, cwd) };
2907
+ return { ok: true, cwd: await manager.setChatCwd(req.params.id, cwd) };
2916
2908
  } catch (err) {
2917
2909
  reply.code(400);
2918
2910
  return { error: err.message };
2919
2911
  }
2920
2912
  }
2921
2913
  );
2922
- app.post("/api/sessions/:id/reload", async (req, reply) => {
2914
+ app.post("/api/chats/:id/reload", async (req, reply) => {
2923
2915
  try {
2924
- return await manager.reloadSession(req.params.id);
2916
+ return await manager.reloadChat(req.params.id);
2925
2917
  } catch (err) {
2926
2918
  reply.code(400);
2927
2919
  return { error: err.message };
2928
2920
  }
2929
2921
  });
2930
- app.delete("/api/sessions/:id", async (req) => {
2931
- await manager.deleteSession(req.params.id);
2922
+ app.delete("/api/chats/:id", async (req) => {
2923
+ await manager.deleteChat(req.params.id);
2932
2924
  return { ok: true };
2933
2925
  });
2934
2926
  }
2935
- var init_sessions = __esm({
2936
- "server/src/routes/sessions.ts"() {
2927
+ var init_chats2 = __esm({
2928
+ "server/src/routes/chats.ts"() {
2937
2929
  "use strict";
2938
2930
  }
2939
2931
  });
@@ -2944,9 +2936,9 @@ import { createReadStream as createReadStream3 } from "node:fs";
2944
2936
  import path16 from "node:path";
2945
2937
  function registerWorkspaceRoutes(app, manager) {
2946
2938
  app.get(
2947
- "/api/sessions/:id/tree",
2939
+ "/api/chats/:id/tree",
2948
2940
  async (req, reply) => {
2949
- const resolved = await resolveSessionPath(
2941
+ const resolved = await resolveChatPath(
2950
2942
  manager,
2951
2943
  req.params.id,
2952
2944
  req.query.path,
@@ -2990,9 +2982,9 @@ function registerWorkspaceRoutes(app, manager) {
2990
2982
  }
2991
2983
  );
2992
2984
  app.get(
2993
- "/api/sessions/:id/download",
2985
+ "/api/chats/:id/download",
2994
2986
  async (req, reply) => {
2995
- const resolved = await resolveSessionPath(manager, req.params.id, req.query.path, "file");
2987
+ const resolved = await resolveChatPath(manager, req.params.id, req.query.path, "file");
2996
2988
  if (!resolved.ok) return replyWith(reply, resolved);
2997
2989
  const { real: realTarget, stat } = resolved;
2998
2990
  if (stat.size > MAX_DOWNLOAD_BYTES) {
@@ -3012,9 +3004,9 @@ function registerWorkspaceRoutes(app, manager) {
3012
3004
  }
3013
3005
  );
3014
3006
  app.get(
3015
- "/api/sessions/:id/preview",
3007
+ "/api/chats/:id/preview",
3016
3008
  async (req, reply) => {
3017
- const resolved = await resolveSessionPath(manager, req.params.id, req.query.path, "file");
3009
+ const resolved = await resolveChatPath(manager, req.params.id, req.query.path, "file");
3018
3010
  if (!resolved.ok) return replyWith(reply, resolved);
3019
3011
  const { real: realTarget, stat } = resolved;
3020
3012
  return sendFilePreview(req, reply, realTarget, stat);
@@ -3259,7 +3251,7 @@ var init_dirWatchers = __esm({
3259
3251
  function send(socket, msg) {
3260
3252
  if (socket.readyState === socket.OPEN) socket.send(JSON.stringify(msg));
3261
3253
  }
3262
- function handleConnection(socket, manager, sessionId, startCursor) {
3254
+ function handleConnection(socket, manager, chatId, startCursor) {
3263
3255
  let cursor = startCursor;
3264
3256
  let unsubscribe = null;
3265
3257
  let alive = true;
@@ -3267,7 +3259,7 @@ function handleConnection(socket, manager, sessionId, startCursor) {
3267
3259
  const watchers = createDirWatchers({
3268
3260
  resolve: async (relative) => {
3269
3261
  try {
3270
- return confineToRoot(await manager.getSessionCwd(sessionId), relative);
3262
+ return confineToRoot(await manager.getChatCwd(chatId), relative);
3271
3263
  } catch {
3272
3264
  return null;
3273
3265
  }
@@ -3281,13 +3273,13 @@ function handleConnection(socket, manager, sessionId, startCursor) {
3281
3273
  };
3282
3274
  const attach = async () => {
3283
3275
  try {
3284
- await manager.ensureOpen(sessionId);
3276
+ await manager.ensureOpen(chatId);
3285
3277
  } catch (err) {
3286
3278
  send(socket, { type: "error", message: err.message });
3287
3279
  socket.close(1011, "open failed");
3288
3280
  return;
3289
3281
  }
3290
- const store = manager.getStore(sessionId);
3282
+ const store = manager.getStore(chatId);
3291
3283
  if (!store) {
3292
3284
  send(socket, { type: "error", message: "Session store unavailable" });
3293
3285
  socket.close(1011, "no store");
@@ -3304,7 +3296,7 @@ function handleConnection(socket, manager, sessionId, startCursor) {
3304
3296
  for (const e of events) forward(e);
3305
3297
  }
3306
3298
  send(socket, { type: "replay_complete", head: store.head() });
3307
- unsubscribe = manager.onEvent(sessionId, forward);
3299
+ unsubscribe = manager.onEvent(chatId, forward);
3308
3300
  ready = true;
3309
3301
  };
3310
3302
  void attach();
@@ -3323,17 +3315,17 @@ function handleConnection(socket, manager, sessionId, startCursor) {
3323
3315
  case "watch_paths":
3324
3316
  return watchers.sync(msg.paths);
3325
3317
  case "prompt":
3326
- return ack("prompt", () => manager.runPrompt(sessionId, msg.content, msg.attachments));
3318
+ return ack("prompt", () => manager.runPrompt(chatId, msg.content, msg.attachments));
3327
3319
  case "cancel":
3328
- return ack("cancel", () => manager.cancel(sessionId));
3320
+ return ack("cancel", () => manager.cancel(chatId));
3329
3321
  case "set_mode":
3330
- return ack("set_mode", () => manager.setMode(sessionId, msg.modeId));
3322
+ return ack("set_mode", () => manager.setMode(chatId, msg.modeId));
3331
3323
  case "set_model":
3332
- return ack("set_model", () => manager.setModel(sessionId, msg.modelId));
3324
+ return ack("set_model", () => manager.setModel(chatId, msg.modelId));
3333
3325
  case "exec_command":
3334
3326
  return ack(
3335
3327
  "exec_command",
3336
- () => manager.execCommand(sessionId, msg.command)
3328
+ () => manager.execCommand(chatId, msg.command)
3337
3329
  );
3338
3330
  default:
3339
3331
  return send(socket, { type: "error", message: "Unknown message type" });
@@ -3380,15 +3372,15 @@ function registerWsGateway(app, manager) {
3380
3372
  socket.close(1008, "Unauthorized");
3381
3373
  return;
3382
3374
  }
3383
- if (!query.sessionId) {
3384
- send(socket, { type: "error", message: "Missing sessionId" });
3385
- socket.close(1008, "Missing sessionId");
3375
+ if (!query.chatId) {
3376
+ send(socket, { type: "error", message: "Missing chatId" });
3377
+ socket.close(1008, "Missing chatId");
3386
3378
  return;
3387
3379
  }
3388
3380
  handleConnection(
3389
3381
  socket,
3390
3382
  manager,
3391
- query.sessionId,
3383
+ query.chatId,
3392
3384
  Number.parseInt(query.cursor ?? "0", 10) || 0
3393
3385
  );
3394
3386
  });
@@ -3437,7 +3429,7 @@ async function buildApp() {
3437
3429
  registerModelRoutes(app);
3438
3430
  registerAgentRoutes(app);
3439
3431
  registerFsRoutes(app);
3440
- registerSessionRoutes(app, manager);
3432
+ registerChatRoutes(app, manager);
3441
3433
  registerWorkspaceRoutes(app, manager);
3442
3434
  registerUploadRoutes(app);
3443
3435
  registerWsGateway(app, manager);
@@ -3473,7 +3465,7 @@ var init_app = __esm({
3473
3465
  init_models2();
3474
3466
  init_agents2();
3475
3467
  init_fs();
3476
- init_sessions();
3468
+ init_chats2();
3477
3469
  init_workspace();
3478
3470
  init_uploads();
3479
3471
  init_health();
@@ -4104,7 +4096,7 @@ var init_server2 = __esm({
4104
4096
  "server/src/mcp/server.ts"() {
4105
4097
  "use strict";
4106
4098
  init_protocol();
4107
- VERSION = true ? "0.12.0" : "dev";
4099
+ VERSION = true ? "0.13.0" : "dev";
4108
4100
  }
4109
4101
  });
4110
4102
 
@@ -7484,7 +7476,7 @@ var program = new Command();
7484
7476
  // server/src/cli/program.ts
7485
7477
  init_paths();
7486
7478
  init_settings();
7487
- var VERSION2 = true ? "0.12.0" : "dev";
7479
+ var VERSION2 = true ? "0.13.0" : "dev";
7488
7480
  async function bootstrap() {
7489
7481
  const file = configFilePath();
7490
7482
  const settings = readSettings(file);