@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.
- package/dist/mcp.js +1 -1
- package/dist/server.js +212 -220
- package/dist/web/assets/{abnfDiagram-N423BO3Z-4Ko3ozfJ.js → abnfDiagram-N423BO3Z-Ckdr5R00.js} +1 -1
- package/dist/web/assets/{arc-Ch7Ehjbn.js → arc-RD0bGtxd.js} +1 -1
- package/dist/web/assets/{architectureDiagram-T3A2C74G-DcWU5yRD.js → architectureDiagram-T3A2C74G-CHkgCulK.js} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-BUzWG1_k.js → blockDiagram-VBNYF7ZC-bs7XxVhZ.js} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-Cbk3RgdU.js → c4Diagram-5PPSVZJV-sipmg1A6.js} +1 -1
- package/dist/web/assets/channel-D8Rxtl4g.js +1 -0
- package/dist/web/assets/{chunk-2GRJ4B5K-B0WXdW3m.js → chunk-2GRJ4B5K-Dk5uhiWq.js} +1 -1
- package/dist/web/assets/{chunk-2Q5K7J3B-Dr5AcgBH.js → chunk-2Q5K7J3B-RLD9D6CE.js} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-NRZvLElM.js → chunk-5RXB4S5H-Bisltr8Y.js} +1 -1
- package/dist/web/assets/{chunk-5VM5RSS4-B2ezMICZ.js → chunk-5VM5RSS4-BFuA55Yw.js} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-0_kTL1pm.js → chunk-6Q2QTUOP-Dgi0Ckwy.js} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-ChJJ-3FT.js → chunk-GF5L2VYU-sLBx1TNb.js} +1 -1
- package/dist/web/assets/{chunk-JWPE2WC7-B15-wRYl.js → chunk-JWPE2WC7-BoIB6XgD.js} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-DsR7v9qB.js → chunk-KBJHAD2P-DE2z1pFd.js} +1 -1
- package/dist/web/assets/{chunk-RYQCIY6F-BkT8T354.js → chunk-RYQCIY6F-S_juj3_n.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-CRB4wwou.js → chunk-XXDRQBXY-gCHzWtzi.js} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-Ch31vHtu.js +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-Ch31vHtu.js +1 -0
- package/dist/web/assets/{cose-bilkent-JH36ORCC-BESlNRq3.js → cose-bilkent-JH36ORCC-Dp9lkOsx.js} +1 -1
- package/dist/web/assets/{cynefin-VYW2F7L2-BPfoy8I9.js → cynefin-VYW2F7L2-OwRF9q4d.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-Byn6JJg0.js → cynefinDiagram-MW4NZA55-DQsh1pP_.js} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-CF6UO64P.js → dagre-VZM6K2ZE-BOwA_-xr.js} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-DgIzI-sU.js → diagram-7IWD3JNH-DCcvWrT9.js} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-DTXfP5om.js → diagram-B4RE2ZJO-1TUDDf93.js} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-CTLNzser.js → diagram-LBJQPF4R-CQ9CHasC.js} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-M38db-G2.js → diagram-Q27KOJAE-CnzspMbf.js} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-CG-vZyFB.js → diagram-UB23O5K3-DwSm7ENR.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-CsJ9VNb2.js → ebnfDiagram-BXEA7PRR-B88O4qYG.js} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-3dK0h9cf.js → erDiagram-JOGREHBK-RVTjJMdw.js} +1 -1
- package/dist/web/assets/{flowDiagram-UKHOOZJN-DHTGEHfr.js → flowDiagram-UKHOOZJN-CAGMFzJU.js} +1 -1
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-DiLysF1Q.js → ganttDiagram-PKOTCBZU-CEvl6Pim.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-2uthJlH6.js → gitGraphDiagram-DS77QQ5N-CH6kGWAl.js} +1 -1
- package/dist/web/assets/{index-DbaSy_a8.js → index-B1jqxtEG.js} +1 -1
- package/dist/web/assets/index-Bm1zLB-5.js +249 -0
- package/dist/web/assets/index-bZk3Q_Yv.css +1 -0
- package/dist/web/assets/{infoDiagram-6WML65LV-B35ot4_O.js → infoDiagram-6WML65LV-DAcfWiSl.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-C6nTJ8A6.js → ishikawaDiagram-WSZJBQD7-C0CNHY8o.js} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-Bmt5Dt3Z.js → journeyDiagram-NVQOT4AX-Dkf1b7P5.js} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DkGk9WrO.js → kanban-definition-27J2QSJJ-DEyWowZE.js} +1 -1
- package/dist/web/assets/{linear-D9RsKviS.js → linear-BIoTAhlf.js} +1 -1
- package/dist/web/assets/{mermaid.core-BkEkrWDL.js → mermaid.core-DTmrl8Wn.js} +4 -4
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-7XkMFJ0C.js → mindmap-definition-FAOFIHXS-mw_tNbPO.js} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-KpO4Zp9v.js → pegDiagram-VL7TDLO6-BhX4qWsU.js} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-CnZkxeeK.js → pieDiagram-7S7Q4E2Y-D8y4mlrR.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-Jej_KG8V.js → quadrantDiagram-CIZ2JOQS-eRi0JVMP.js} +1 -1
- package/dist/web/assets/{railroadDiagram-AXF67PYL-BfteVM0C.js → railroadDiagram-AXF67PYL-BtWC75OM.js} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-BTSeDpTM.js → requirementDiagram-LRYGKXZP-QqWK5Q2W.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-CeB7UW0v.js → sankeyDiagram-W5VNT64P-DWPZuz_o.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-BeOLM9UY.js → sequenceDiagram-SI44F4Z6-BjtTZ0wA.js} +1 -1
- package/dist/web/assets/{sizeCapture-X5ZJPWSS-7aezJBJp.js → sizeCapture-X5ZJPWSS-CHpioWdU.js} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-DmJkX_vQ.js → stateDiagram-OKZ733FA-CUyQMj6M.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-BStIRqFz.js +1 -0
- package/dist/web/assets/{swimlanes-SLNWSIFB-CmnhwW2u.js → swimlanes-SLNWSIFB-DDbnLkK8.js} +2 -2
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-D-e3uCkk.js +8 -0
- package/dist/web/assets/{timeline-definition-Z64GVDOM-DtxMhUPD.js → timeline-definition-Z64GVDOM-Cxc91WrS.js} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-DO34MvMM.js → vennDiagram-T6HMQDX7-CjOhqkxb.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-s2zhjdd1.js → wardleyDiagram-T6FBY63Y-4Ag9jAUE.js} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-CnCQs0NK.js → xychartDiagram-ELKLHX3M-BbQy0Bji.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/web/assets/channel-DSuKNwf1.js +0 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-DY_Arw9F.js +0 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-DY_Arw9F.js +0 -1
- package/dist/web/assets/index-D3zBm-7r.js +0 -249
- package/dist/web/assets/index-D_ANHT9G.css +0 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CkEEUkE9.js +0 -1
- 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
|
|
395
|
-
|
|
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
|
-
|
|
412
|
-
ordinal
|
|
413
|
-
path
|
|
414
|
-
name
|
|
415
|
-
size
|
|
416
|
-
kind
|
|
417
|
-
PRIMARY KEY (
|
|
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 &&
|
|
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
|
-
|
|
1193
|
+
chatId;
|
|
1196
1194
|
buffer = [];
|
|
1197
1195
|
capacity;
|
|
1198
1196
|
seq = 0;
|
|
1199
|
-
constructor(
|
|
1197
|
+
constructor(chatId) {
|
|
1200
1198
|
super();
|
|
1201
|
-
this.
|
|
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
|
-
|
|
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/
|
|
1486
|
-
var
|
|
1487
|
-
var
|
|
1488
|
-
"server/src/session/
|
|
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
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
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
|
-
|
|
1496
|
-
|
|
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
|
-
|
|
1499
|
-
|
|
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
|
-
*
|
|
1506
|
-
*
|
|
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
|
-
|
|
1510
|
-
|
|
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
|
-
|
|
1513
|
-
this.
|
|
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(
|
|
1499
|
+
setAttachments(chatId, ordinal, files) {
|
|
1517
1500
|
if (files.length === 0) return;
|
|
1518
1501
|
const stmt = db().prepare(
|
|
1519
|
-
`INSERT INTO message_attachments (
|
|
1502
|
+
`INSERT INTO message_attachments (chat_id, ordinal, path, name, size, kind)
|
|
1520
1503
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
1521
|
-
ON CONFLICT(
|
|
1504
|
+
ON CONFLICT(chat_id, ordinal, path) DO NOTHING`
|
|
1522
1505
|
);
|
|
1523
1506
|
for (const f of files) {
|
|
1524
|
-
stmt.run(
|
|
1507
|
+
stmt.run(chatId, ordinal, f.path, f.name, f.size, f.kind);
|
|
1525
1508
|
}
|
|
1526
1509
|
}
|
|
1527
|
-
/** Every attachment in a
|
|
1528
|
-
|
|
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
|
|
1532
|
-
).all(
|
|
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
|
-
|
|
1547
|
-
|
|
1548
|
-
db().prepare("DELETE FROM
|
|
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(
|
|
1552
|
-
const row = db().prepare(`SELECT ${column} AS value FROM
|
|
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(
|
|
1537
|
+
write(chatId, column, value) {
|
|
1556
1538
|
db().prepare(
|
|
1557
|
-
`INSERT INTO
|
|
1558
|
-
ON CONFLICT(
|
|
1559
|
-
).run(
|
|
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
|
-
|
|
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
|
|
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(
|
|
1674
|
+
titleOf(chat, parts) {
|
|
1695
1675
|
return resolveSessionTitle({
|
|
1696
|
-
override:
|
|
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
|
-
|
|
1703
|
-
renameSession(sessionId, title) {
|
|
1682
|
+
renameChat(chatId, title) {
|
|
1704
1683
|
const clean = sanitizeTitle(title);
|
|
1705
|
-
this.store.setTitle(
|
|
1706
|
-
const s = this.sessions.get(
|
|
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
|
|
1692
|
+
async setChatCwd(chatId, input) {
|
|
1714
1693
|
const resolved = resolveCwd(input);
|
|
1715
|
-
const s = await this.ensureOpen(
|
|
1716
|
-
this.store.setCwd(
|
|
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({
|
|
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
|
|
1735
|
-
const s = await this.ensureOpen(
|
|
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(
|
|
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(
|
|
1794
|
-
const s = this.sessions.get(
|
|
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(
|
|
1800
|
-
return this.sessions.get(
|
|
1778
|
+
getStore(chatId) {
|
|
1779
|
+
return this.sessions.get(this.store.sessionIdForChat(chatId) ?? "")?.store;
|
|
1801
1780
|
}
|
|
1802
|
-
/**
|
|
1803
|
-
async
|
|
1804
|
-
const s = await this.ensureOpen(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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.
|
|
1932
|
-
|
|
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(
|
|
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(
|
|
1944
|
-
const s = await this.ensureOpen(
|
|
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(
|
|
1936
|
+
if (!this.store.getTitle(chatId)) {
|
|
1957
1937
|
const title = titleFromPrompt(content);
|
|
1958
1938
|
if (title) {
|
|
1959
|
-
this.store.setTitle(
|
|
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(
|
|
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(
|
|
1984
|
-
const s = this.sessions.get(
|
|
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(
|
|
1988
|
-
const s = await this.ensureOpen(
|
|
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(
|
|
1997
|
-
const s = await this.ensureOpen(
|
|
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(
|
|
2005
|
-
const s = await this.ensureOpen(
|
|
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
|
|
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 ??
|
|
2001
|
+
const cwd = live?.cwd ?? chat.cwd ?? persisted?.cwd ?? config.defaultCwd;
|
|
2023
2002
|
return {
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
title: this.titleOf(
|
|
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.
|
|
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.
|
|
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
|
|
2046
|
-
const
|
|
2047
|
-
const files =
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
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
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
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.
|
|
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
|
|
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(
|
|
2065
|
+
async getTranscriptPage(chatId, offset, limit) {
|
|
2076
2066
|
const transcript = await hydrateTranscript(
|
|
2077
|
-
|
|
2078
|
-
this.store.
|
|
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
|
|
2142
|
-
const
|
|
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
|
|
2419
|
+
async function resolveChatPath(sessions, sessionId, requestedPath, require2) {
|
|
2428
2420
|
let cwd;
|
|
2429
2421
|
try {
|
|
2430
|
-
cwd = await sessions.
|
|
2422
|
+
cwd = await sessions.getChatCwd(sessionId);
|
|
2431
2423
|
} catch {
|
|
2432
|
-
return { ok: false, status: 404, error: "
|
|
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/
|
|
2821
|
-
function
|
|
2822
|
-
app.get("/api/
|
|
2823
|
-
return {
|
|
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/
|
|
2817
|
+
app.post("/api/chats", async (req, reply) => {
|
|
2826
2818
|
const body = req.body ?? {};
|
|
2827
2819
|
try {
|
|
2828
|
-
return await manager.
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
2892
|
+
"/api/chats/:id/rename",
|
|
2901
2893
|
async (req) => {
|
|
2902
|
-
manager.
|
|
2894
|
+
manager.renameChat(req.params.id, req.body.title);
|
|
2903
2895
|
return { ok: true };
|
|
2904
2896
|
}
|
|
2905
2897
|
);
|
|
2906
2898
|
app.post(
|
|
2907
|
-
"/api/
|
|
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.
|
|
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/
|
|
2914
|
+
app.post("/api/chats/:id/reload", async (req, reply) => {
|
|
2923
2915
|
try {
|
|
2924
|
-
return await manager.
|
|
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/
|
|
2931
|
-
await manager.
|
|
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
|
|
2936
|
-
"server/src/routes/
|
|
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/
|
|
2939
|
+
"/api/chats/:id/tree",
|
|
2948
2940
|
async (req, reply) => {
|
|
2949
|
-
const resolved = await
|
|
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/
|
|
2985
|
+
"/api/chats/:id/download",
|
|
2994
2986
|
async (req, reply) => {
|
|
2995
|
-
const resolved = await
|
|
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/
|
|
3007
|
+
"/api/chats/:id/preview",
|
|
3016
3008
|
async (req, reply) => {
|
|
3017
|
-
const resolved = await
|
|
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,
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
3318
|
+
return ack("prompt", () => manager.runPrompt(chatId, msg.content, msg.attachments));
|
|
3327
3319
|
case "cancel":
|
|
3328
|
-
return ack("cancel", () => manager.cancel(
|
|
3320
|
+
return ack("cancel", () => manager.cancel(chatId));
|
|
3329
3321
|
case "set_mode":
|
|
3330
|
-
return ack("set_mode", () => manager.setMode(
|
|
3322
|
+
return ack("set_mode", () => manager.setMode(chatId, msg.modeId));
|
|
3331
3323
|
case "set_model":
|
|
3332
|
-
return ack("set_model", () => manager.setModel(
|
|
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(
|
|
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.
|
|
3384
|
-
send(socket, { type: "error", message: "Missing
|
|
3385
|
-
socket.close(1008, "Missing
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
7479
|
+
var VERSION2 = true ? "0.13.0" : "dev";
|
|
7488
7480
|
async function bootstrap() {
|
|
7489
7481
|
const file = configFilePath();
|
|
7490
7482
|
const settings = readSettings(file);
|