@joeyshi12/casper 0.12.0 → 0.13.1
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 +258 -259
- 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
|
}
|
|
@@ -799,55 +797,24 @@ var init_agents = __esm({
|
|
|
799
797
|
}
|
|
800
798
|
});
|
|
801
799
|
|
|
802
|
-
// server/src/session/chats.ts
|
|
803
|
-
import fs7 from "node:fs";
|
|
804
|
-
import path8 from "node:path";
|
|
805
|
-
function chatsRoot() {
|
|
806
|
-
return path8.join(config.casperDataDir, "chats");
|
|
807
|
-
}
|
|
808
|
-
function chatDir(chatId) {
|
|
809
|
-
return path8.join(chatsRoot(), chatId);
|
|
810
|
-
}
|
|
811
|
-
function chatUploadsDir(chatId) {
|
|
812
|
-
return path8.join(chatDir(chatId), "uploads");
|
|
813
|
-
}
|
|
814
|
-
function chatWorkspaceDir(chatId) {
|
|
815
|
-
return path8.join(chatDir(chatId), "workspace");
|
|
816
|
-
}
|
|
817
|
-
function createChatWorkspace(chatId) {
|
|
818
|
-
const dir = chatWorkspaceDir(chatId);
|
|
819
|
-
fs7.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
820
|
-
return dir;
|
|
821
|
-
}
|
|
822
|
-
function isManagedWorkspace(dir) {
|
|
823
|
-
const root = chatsRoot();
|
|
824
|
-
return dir === root || dir.startsWith(root + path8.sep);
|
|
825
|
-
}
|
|
826
|
-
var init_chats = __esm({
|
|
827
|
-
"server/src/session/chats.ts"() {
|
|
828
|
-
"use strict";
|
|
829
|
-
init_config();
|
|
830
|
-
}
|
|
831
|
-
});
|
|
832
|
-
|
|
833
800
|
// server/src/util/paths.ts
|
|
834
|
-
import
|
|
801
|
+
import fs7 from "node:fs";
|
|
835
802
|
import fsp from "node:fs/promises";
|
|
836
|
-
import
|
|
803
|
+
import path8 from "node:path";
|
|
837
804
|
function isWithinRoot(root, target) {
|
|
838
|
-
const resolved =
|
|
839
|
-
if (root ===
|
|
840
|
-
return resolved === root || resolved.startsWith(root +
|
|
805
|
+
const resolved = path8.resolve(target);
|
|
806
|
+
if (root === path8.sep) return true;
|
|
807
|
+
return resolved === root || resolved.startsWith(root + path8.sep);
|
|
841
808
|
}
|
|
842
809
|
function confineToRoot(root, input) {
|
|
843
|
-
const resolved =
|
|
810
|
+
const resolved = path8.resolve(root, input);
|
|
844
811
|
return isWithinRoot(root, resolved) ? resolved : null;
|
|
845
812
|
}
|
|
846
813
|
function resolveRealRoot(root) {
|
|
847
814
|
if (realRootCache && realRootCache.root === root) return realRootCache.real;
|
|
848
815
|
let real;
|
|
849
816
|
try {
|
|
850
|
-
real =
|
|
817
|
+
real = fs7.realpathSync(root);
|
|
851
818
|
} catch {
|
|
852
819
|
real = root;
|
|
853
820
|
}
|
|
@@ -867,7 +834,7 @@ function isValidSessionId(id) {
|
|
|
867
834
|
return /^[A-Za-z0-9._-]+$/.test(id) && id !== "." && !id.includes("..");
|
|
868
835
|
}
|
|
869
836
|
function isValidChatId(id) {
|
|
870
|
-
return !!id &&
|
|
837
|
+
return !!id && isValidSessionId(id);
|
|
871
838
|
}
|
|
872
839
|
var realRootCache;
|
|
873
840
|
var init_paths2 = __esm({
|
|
@@ -877,6 +844,43 @@ var init_paths2 = __esm({
|
|
|
877
844
|
}
|
|
878
845
|
});
|
|
879
846
|
|
|
847
|
+
// server/src/session/chats.ts
|
|
848
|
+
import fs8 from "node:fs";
|
|
849
|
+
import fsp2 from "node:fs/promises";
|
|
850
|
+
import path9 from "node:path";
|
|
851
|
+
function chatsRoot() {
|
|
852
|
+
return path9.join(config.casperDataDir, "chats");
|
|
853
|
+
}
|
|
854
|
+
function chatDir(chatId) {
|
|
855
|
+
return path9.join(chatsRoot(), chatId);
|
|
856
|
+
}
|
|
857
|
+
function chatUploadsDir(chatId) {
|
|
858
|
+
return path9.join(chatDir(chatId), "uploads");
|
|
859
|
+
}
|
|
860
|
+
function chatWorkspaceDir(chatId) {
|
|
861
|
+
return path9.join(chatDir(chatId), "workspace");
|
|
862
|
+
}
|
|
863
|
+
function createChatWorkspace(chatId) {
|
|
864
|
+
const dir = chatWorkspaceDir(chatId);
|
|
865
|
+
fs8.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
866
|
+
return dir;
|
|
867
|
+
}
|
|
868
|
+
async function removeChatDir(chatId) {
|
|
869
|
+
if (!isValidChatId(chatId)) return;
|
|
870
|
+
await fsp2.rm(chatDir(chatId), { recursive: true, force: true });
|
|
871
|
+
}
|
|
872
|
+
function isManagedWorkspace(dir) {
|
|
873
|
+
const root = chatsRoot();
|
|
874
|
+
return dir === root || dir.startsWith(root + path9.sep);
|
|
875
|
+
}
|
|
876
|
+
var init_chats = __esm({
|
|
877
|
+
"server/src/session/chats.ts"() {
|
|
878
|
+
"use strict";
|
|
879
|
+
init_config();
|
|
880
|
+
init_paths2();
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
|
|
880
884
|
// server/src/acp/errors.ts
|
|
881
885
|
function describeError(error) {
|
|
882
886
|
const detail = detailText(error.data);
|
|
@@ -1192,13 +1196,13 @@ var init_EventStore = __esm({
|
|
|
1192
1196
|
"use strict";
|
|
1193
1197
|
init_config();
|
|
1194
1198
|
EventStore = class extends EventEmitter4 {
|
|
1195
|
-
|
|
1199
|
+
chatId;
|
|
1196
1200
|
buffer = [];
|
|
1197
1201
|
capacity;
|
|
1198
1202
|
seq = 0;
|
|
1199
|
-
constructor(
|
|
1203
|
+
constructor(chatId) {
|
|
1200
1204
|
super();
|
|
1201
|
-
this.
|
|
1205
|
+
this.chatId = chatId;
|
|
1202
1206
|
this.capacity = config.eventBufferSize;
|
|
1203
1207
|
}
|
|
1204
1208
|
/** Append an event, assign it the next seq, and fan it out. */
|
|
@@ -1207,7 +1211,7 @@ var init_EventStore = __esm({
|
|
|
1207
1211
|
const event = {
|
|
1208
1212
|
seq: this.seq,
|
|
1209
1213
|
ts: Date.now(),
|
|
1210
|
-
|
|
1214
|
+
chatId: this.chatId,
|
|
1211
1215
|
payload
|
|
1212
1216
|
};
|
|
1213
1217
|
this.buffer.push(event);
|
|
@@ -1325,43 +1329,16 @@ function summarize(j) {
|
|
|
1325
1329
|
const contextUsagePercentage = state?.rts_model_state?.context_usage_percentage ?? turns[turns.length - 1]?.context_usage_percentage;
|
|
1326
1330
|
return {
|
|
1327
1331
|
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
1332
|
// Left empty when kiro has not named it; resolveSessionTitle decides what to show.
|
|
1332
1333
|
title: j.title?.trim() ?? "",
|
|
1333
1334
|
cwd: j.cwd,
|
|
1334
1335
|
createdAt: j.created_at,
|
|
1335
1336
|
updatedAt: j.updated_at,
|
|
1336
|
-
liveness: "dormant",
|
|
1337
1337
|
agentId: state?.agent_name,
|
|
1338
1338
|
modelId: state?.rts_model_state?.model_info?.model_id,
|
|
1339
|
-
running: false,
|
|
1340
1339
|
contextUsagePercentage
|
|
1341
1340
|
};
|
|
1342
1341
|
}
|
|
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
1342
|
async function deletePersistedSession(sessionId) {
|
|
1366
1343
|
if (!isValidSessionId(sessionId)) return;
|
|
1367
1344
|
const targets = [
|
|
@@ -1482,54 +1459,66 @@ var init_kiroFiles = __esm({
|
|
|
1482
1459
|
}
|
|
1483
1460
|
});
|
|
1484
1461
|
|
|
1485
|
-
// server/src/session/
|
|
1486
|
-
var
|
|
1487
|
-
var
|
|
1488
|
-
"server/src/session/
|
|
1462
|
+
// server/src/session/chatStore.ts
|
|
1463
|
+
var ChatStore;
|
|
1464
|
+
var init_chatStore = __esm({
|
|
1465
|
+
"server/src/session/chatStore.ts"() {
|
|
1489
1466
|
"use strict";
|
|
1490
1467
|
init_db();
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
setTitle(sessionId, title) {
|
|
1496
|
-
this.write(sessionId, "title", title);
|
|
1468
|
+
ChatStore = class {
|
|
1469
|
+
/** Every chat. Ordering is kiro's business, applied once the file is joined on. */
|
|
1470
|
+
all() {
|
|
1471
|
+
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 }));
|
|
1497
1472
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1473
|
+
get(chatId) {
|
|
1474
|
+
const r = db().prepare("SELECT chat_id, session_id, title, cwd FROM chats WHERE chat_id = ?").get(chatId);
|
|
1475
|
+
return r ? { chatId: r.chat_id, sessionId: r.session_id, title: r.title, cwd: r.cwd } : void 0;
|
|
1500
1476
|
}
|
|
1501
|
-
|
|
1502
|
-
|
|
1477
|
+
create(chatId) {
|
|
1478
|
+
db().prepare("INSERT INTO chats (chat_id) VALUES (?) ON CONFLICT(chat_id) DO NOTHING").run(chatId);
|
|
1503
1479
|
}
|
|
1504
1480
|
/**
|
|
1505
|
-
*
|
|
1506
|
-
*
|
|
1507
|
-
* session, which gives it one stable directory rather than a new one per call.
|
|
1481
|
+
* kiro names the session once it starts one; bind it to the chat that owns it. A session
|
|
1482
|
+
* belongs to exactly one chat, so any earlier claim on it is released.
|
|
1508
1483
|
*/
|
|
1509
|
-
|
|
1510
|
-
|
|
1484
|
+
bindSession(chatId, sessionId) {
|
|
1485
|
+
db().prepare("UPDATE chats SET session_id = NULL WHERE session_id = ? AND chat_id <> ?").run(sessionId, chatId);
|
|
1486
|
+
this.write(chatId, "session_id", sessionId);
|
|
1487
|
+
}
|
|
1488
|
+
sessionIdForChat(chatId) {
|
|
1489
|
+
const row = db().prepare("SELECT session_id FROM chats WHERE chat_id = ?").get(chatId);
|
|
1490
|
+
return row?.session_id ?? void 0;
|
|
1491
|
+
}
|
|
1492
|
+
getTitle(chatId) {
|
|
1493
|
+
return this.read(chatId, "title");
|
|
1494
|
+
}
|
|
1495
|
+
setTitle(chatId, title) {
|
|
1496
|
+
this.write(chatId, "title", title);
|
|
1511
1497
|
}
|
|
1512
|
-
|
|
1513
|
-
this.
|
|
1498
|
+
getCwd(chatId) {
|
|
1499
|
+
return this.read(chatId, "cwd");
|
|
1500
|
+
}
|
|
1501
|
+
setCwd(chatId, cwd) {
|
|
1502
|
+
this.write(chatId, "cwd", cwd);
|
|
1514
1503
|
}
|
|
1515
1504
|
/** Record what was attached to one prompt. See db.ts for why the key is an ordinal. */
|
|
1516
|
-
setAttachments(
|
|
1505
|
+
setAttachments(chatId, ordinal, files) {
|
|
1517
1506
|
if (files.length === 0) return;
|
|
1518
1507
|
const stmt = db().prepare(
|
|
1519
|
-
`INSERT INTO message_attachments (
|
|
1508
|
+
`INSERT INTO message_attachments (chat_id, ordinal, path, name, size, kind)
|
|
1520
1509
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
1521
|
-
ON CONFLICT(
|
|
1510
|
+
ON CONFLICT(chat_id, ordinal, path) DO NOTHING`
|
|
1522
1511
|
);
|
|
1523
1512
|
for (const f of files) {
|
|
1524
|
-
stmt.run(
|
|
1513
|
+
stmt.run(chatId, ordinal, f.path, f.name, f.size, f.kind);
|
|
1525
1514
|
}
|
|
1526
1515
|
}
|
|
1527
|
-
/** Every attachment in a
|
|
1528
|
-
|
|
1516
|
+
/** Every attachment in a chat, grouped by the message it belongs to. */
|
|
1517
|
+
attachmentsByChat(chatId) {
|
|
1529
1518
|
const rows = db().prepare(
|
|
1530
1519
|
`SELECT ordinal, path, name, size, kind FROM message_attachments
|
|
1531
|
-
WHERE
|
|
1532
|
-
).all(
|
|
1520
|
+
WHERE chat_id = ? ORDER BY ordinal, path`
|
|
1521
|
+
).all(chatId);
|
|
1533
1522
|
const out = /* @__PURE__ */ new Map();
|
|
1534
1523
|
for (const r of rows) {
|
|
1535
1524
|
const list = out.get(r.ordinal) ?? [];
|
|
@@ -1543,20 +1532,19 @@ var init_sessionStore = __esm({
|
|
|
1543
1532
|
}
|
|
1544
1533
|
return out;
|
|
1545
1534
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
db().prepare("DELETE FROM
|
|
1549
|
-
db().prepare("DELETE FROM message_attachments WHERE session_id = ?").run(sessionId);
|
|
1535
|
+
remove(chatId) {
|
|
1536
|
+
db().prepare("DELETE FROM chats WHERE chat_id = ?").run(chatId);
|
|
1537
|
+
db().prepare("DELETE FROM message_attachments WHERE chat_id = ?").run(chatId);
|
|
1550
1538
|
}
|
|
1551
|
-
read(
|
|
1552
|
-
const row = db().prepare(`SELECT ${column} AS value FROM
|
|
1539
|
+
read(chatId, column) {
|
|
1540
|
+
const row = db().prepare(`SELECT ${column} AS value FROM chats WHERE chat_id = ?`).get(chatId);
|
|
1553
1541
|
return row?.value ?? void 0;
|
|
1554
1542
|
}
|
|
1555
|
-
write(
|
|
1543
|
+
write(chatId, column, value) {
|
|
1556
1544
|
db().prepare(
|
|
1557
|
-
`INSERT INTO
|
|
1558
|
-
ON CONFLICT(
|
|
1559
|
-
).run(
|
|
1545
|
+
`INSERT INTO chats (chat_id, ${column}) VALUES (?, ?)
|
|
1546
|
+
ON CONFLICT(chat_id) DO UPDATE SET ${column} = excluded.${column}`
|
|
1547
|
+
).run(chatId, value);
|
|
1560
1548
|
}
|
|
1561
1549
|
};
|
|
1562
1550
|
}
|
|
@@ -1620,14 +1608,12 @@ var init_SessionManager = __esm({
|
|
|
1620
1608
|
init_EventStore();
|
|
1621
1609
|
init_TurnState();
|
|
1622
1610
|
init_kiroFiles();
|
|
1623
|
-
|
|
1611
|
+
init_chatStore();
|
|
1624
1612
|
Session = class {
|
|
1625
1613
|
sessionId;
|
|
1626
1614
|
store;
|
|
1627
1615
|
turnState = new TurnState();
|
|
1628
1616
|
cwd;
|
|
1629
|
-
/** The chat that owns this session's uploads. See chats.ts. */
|
|
1630
|
-
chatId;
|
|
1631
1617
|
agentId;
|
|
1632
1618
|
modelId;
|
|
1633
1619
|
currentModeId;
|
|
@@ -1681,7 +1667,7 @@ var init_SessionManager = __esm({
|
|
|
1681
1667
|
SessionManager = class {
|
|
1682
1668
|
sessions = /* @__PURE__ */ new Map();
|
|
1683
1669
|
log;
|
|
1684
|
-
store = new
|
|
1670
|
+
store = new ChatStore();
|
|
1685
1671
|
spawnProcess;
|
|
1686
1672
|
constructor(log, opts = {}) {
|
|
1687
1673
|
this.log = log;
|
|
@@ -1691,34 +1677,33 @@ var init_SessionManager = __esm({
|
|
|
1691
1677
|
* What a session is called, from every read path. The folder is skipped for a workspace of
|
|
1692
1678
|
* ours, whose name is a uuid.
|
|
1693
1679
|
*/
|
|
1694
|
-
titleOf(
|
|
1680
|
+
titleOf(chat, parts) {
|
|
1695
1681
|
return resolveSessionTitle({
|
|
1696
|
-
override:
|
|
1682
|
+
override: chat.title ?? void 0,
|
|
1697
1683
|
kiroTitle: parts.kiroTitle,
|
|
1698
1684
|
firstPrompt: parts.firstPrompt,
|
|
1699
1685
|
folder: isManagedWorkspace(parts.cwd) ? void 0 : path11.basename(parts.cwd)
|
|
1700
1686
|
});
|
|
1701
1687
|
}
|
|
1702
|
-
|
|
1703
|
-
renameSession(sessionId, title) {
|
|
1688
|
+
renameChat(chatId, title) {
|
|
1704
1689
|
const clean = sanitizeTitle(title);
|
|
1705
|
-
this.store.setTitle(
|
|
1706
|
-
const s = this.sessions.get(
|
|
1690
|
+
this.store.setTitle(chatId, clean);
|
|
1691
|
+
const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
|
|
1707
1692
|
if (s) s.title = clean || s.title;
|
|
1708
1693
|
}
|
|
1709
1694
|
/**
|
|
1710
1695
|
* Re-point a session at a different working directory. Any live process was spawned with the
|
|
1711
1696
|
* old cwd, so it is disposed and the next turn respawns in the new one, transcript intact.
|
|
1712
1697
|
*/
|
|
1713
|
-
async
|
|
1698
|
+
async setChatCwd(chatId, input) {
|
|
1714
1699
|
const resolved = resolveCwd(input);
|
|
1715
|
-
const s = await this.ensureOpen(
|
|
1716
|
-
this.store.setCwd(
|
|
1700
|
+
const s = await this.ensureOpen(chatId);
|
|
1701
|
+
this.store.setCwd(chatId, resolved);
|
|
1717
1702
|
if (s.cwd !== resolved) {
|
|
1718
1703
|
s.cwd = resolved;
|
|
1719
1704
|
s.proc?.dispose();
|
|
1720
1705
|
s.proc = void 0;
|
|
1721
|
-
this.log.info({
|
|
1706
|
+
this.log.info({ chatId, cwd: resolved }, "chat working directory changed");
|
|
1722
1707
|
}
|
|
1723
1708
|
return resolved;
|
|
1724
1709
|
}
|
|
@@ -1731,8 +1716,8 @@ var init_SessionManager = __esm({
|
|
|
1731
1716
|
* The old child must be awaited out first: kiro flushes its session file on shutdown and
|
|
1732
1717
|
* the replacement reads that file to reload the conversation.
|
|
1733
1718
|
*/
|
|
1734
|
-
async
|
|
1735
|
-
const s = await this.ensureOpen(
|
|
1719
|
+
async reloadChat(chatId) {
|
|
1720
|
+
const s = await this.ensureOpen(chatId);
|
|
1736
1721
|
if (s.running) {
|
|
1737
1722
|
throw new Error("Cannot reload while a turn is running");
|
|
1738
1723
|
}
|
|
@@ -1747,14 +1732,14 @@ var init_SessionManager = __esm({
|
|
|
1747
1732
|
ready = resolve;
|
|
1748
1733
|
});
|
|
1749
1734
|
try {
|
|
1750
|
-
return await this.replaceProcess(s);
|
|
1735
|
+
return await this.replaceProcess(chatId, s);
|
|
1751
1736
|
} finally {
|
|
1752
1737
|
s.reloading = void 0;
|
|
1753
1738
|
ready();
|
|
1754
1739
|
}
|
|
1755
1740
|
}
|
|
1756
1741
|
/** The reload itself. Only ever called with the session's reload claim held. */
|
|
1757
|
-
async replaceProcess(s) {
|
|
1742
|
+
async replaceProcess(chatId, s) {
|
|
1758
1743
|
if (!await hasRecordedTurns(s.sessionId)) {
|
|
1759
1744
|
throw new Error(
|
|
1760
1745
|
"kiro has not saved this session yet. Send a message first, then reload."
|
|
@@ -1773,7 +1758,7 @@ var init_SessionManager = __esm({
|
|
|
1773
1758
|
s.lastActivity = Date.now();
|
|
1774
1759
|
invalidateAgents();
|
|
1775
1760
|
this.log.info({ sessionId: s.sessionId, cwd: s.cwd }, "session reloaded");
|
|
1776
|
-
return this.getDetail(
|
|
1761
|
+
return this.getDetail(chatId);
|
|
1777
1762
|
}
|
|
1778
1763
|
/**
|
|
1779
1764
|
* Wait out a reload before touching the session's process. Without this an action
|
|
@@ -1790,33 +1775,34 @@ var init_SessionManager = __esm({
|
|
|
1790
1775
|
// -------------------------------------------------------------------------
|
|
1791
1776
|
// Event subscription - works for any opened session, spawned or not.
|
|
1792
1777
|
// -------------------------------------------------------------------------
|
|
1793
|
-
onEvent(
|
|
1794
|
-
const s = this.sessions.get(
|
|
1778
|
+
onEvent(chatId, cb) {
|
|
1779
|
+
const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
|
|
1795
1780
|
if (!s) return null;
|
|
1796
1781
|
s.store.on("event", cb);
|
|
1797
1782
|
return () => s.store.off("event", cb);
|
|
1798
1783
|
}
|
|
1799
|
-
getStore(
|
|
1800
|
-
return this.sessions.get(
|
|
1784
|
+
getStore(chatId) {
|
|
1785
|
+
return this.sessions.get(this.store.sessionIdForChat(chatId) ?? "")?.store;
|
|
1801
1786
|
}
|
|
1802
|
-
/**
|
|
1803
|
-
async
|
|
1804
|
-
const s = await this.ensureOpen(
|
|
1787
|
+
/** A chat's working directory. Opens it in memory if needed. */
|
|
1788
|
+
async getChatCwd(chatId) {
|
|
1789
|
+
const s = await this.ensureOpen(chatId);
|
|
1805
1790
|
return s.cwd;
|
|
1806
1791
|
}
|
|
1807
|
-
/** Open a session in memory (store + metadata) WITHOUT spawning a process. */
|
|
1808
|
-
async ensureOpen(
|
|
1792
|
+
/** Open a chat's session in memory (store + metadata) WITHOUT spawning a process. */
|
|
1793
|
+
async ensureOpen(chatId) {
|
|
1794
|
+
const sessionId = this.sessionIdOf(chatId);
|
|
1809
1795
|
const existing = this.sessions.get(sessionId);
|
|
1810
1796
|
if (existing) return existing;
|
|
1811
1797
|
const persisted = await readPersistedSession(sessionId);
|
|
1812
1798
|
if (!persisted) throw new Error(`Unknown session: ${sessionId}`);
|
|
1813
|
-
const effectiveCwd = this.store.getCwd(
|
|
1799
|
+
const effectiveCwd = this.store.getCwd(chatId) ?? persisted.cwd;
|
|
1814
1800
|
if (!isWithinRoot(config.fileRoot, effectiveCwd)) {
|
|
1815
1801
|
throw new Error(
|
|
1816
1802
|
`Session working directory is outside the allowed root: ${effectiveCwd}`
|
|
1817
1803
|
);
|
|
1818
1804
|
}
|
|
1819
|
-
const store = new EventStore(
|
|
1805
|
+
const store = new EventStore(chatId);
|
|
1820
1806
|
const s = new Session(sessionId, store, effectiveCwd);
|
|
1821
1807
|
s.title = persisted.title;
|
|
1822
1808
|
s.agentId = persisted.agentId;
|
|
@@ -1912,11 +1898,11 @@ var init_SessionManager = __esm({
|
|
|
1912
1898
|
// Creating / opening
|
|
1913
1899
|
// -------------------------------------------------------------------------
|
|
1914
1900
|
/** Create a new session. Spawns immediately so we get a real kiro sessionId. */
|
|
1915
|
-
async
|
|
1901
|
+
async createChat(opts) {
|
|
1916
1902
|
const chatId = isValidChatId(opts.chatId) ? opts.chatId : crypto.randomUUID();
|
|
1917
1903
|
const cwd = opts.freshWorkspace ? createChatWorkspace(chatId) : resolveCwd(opts.cwd);
|
|
1918
1904
|
const tempId = `pending-${Date.now()}-${Math.floor(this.sessions.size)}`;
|
|
1919
|
-
const store = new EventStore(
|
|
1905
|
+
const store = new EventStore(chatId);
|
|
1920
1906
|
const s = new Session(tempId, store, cwd);
|
|
1921
1907
|
s.agentId = opts.agentId ?? config.defaultAgent;
|
|
1922
1908
|
s.currentModeId = s.agentId;
|
|
@@ -1928,20 +1914,20 @@ var init_SessionManager = __esm({
|
|
|
1928
1914
|
this.evict(s.sessionId);
|
|
1929
1915
|
throw err;
|
|
1930
1916
|
}
|
|
1931
|
-
this.store.
|
|
1932
|
-
|
|
1917
|
+
this.store.create(chatId);
|
|
1918
|
+
this.store.bindSession(chatId, s.sessionId);
|
|
1933
1919
|
const name = sanitizeTitle(opts.title ?? "") || (opts.freshWorkspace ? "" : path11.basename(s.cwd));
|
|
1934
1920
|
if (name) {
|
|
1935
|
-
this.store.setTitle(
|
|
1921
|
+
this.store.setTitle(chatId, name);
|
|
1936
1922
|
s.title = name;
|
|
1937
1923
|
}
|
|
1938
|
-
return this.buildDetail(s, []);
|
|
1924
|
+
return this.buildDetail(this.store.get(chatId), s, []);
|
|
1939
1925
|
}
|
|
1940
1926
|
// -------------------------------------------------------------------------
|
|
1941
1927
|
// Actions - these spawn the process lazily.
|
|
1942
1928
|
// -------------------------------------------------------------------------
|
|
1943
|
-
async runPrompt(
|
|
1944
|
-
const s = await this.ensureOpen(
|
|
1929
|
+
async runPrompt(chatId, content, attachments) {
|
|
1930
|
+
const s = await this.ensureOpen(chatId);
|
|
1945
1931
|
await this.settleReload(s);
|
|
1946
1932
|
if (s.running) throw new Error("A turn is already running for this session");
|
|
1947
1933
|
s.running = true;
|
|
@@ -1953,17 +1939,17 @@ var init_SessionManager = __esm({
|
|
|
1953
1939
|
s.running = false;
|
|
1954
1940
|
throw err;
|
|
1955
1941
|
}
|
|
1956
|
-
if (!this.store.getTitle(
|
|
1942
|
+
if (!this.store.getTitle(chatId)) {
|
|
1957
1943
|
const title = titleFromPrompt(content);
|
|
1958
1944
|
if (title) {
|
|
1959
|
-
this.store.setTitle(
|
|
1945
|
+
this.store.setTitle(chatId, title);
|
|
1960
1946
|
s.title = title;
|
|
1961
1947
|
}
|
|
1962
1948
|
}
|
|
1963
1949
|
let recorded;
|
|
1964
1950
|
if (attachments?.length) {
|
|
1965
1951
|
const ordinal = await promptCount(s.sessionId);
|
|
1966
|
-
this.store.setAttachments(
|
|
1952
|
+
this.store.setAttachments(chatId, ordinal, attachments);
|
|
1967
1953
|
recorded = attachments;
|
|
1968
1954
|
}
|
|
1969
1955
|
s.record({ kind: "turn_started", prompt: content, attachments: recorded });
|
|
@@ -1980,12 +1966,12 @@ ${tail}` : err.message;
|
|
|
1980
1966
|
s.lastActivity = Date.now();
|
|
1981
1967
|
});
|
|
1982
1968
|
}
|
|
1983
|
-
cancel(
|
|
1984
|
-
const s = this.sessions.get(
|
|
1969
|
+
cancel(chatId) {
|
|
1970
|
+
const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
|
|
1985
1971
|
s?.proc?.cancel(s.sessionId);
|
|
1986
1972
|
}
|
|
1987
|
-
async setMode(
|
|
1988
|
-
const s = await this.ensureOpen(
|
|
1973
|
+
async setMode(chatId, modeId) {
|
|
1974
|
+
const s = await this.ensureOpen(chatId);
|
|
1989
1975
|
await this.settleReload(s);
|
|
1990
1976
|
const proc = await this.ensureProc(s);
|
|
1991
1977
|
await proc.setMode(s.sessionId, modeId);
|
|
@@ -1993,16 +1979,16 @@ ${tail}` : err.message;
|
|
|
1993
1979
|
s.agentId = modeId;
|
|
1994
1980
|
s.lastActivity = Date.now();
|
|
1995
1981
|
}
|
|
1996
|
-
async setModel(
|
|
1997
|
-
const s = await this.ensureOpen(
|
|
1982
|
+
async setModel(chatId, modelId) {
|
|
1983
|
+
const s = await this.ensureOpen(chatId);
|
|
1998
1984
|
await this.settleReload(s);
|
|
1999
1985
|
const proc = await this.ensureProc(s);
|
|
2000
1986
|
await proc.setModel(s.sessionId, modelId);
|
|
2001
1987
|
s.modelId = modelId;
|
|
2002
1988
|
s.lastActivity = Date.now();
|
|
2003
1989
|
}
|
|
2004
|
-
async execCommand(
|
|
2005
|
-
const s = await this.ensureOpen(
|
|
1990
|
+
async execCommand(chatId, command) {
|
|
1991
|
+
const s = await this.ensureOpen(chatId);
|
|
2006
1992
|
await this.settleReload(s);
|
|
2007
1993
|
const proc = await this.ensureProc(s);
|
|
2008
1994
|
await proc.execCommand(s.sessionId, command);
|
|
@@ -2012,27 +1998,26 @@ ${tail}` : err.message;
|
|
|
2012
1998
|
// Listing / detail - never spawns.
|
|
2013
1999
|
// -------------------------------------------------------------------------
|
|
2014
2000
|
/**
|
|
2015
|
-
* The one place a
|
|
2001
|
+
* The one place a ChatSummary is assembled: kiro's file and Casper's live state each hold
|
|
2016
2002
|
* part of the truth, so every read path projects them through here. `persisted` is absent for
|
|
2017
2003
|
* a session with no file yet, `live` for a dormant one, and callers guarantee one of them.
|
|
2018
2004
|
*/
|
|
2019
|
-
summaryOf(live, persisted, transcript) {
|
|
2020
|
-
const sessionId = live?.sessionId ?? persisted.sessionId;
|
|
2005
|
+
summaryOf(chat, live, persisted, transcript) {
|
|
2021
2006
|
const snap = live?.turnState.get();
|
|
2022
|
-
const cwd = live?.cwd ??
|
|
2007
|
+
const cwd = live?.cwd ?? chat.cwd ?? persisted?.cwd ?? config.defaultCwd;
|
|
2023
2008
|
return {
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
title: this.titleOf(
|
|
2009
|
+
chatId: chat.chatId,
|
|
2010
|
+
sessionId: live?.sessionId ?? chat.sessionId ?? void 0,
|
|
2011
|
+
title: this.titleOf(chat, {
|
|
2027
2012
|
// kiro's file is what kiro called it; the live copy is only a cache of it.
|
|
2028
2013
|
kiroTitle: persisted?.title || live?.title,
|
|
2029
2014
|
firstPrompt: transcript && firstPromptText(transcript),
|
|
2030
2015
|
cwd
|
|
2031
2016
|
}),
|
|
2032
2017
|
cwd,
|
|
2033
|
-
createdAt: persisted?.createdAt ?? live.
|
|
2018
|
+
createdAt: persisted?.createdAt ?? live?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2034
2019
|
// kiro's file and our own activity move separately.
|
|
2035
|
-
updatedAt: live ? newerOf(persisted?.updatedAt, live.updatedAt) : persisted.
|
|
2020
|
+
updatedAt: live ? newerOf(persisted?.updatedAt, live.updatedAt) : persisted?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2036
2021
|
liveness: live?.proc ? "live" : "dormant",
|
|
2037
2022
|
agentId: live?.agentId ?? persisted?.agentId,
|
|
2038
2023
|
modelId: live?.modelId ?? persisted?.modelId,
|
|
@@ -2042,49 +2027,60 @@ ${tail}` : err.message;
|
|
|
2042
2027
|
contextUsagePercentage: snap?.contextUsagePercentage || persisted?.contextUsagePercentage
|
|
2043
2028
|
};
|
|
2044
2029
|
}
|
|
2045
|
-
async
|
|
2046
|
-
const
|
|
2047
|
-
const files =
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2030
|
+
async listChats() {
|
|
2031
|
+
const rows = this.store.all();
|
|
2032
|
+
const files = await Promise.all(
|
|
2033
|
+
rows.map((r) => r.sessionId ? readPersistedSession(r.sessionId) : null)
|
|
2034
|
+
);
|
|
2035
|
+
const out = [];
|
|
2036
|
+
rows.forEach((row, i) => {
|
|
2037
|
+
const live = row.sessionId ? this.sessions.get(row.sessionId) : void 0;
|
|
2038
|
+
const persisted = files[i] ?? void 0;
|
|
2039
|
+
if (live && isGhost(live, !!persisted)) {
|
|
2040
|
+
this.evict(live.sessionId);
|
|
2041
|
+
return;
|
|
2056
2042
|
}
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2043
|
+
if (!persisted && !live) return;
|
|
2044
|
+
out.push(this.summaryOf(row, live, persisted));
|
|
2045
|
+
});
|
|
2046
|
+
return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
2047
|
+
}
|
|
2048
|
+
/** kiro's session id for a chat, or a throw if the chat is unknown. */
|
|
2049
|
+
sessionIdOf(chatId) {
|
|
2050
|
+
const sessionId = this.store.sessionIdForChat(chatId);
|
|
2051
|
+
if (!sessionId) throw new Error(`Unknown chat: ${chatId}`);
|
|
2052
|
+
return sessionId;
|
|
2053
|
+
}
|
|
2054
|
+
async getDetail(chatId) {
|
|
2055
|
+
const chat = this.store.get(chatId);
|
|
2056
|
+
if (!chat) throw new Error(`Unknown chat: ${chatId}`);
|
|
2057
|
+
const sessionId = chat.sessionId;
|
|
2062
2058
|
const [transcript, persisted] = await Promise.all([
|
|
2063
|
-
hydrateTranscript(sessionId, this.store.
|
|
2064
|
-
readPersistedSession(sessionId)
|
|
2059
|
+
sessionId ? hydrateTranscript(sessionId, this.store.attachmentsByChat(chatId)) : Promise.resolve([]),
|
|
2060
|
+
sessionId ? readPersistedSession(sessionId) : Promise.resolve(null)
|
|
2065
2061
|
]);
|
|
2066
|
-
const s = this.sessions.get(sessionId);
|
|
2067
|
-
if (!s && !persisted) throw new Error(`Unknown
|
|
2068
|
-
return this.buildDetail(s, transcript, persisted ?? void 0);
|
|
2062
|
+
const s = sessionId ? this.sessions.get(sessionId) : void 0;
|
|
2063
|
+
if (!s && !persisted) throw new Error(`Unknown chat: ${chatId}`);
|
|
2064
|
+
return this.buildDetail(chat, s, transcript, persisted ?? void 0);
|
|
2069
2065
|
}
|
|
2070
2066
|
/**
|
|
2071
2067
|
* A slice of the transcript for lazy "load older on scroll up". Re-hydrates
|
|
2072
2068
|
* from disk (fast: ~150ms even for a multi-MB session) and returns the
|
|
2073
2069
|
* requested window. offset/limit are clamped to the transcript bounds.
|
|
2074
2070
|
*/
|
|
2075
|
-
async getTranscriptPage(
|
|
2071
|
+
async getTranscriptPage(chatId, offset, limit) {
|
|
2076
2072
|
const transcript = await hydrateTranscript(
|
|
2077
|
-
|
|
2078
|
-
this.store.
|
|
2073
|
+
this.sessionIdOf(chatId),
|
|
2074
|
+
this.store.attachmentsByChat(chatId)
|
|
2079
2075
|
);
|
|
2080
2076
|
const start = Math.max(0, Math.min(offset, transcript.length));
|
|
2081
2077
|
const end = Math.max(start, Math.min(start + limit, transcript.length));
|
|
2082
2078
|
return transcript.slice(start, end);
|
|
2083
2079
|
}
|
|
2084
2080
|
/** One projection, so a dormant session and a live one cannot disagree. */
|
|
2085
|
-
buildDetail(s, transcript, persisted) {
|
|
2081
|
+
buildDetail(chat, s, transcript, persisted) {
|
|
2086
2082
|
return {
|
|
2087
|
-
summary: this.summaryOf(s, persisted, transcript),
|
|
2083
|
+
summary: this.summaryOf(chat, s, persisted, transcript),
|
|
2088
2084
|
modes: s?.availableModes ?? [],
|
|
2089
2085
|
currentModeId: s?.currentModeId ?? persisted?.agentId,
|
|
2090
2086
|
// Only the tail is sent on load; replayHead/title use the full transcript.
|
|
@@ -2137,17 +2133,20 @@ ${tail}` : err.message;
|
|
|
2137
2133
|
s.store.dispose();
|
|
2138
2134
|
}
|
|
2139
2135
|
// Permanently delete a session: evict it from memory, remove its on-disk
|
|
2140
|
-
// files, and drop any title override.
|
|
2141
|
-
async
|
|
2142
|
-
const
|
|
2136
|
+
// files and the directory the chat owns, and drop any title override.
|
|
2137
|
+
async deleteChat(chatId) {
|
|
2138
|
+
const sessionId = this.store.sessionIdForChat(chatId);
|
|
2139
|
+
const s = sessionId ? this.sessions.get(sessionId) : void 0;
|
|
2143
2140
|
if (s?.proc) {
|
|
2144
2141
|
await s.proc.disposeAndWait().catch(() => {
|
|
2145
2142
|
});
|
|
2146
2143
|
s.proc = void 0;
|
|
2147
2144
|
s.running = false;
|
|
2148
2145
|
}
|
|
2146
|
+
this.store.remove(chatId);
|
|
2147
|
+
await removeChatDir(chatId);
|
|
2148
|
+
if (!sessionId) return;
|
|
2149
2149
|
this.evict(sessionId);
|
|
2150
|
-
this.store.remove(sessionId);
|
|
2151
2150
|
await deletePersistedSession(sessionId);
|
|
2152
2151
|
setTimeout(() => void deletePersistedSession(sessionId).catch(() => {
|
|
2153
2152
|
}), 2500).unref?.();
|
|
@@ -2424,12 +2423,12 @@ function replyWith(reply, failure) {
|
|
|
2424
2423
|
reply.code(failure.status);
|
|
2425
2424
|
return { error: failure.error };
|
|
2426
2425
|
}
|
|
2427
|
-
async function
|
|
2426
|
+
async function resolveChatPath(sessions, sessionId, requestedPath, require2) {
|
|
2428
2427
|
let cwd;
|
|
2429
2428
|
try {
|
|
2430
|
-
cwd = await sessions.
|
|
2429
|
+
cwd = await sessions.getChatCwd(sessionId);
|
|
2431
2430
|
} catch {
|
|
2432
|
-
return { ok: false, status: 404, error: "
|
|
2431
|
+
return { ok: false, status: 404, error: "Chat not found" };
|
|
2433
2432
|
}
|
|
2434
2433
|
const relative = (requestedPath ?? "").replace(/^\/+/, "");
|
|
2435
2434
|
if (require2 === "file" && !relative) {
|
|
@@ -2817,15 +2816,15 @@ var init_fs = __esm({
|
|
|
2817
2816
|
}
|
|
2818
2817
|
});
|
|
2819
2818
|
|
|
2820
|
-
// server/src/routes/
|
|
2821
|
-
function
|
|
2822
|
-
app.get("/api/
|
|
2823
|
-
return {
|
|
2819
|
+
// server/src/routes/chats.ts
|
|
2820
|
+
function registerChatRoutes(app, manager) {
|
|
2821
|
+
app.get("/api/chats", async () => {
|
|
2822
|
+
return { chats: await manager.listChats() };
|
|
2824
2823
|
});
|
|
2825
|
-
app.post("/api/
|
|
2824
|
+
app.post("/api/chats", async (req, reply) => {
|
|
2826
2825
|
const body = req.body ?? {};
|
|
2827
2826
|
try {
|
|
2828
|
-
return await manager.
|
|
2827
|
+
return await manager.createChat({
|
|
2829
2828
|
cwd: body.cwd,
|
|
2830
2829
|
agentId: body.agentId,
|
|
2831
2830
|
modelId: body.modelId,
|
|
@@ -2841,7 +2840,7 @@ function registerSessionRoutes(app, manager) {
|
|
|
2841
2840
|
return { error: err.message };
|
|
2842
2841
|
}
|
|
2843
2842
|
});
|
|
2844
|
-
app.get("/api/
|
|
2843
|
+
app.get("/api/chats/:id", async (req, reply) => {
|
|
2845
2844
|
try {
|
|
2846
2845
|
return await manager.getDetail(req.params.id);
|
|
2847
2846
|
} catch (err) {
|
|
@@ -2850,7 +2849,7 @@ function registerSessionRoutes(app, manager) {
|
|
|
2850
2849
|
}
|
|
2851
2850
|
});
|
|
2852
2851
|
app.get(
|
|
2853
|
-
"/api/
|
|
2852
|
+
"/api/chats/:id/transcript",
|
|
2854
2853
|
async (req, reply) => {
|
|
2855
2854
|
const offset = Number.parseInt(req.query.offset ?? "", 10);
|
|
2856
2855
|
const limit = Number.parseInt(req.query.limit ?? "", 10);
|
|
@@ -2867,7 +2866,7 @@ function registerSessionRoutes(app, manager) {
|
|
|
2867
2866
|
}
|
|
2868
2867
|
);
|
|
2869
2868
|
app.post(
|
|
2870
|
-
"/api/
|
|
2869
|
+
"/api/chats/:id/prompt",
|
|
2871
2870
|
async (req, reply) => {
|
|
2872
2871
|
try {
|
|
2873
2872
|
await manager.runPrompt(req.params.id, req.body.prompt, req.body.attachments);
|
|
@@ -2878,33 +2877,33 @@ function registerSessionRoutes(app, manager) {
|
|
|
2878
2877
|
}
|
|
2879
2878
|
}
|
|
2880
2879
|
);
|
|
2881
|
-
app.post("/api/
|
|
2880
|
+
app.post("/api/chats/:id/cancel", async (req) => {
|
|
2882
2881
|
manager.cancel(req.params.id);
|
|
2883
2882
|
return { ok: true };
|
|
2884
2883
|
});
|
|
2885
2884
|
app.post(
|
|
2886
|
-
"/api/
|
|
2885
|
+
"/api/chats/:id/model",
|
|
2887
2886
|
async (req) => {
|
|
2888
2887
|
await manager.setModel(req.params.id, req.body.modelId);
|
|
2889
2888
|
return { ok: true };
|
|
2890
2889
|
}
|
|
2891
2890
|
);
|
|
2892
2891
|
app.post(
|
|
2893
|
-
"/api/
|
|
2892
|
+
"/api/chats/:id/mode",
|
|
2894
2893
|
async (req) => {
|
|
2895
2894
|
await manager.setMode(req.params.id, req.body.modeId);
|
|
2896
2895
|
return { ok: true };
|
|
2897
2896
|
}
|
|
2898
2897
|
);
|
|
2899
2898
|
app.post(
|
|
2900
|
-
"/api/
|
|
2899
|
+
"/api/chats/:id/rename",
|
|
2901
2900
|
async (req) => {
|
|
2902
|
-
manager.
|
|
2901
|
+
manager.renameChat(req.params.id, req.body.title);
|
|
2903
2902
|
return { ok: true };
|
|
2904
2903
|
}
|
|
2905
2904
|
);
|
|
2906
2905
|
app.post(
|
|
2907
|
-
"/api/
|
|
2906
|
+
"/api/chats/:id/cwd",
|
|
2908
2907
|
async (req, reply) => {
|
|
2909
2908
|
const cwd = (req.body?.cwd ?? "").trim();
|
|
2910
2909
|
if (!cwd) {
|
|
@@ -2912,28 +2911,28 @@ function registerSessionRoutes(app, manager) {
|
|
|
2912
2911
|
return { error: "cwd is required" };
|
|
2913
2912
|
}
|
|
2914
2913
|
try {
|
|
2915
|
-
return { ok: true, cwd: await manager.
|
|
2914
|
+
return { ok: true, cwd: await manager.setChatCwd(req.params.id, cwd) };
|
|
2916
2915
|
} catch (err) {
|
|
2917
2916
|
reply.code(400);
|
|
2918
2917
|
return { error: err.message };
|
|
2919
2918
|
}
|
|
2920
2919
|
}
|
|
2921
2920
|
);
|
|
2922
|
-
app.post("/api/
|
|
2921
|
+
app.post("/api/chats/:id/reload", async (req, reply) => {
|
|
2923
2922
|
try {
|
|
2924
|
-
return await manager.
|
|
2923
|
+
return await manager.reloadChat(req.params.id);
|
|
2925
2924
|
} catch (err) {
|
|
2926
2925
|
reply.code(400);
|
|
2927
2926
|
return { error: err.message };
|
|
2928
2927
|
}
|
|
2929
2928
|
});
|
|
2930
|
-
app.delete("/api/
|
|
2931
|
-
await manager.
|
|
2929
|
+
app.delete("/api/chats/:id", async (req) => {
|
|
2930
|
+
await manager.deleteChat(req.params.id);
|
|
2932
2931
|
return { ok: true };
|
|
2933
2932
|
});
|
|
2934
2933
|
}
|
|
2935
|
-
var
|
|
2936
|
-
"server/src/routes/
|
|
2934
|
+
var init_chats2 = __esm({
|
|
2935
|
+
"server/src/routes/chats.ts"() {
|
|
2937
2936
|
"use strict";
|
|
2938
2937
|
}
|
|
2939
2938
|
});
|
|
@@ -2944,9 +2943,9 @@ import { createReadStream as createReadStream3 } from "node:fs";
|
|
|
2944
2943
|
import path16 from "node:path";
|
|
2945
2944
|
function registerWorkspaceRoutes(app, manager) {
|
|
2946
2945
|
app.get(
|
|
2947
|
-
"/api/
|
|
2946
|
+
"/api/chats/:id/tree",
|
|
2948
2947
|
async (req, reply) => {
|
|
2949
|
-
const resolved = await
|
|
2948
|
+
const resolved = await resolveChatPath(
|
|
2950
2949
|
manager,
|
|
2951
2950
|
req.params.id,
|
|
2952
2951
|
req.query.path,
|
|
@@ -2990,9 +2989,9 @@ function registerWorkspaceRoutes(app, manager) {
|
|
|
2990
2989
|
}
|
|
2991
2990
|
);
|
|
2992
2991
|
app.get(
|
|
2993
|
-
"/api/
|
|
2992
|
+
"/api/chats/:id/download",
|
|
2994
2993
|
async (req, reply) => {
|
|
2995
|
-
const resolved = await
|
|
2994
|
+
const resolved = await resolveChatPath(manager, req.params.id, req.query.path, "file");
|
|
2996
2995
|
if (!resolved.ok) return replyWith(reply, resolved);
|
|
2997
2996
|
const { real: realTarget, stat } = resolved;
|
|
2998
2997
|
if (stat.size > MAX_DOWNLOAD_BYTES) {
|
|
@@ -3012,9 +3011,9 @@ function registerWorkspaceRoutes(app, manager) {
|
|
|
3012
3011
|
}
|
|
3013
3012
|
);
|
|
3014
3013
|
app.get(
|
|
3015
|
-
"/api/
|
|
3014
|
+
"/api/chats/:id/preview",
|
|
3016
3015
|
async (req, reply) => {
|
|
3017
|
-
const resolved = await
|
|
3016
|
+
const resolved = await resolveChatPath(manager, req.params.id, req.query.path, "file");
|
|
3018
3017
|
if (!resolved.ok) return replyWith(reply, resolved);
|
|
3019
3018
|
const { real: realTarget, stat } = resolved;
|
|
3020
3019
|
return sendFilePreview(req, reply, realTarget, stat);
|
|
@@ -3259,7 +3258,7 @@ var init_dirWatchers = __esm({
|
|
|
3259
3258
|
function send(socket, msg) {
|
|
3260
3259
|
if (socket.readyState === socket.OPEN) socket.send(JSON.stringify(msg));
|
|
3261
3260
|
}
|
|
3262
|
-
function handleConnection(socket, manager,
|
|
3261
|
+
function handleConnection(socket, manager, chatId, startCursor) {
|
|
3263
3262
|
let cursor = startCursor;
|
|
3264
3263
|
let unsubscribe = null;
|
|
3265
3264
|
let alive = true;
|
|
@@ -3267,7 +3266,7 @@ function handleConnection(socket, manager, sessionId, startCursor) {
|
|
|
3267
3266
|
const watchers = createDirWatchers({
|
|
3268
3267
|
resolve: async (relative) => {
|
|
3269
3268
|
try {
|
|
3270
|
-
return confineToRoot(await manager.
|
|
3269
|
+
return confineToRoot(await manager.getChatCwd(chatId), relative);
|
|
3271
3270
|
} catch {
|
|
3272
3271
|
return null;
|
|
3273
3272
|
}
|
|
@@ -3281,13 +3280,13 @@ function handleConnection(socket, manager, sessionId, startCursor) {
|
|
|
3281
3280
|
};
|
|
3282
3281
|
const attach = async () => {
|
|
3283
3282
|
try {
|
|
3284
|
-
await manager.ensureOpen(
|
|
3283
|
+
await manager.ensureOpen(chatId);
|
|
3285
3284
|
} catch (err) {
|
|
3286
3285
|
send(socket, { type: "error", message: err.message });
|
|
3287
3286
|
socket.close(1011, "open failed");
|
|
3288
3287
|
return;
|
|
3289
3288
|
}
|
|
3290
|
-
const store = manager.getStore(
|
|
3289
|
+
const store = manager.getStore(chatId);
|
|
3291
3290
|
if (!store) {
|
|
3292
3291
|
send(socket, { type: "error", message: "Session store unavailable" });
|
|
3293
3292
|
socket.close(1011, "no store");
|
|
@@ -3304,7 +3303,7 @@ function handleConnection(socket, manager, sessionId, startCursor) {
|
|
|
3304
3303
|
for (const e of events) forward(e);
|
|
3305
3304
|
}
|
|
3306
3305
|
send(socket, { type: "replay_complete", head: store.head() });
|
|
3307
|
-
unsubscribe = manager.onEvent(
|
|
3306
|
+
unsubscribe = manager.onEvent(chatId, forward);
|
|
3308
3307
|
ready = true;
|
|
3309
3308
|
};
|
|
3310
3309
|
void attach();
|
|
@@ -3323,17 +3322,17 @@ function handleConnection(socket, manager, sessionId, startCursor) {
|
|
|
3323
3322
|
case "watch_paths":
|
|
3324
3323
|
return watchers.sync(msg.paths);
|
|
3325
3324
|
case "prompt":
|
|
3326
|
-
return ack("prompt", () => manager.runPrompt(
|
|
3325
|
+
return ack("prompt", () => manager.runPrompt(chatId, msg.content, msg.attachments));
|
|
3327
3326
|
case "cancel":
|
|
3328
|
-
return ack("cancel", () => manager.cancel(
|
|
3327
|
+
return ack("cancel", () => manager.cancel(chatId));
|
|
3329
3328
|
case "set_mode":
|
|
3330
|
-
return ack("set_mode", () => manager.setMode(
|
|
3329
|
+
return ack("set_mode", () => manager.setMode(chatId, msg.modeId));
|
|
3331
3330
|
case "set_model":
|
|
3332
|
-
return ack("set_model", () => manager.setModel(
|
|
3331
|
+
return ack("set_model", () => manager.setModel(chatId, msg.modelId));
|
|
3333
3332
|
case "exec_command":
|
|
3334
3333
|
return ack(
|
|
3335
3334
|
"exec_command",
|
|
3336
|
-
() => manager.execCommand(
|
|
3335
|
+
() => manager.execCommand(chatId, msg.command)
|
|
3337
3336
|
);
|
|
3338
3337
|
default:
|
|
3339
3338
|
return send(socket, { type: "error", message: "Unknown message type" });
|
|
@@ -3380,15 +3379,15 @@ function registerWsGateway(app, manager) {
|
|
|
3380
3379
|
socket.close(1008, "Unauthorized");
|
|
3381
3380
|
return;
|
|
3382
3381
|
}
|
|
3383
|
-
if (!query.
|
|
3384
|
-
send(socket, { type: "error", message: "Missing
|
|
3385
|
-
socket.close(1008, "Missing
|
|
3382
|
+
if (!query.chatId) {
|
|
3383
|
+
send(socket, { type: "error", message: "Missing chatId" });
|
|
3384
|
+
socket.close(1008, "Missing chatId");
|
|
3386
3385
|
return;
|
|
3387
3386
|
}
|
|
3388
3387
|
handleConnection(
|
|
3389
3388
|
socket,
|
|
3390
3389
|
manager,
|
|
3391
|
-
query.
|
|
3390
|
+
query.chatId,
|
|
3392
3391
|
Number.parseInt(query.cursor ?? "0", 10) || 0
|
|
3393
3392
|
);
|
|
3394
3393
|
});
|
|
@@ -3437,7 +3436,7 @@ async function buildApp() {
|
|
|
3437
3436
|
registerModelRoutes(app);
|
|
3438
3437
|
registerAgentRoutes(app);
|
|
3439
3438
|
registerFsRoutes(app);
|
|
3440
|
-
|
|
3439
|
+
registerChatRoutes(app, manager);
|
|
3441
3440
|
registerWorkspaceRoutes(app, manager);
|
|
3442
3441
|
registerUploadRoutes(app);
|
|
3443
3442
|
registerWsGateway(app, manager);
|
|
@@ -3473,7 +3472,7 @@ var init_app = __esm({
|
|
|
3473
3472
|
init_models2();
|
|
3474
3473
|
init_agents2();
|
|
3475
3474
|
init_fs();
|
|
3476
|
-
|
|
3475
|
+
init_chats2();
|
|
3477
3476
|
init_workspace();
|
|
3478
3477
|
init_uploads();
|
|
3479
3478
|
init_health();
|
|
@@ -4104,7 +4103,7 @@ var init_server2 = __esm({
|
|
|
4104
4103
|
"server/src/mcp/server.ts"() {
|
|
4105
4104
|
"use strict";
|
|
4106
4105
|
init_protocol();
|
|
4107
|
-
VERSION = true ? "0.
|
|
4106
|
+
VERSION = true ? "0.13.1" : "dev";
|
|
4108
4107
|
}
|
|
4109
4108
|
});
|
|
4110
4109
|
|
|
@@ -7484,7 +7483,7 @@ var program = new Command();
|
|
|
7484
7483
|
// server/src/cli/program.ts
|
|
7485
7484
|
init_paths();
|
|
7486
7485
|
init_settings();
|
|
7487
|
-
var VERSION2 = true ? "0.
|
|
7486
|
+
var VERSION2 = true ? "0.13.1" : "dev";
|
|
7488
7487
|
async function bootstrap() {
|
|
7489
7488
|
const file = configFilePath();
|
|
7490
7489
|
const settings = readSettings(file);
|