@joeyshi12/casper 0.14.4 → 0.15.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/casper.js +7 -10
- package/dist/mcp.js +1 -1
- package/dist/server.js +388 -176
- package/dist/web/assets/{abnfDiagram-N423BO3Z-CqyGEOG8.js → abnfDiagram-N423BO3Z-BdMaGN9m.js} +1 -1
- package/dist/web/assets/{arc-Co7J4_g0.js → arc-BnKKRvQY.js} +1 -1
- package/dist/web/assets/{architectureDiagram-T3A2C74G-DrmqajPn.js → architectureDiagram-T3A2C74G-DTlxOib5.js} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-BM4aGq-2.js → blockDiagram-VBNYF7ZC-DTkRCQhv.js} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-jYLQex_F.js → c4Diagram-5PPSVZJV--67cA9OX.js} +1 -1
- package/dist/web/assets/channel-DTXv2anz.js +1 -0
- package/dist/web/assets/{chunk-2GRJ4B5K-D3yOD5Jc.js → chunk-2GRJ4B5K-BrjB56ka.js} +1 -1
- package/dist/web/assets/{chunk-2Q5K7J3B-eNp6GymR.js → chunk-2Q5K7J3B-WbUpQHmY.js} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-T9iaxe5e.js → chunk-5RXB4S5H-DeZGy6C1.js} +1 -1
- package/dist/web/assets/{chunk-5VM5RSS4-BjDCtHJi.js → chunk-5VM5RSS4-BB9K8srw.js} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-DYRDUSAT.js → chunk-6Q2QTUOP-DJIJSfBw.js} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-CDkVvx8j.js → chunk-GF5L2VYU-CFBKUiz3.js} +1 -1
- package/dist/web/assets/{chunk-JWPE2WC7-DrIE-Bh-.js → chunk-JWPE2WC7-BDdR90_8.js} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-DG-dHq84.js → chunk-KBJHAD2P-DLpqjbH7.js} +1 -1
- package/dist/web/assets/{chunk-RYQCIY6F-xj6xc7d2.js → chunk-RYQCIY6F-BwVrXywq.js} +1 -1
- package/dist/web/assets/{chunk-XXDRQBXY-CjIiZdHc.js → chunk-XXDRQBXY-BnjspxMt.js} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-CJCliu9V.js +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-CJCliu9V.js +1 -0
- package/dist/web/assets/{cose-bilkent-JH36ORCC-BcSHtUpl.js → cose-bilkent-JH36ORCC-BDrRQebw.js} +1 -1
- package/dist/web/assets/{cynefin-VYW2F7L2-Bj-UuS_4.js → cynefin-VYW2F7L2-BUryYrZF.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-1Cd2NcM6.js → cynefinDiagram-MW4NZA55-DusXqwHu.js} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-DUX4S3MP.js → dagre-VZM6K2ZE-ClQlnQBH.js} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-jGdatiQR.js → diagram-7IWD3JNH-IG9jBapz.js} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-Duop1UHS.js → diagram-B4RE2ZJO-CyYhYQZf.js} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-DEFogBqA.js → diagram-LBJQPF4R-DEntLFKu.js} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-CL1d-wRv.js → diagram-Q27KOJAE-zGK474hW.js} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-DXwhKhT2.js → diagram-UB23O5K3-gaRM7jNY.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-Cx5whiF_.js → ebnfDiagram-BXEA7PRR-PxMCEG-q.js} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-CxxmfAXp.js → erDiagram-JOGREHBK-VIuCO-DR.js} +1 -1
- package/dist/web/assets/{flowDiagram-UKHOOZJN-D9XXHTx5.js → flowDiagram-UKHOOZJN-XdCyqSLb.js} +1 -1
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-f2WbEiXa.js → ganttDiagram-PKOTCBZU-BP6Hh3cz.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-BIOj5QUF.js → gitGraphDiagram-DS77QQ5N-De7H7ZE4.js} +1 -1
- package/dist/web/assets/index-61Vziwj9.css +1 -0
- package/dist/web/assets/index-DleBflj4.js +244 -0
- package/dist/web/assets/{index-B9wyN9oZ.js → index-DpcMyL8g.js} +1 -1
- package/dist/web/assets/{infoDiagram-6WML65LV-D1qQFwzf.js → infoDiagram-6WML65LV-O3MyFFXV.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-BWg4hrU2.js → ishikawaDiagram-WSZJBQD7-Cw56BHJs.js} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-DKKVHX1m.js → journeyDiagram-NVQOT4AX-BMN5g7oz.js} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DK7802hr.js → kanban-definition-27J2QSJJ-nN75H6xk.js} +1 -1
- package/dist/web/assets/{linear-j2Z2-Ts5.js → linear-ChgkcafX.js} +1 -1
- package/dist/web/assets/{mermaid.core-B9t4l1LB.js → mermaid.core-CInfhkgy.js} +4 -4
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-MpYC1ION.js → mindmap-definition-FAOFIHXS-hT02Fvo2.js} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-CEVJqCIu.js → pegDiagram-VL7TDLO6-DMmqTB_C.js} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-DQDaWsnB.js → pieDiagram-7S7Q4E2Y-DfDTce-C.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-D2ovSq73.js → quadrantDiagram-CIZ2JOQS-C8CVCtgF.js} +1 -1
- package/dist/web/assets/{railroadDiagram-AXF67PYL-BtG5ML2m.js → railroadDiagram-AXF67PYL-BBsvtc1u.js} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-t8GKRNCM.js → requirementDiagram-LRYGKXZP-Dkf3d5-L.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-B5J2_2ZC.js → sankeyDiagram-W5VNT64P-CzwdhwVC.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-C64N2umn.js → sequenceDiagram-SI44F4Z6-CozbioOX.js} +1 -1
- package/dist/web/assets/{sizeCapture-X5ZJPWSS-BcAjn7U8.js → sizeCapture-X5ZJPWSS-COtxgRbh.js} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-DkySkcyS.js → stateDiagram-OKZ733FA-BX-92BW6.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-CnmbbbmA.js +1 -0
- package/dist/web/assets/{swimlanes-SLNWSIFB-2PZUwW3q.js → swimlanes-SLNWSIFB-BmnR0ZMK.js} +2 -2
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CfCl-u_J.js +8 -0
- package/dist/web/assets/{timeline-definition-Z64GVDOM-DP8smTU4.js → timeline-definition-Z64GVDOM-CvRskGWc.js} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-0oTgA1su.js → vennDiagram-T6HMQDX7-C5xgCVPR.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-BTH9DrGt.js → wardleyDiagram-T6FBY63Y-B6ued_kM.js} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-CaIgazAx.js → xychartDiagram-ELKLHX3M-CW8ePraJ.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-C8HH961c.js +0 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-C-Ud8JVm.js +0 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-C-Ud8JVm.js +0 -1
- package/dist/web/assets/index-BnkFD0g5.css +0 -1
- package/dist/web/assets/index-Bs2qO5Pu.js +0 -249
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-Dw0_SvNt.js +0 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-DefMsorl.js +0 -8
package/dist/server.js
CHANGED
|
@@ -96,9 +96,7 @@ function agentPrompt() {
|
|
|
96
96
|
const here = path4.dirname(url.fileURLToPath(import.meta.url));
|
|
97
97
|
const candidates = [
|
|
98
98
|
path4.resolve(here, "agents/prompt.txt"),
|
|
99
|
-
// bundled: beside server.js
|
|
100
99
|
path4.resolve(here, "../../../assets/agents/prompt.txt")
|
|
101
|
-
// from source
|
|
102
100
|
];
|
|
103
101
|
const found = candidates.find((c) => fs3.existsSync(c));
|
|
104
102
|
return found ? fs3.readFileSync(found, "utf8").trimEnd() : null;
|
|
@@ -107,9 +105,7 @@ function mcpServerPath() {
|
|
|
107
105
|
const here = path4.dirname(url.fileURLToPath(import.meta.url));
|
|
108
106
|
const candidates = [
|
|
109
107
|
path4.resolve(here, "mcp.js"),
|
|
110
|
-
// bundled: beside server.js
|
|
111
108
|
path4.resolve(here, "../../dist/mcp.js")
|
|
112
|
-
// from source, if built
|
|
113
109
|
];
|
|
114
110
|
return candidates.find((c) => fs3.existsSync(c)) ?? null;
|
|
115
111
|
}
|
|
@@ -314,31 +310,19 @@ var init_config = __esm({
|
|
|
314
310
|
config = {
|
|
315
311
|
host: setting("HOST", "host", "0.0.0.0"),
|
|
316
312
|
port: settingInt("PORT", "port", 4319),
|
|
317
|
-
/** Shared
|
|
313
|
+
/** Shared secret for login. Empty disables auth (dev only). */
|
|
318
314
|
token: setting("CASPER_TOKEN", "token", ""),
|
|
319
|
-
/** Device-login lifetime in seconds
|
|
315
|
+
/** Device-login lifetime in seconds, slid forward on activity. Default 7 days. */
|
|
320
316
|
sessionTtlSeconds: settingInt("CASPER_SESSION_TTL_SECONDS", "sessionTtlSeconds", 60 * 60 * 24 * 7),
|
|
321
317
|
kiroBin: resolveKiroBin(setting("KIRO_BIN", "kiroBin", "kiro-cli"), home),
|
|
322
318
|
defaultCwd: setting("DEFAULT_CWD", "defaultCwd", process.cwd()),
|
|
323
319
|
maxLiveSessions: settingInt("MAX_LIVE_SESSIONS", "maxLiveSessions", 6),
|
|
324
|
-
/**
|
|
325
|
-
*
|
|
326
|
-
* default even when it is missing: kiro-cli acp falls back to kiro_default rather
|
|
327
|
-
* than failing, and reports what it chose, which is what a session records.
|
|
328
|
-
*/
|
|
320
|
+
/** The agent Casper installs, with the widget tools. Falls back to kiro_default
|
|
321
|
+
* if missing rather than failing. */
|
|
329
322
|
defaultAgent: setting("DEFAULT_AGENT", "defaultAgent", "casper"),
|
|
330
|
-
/**
|
|
331
|
-
*
|
|
332
|
-
* resolving outside it is rejected. Defaults to /, so browsing spans everything the process
|
|
333
|
-
* can read - set a narrower path to keep authenticated users out of system files such as /etc
|
|
334
|
-
* or SSH keys.
|
|
335
|
-
*/
|
|
323
|
+
/** Filesystem root the file endpoints are confined to. Defaults to /; narrow it
|
|
324
|
+
* to keep authenticated users out of system files such as /etc or SSH keys. */
|
|
336
325
|
fileRoot: path5.resolve(setting("CASPER_FILE_ROOT", "fileRoot", "/")),
|
|
337
|
-
/**
|
|
338
|
-
* Directory where kiro-cli persists its own session files. Overridable for the
|
|
339
|
-
* same reason as every other path here: a non-default kiro home, a container,
|
|
340
|
-
* or a test that must not write into the real one.
|
|
341
|
-
*/
|
|
342
326
|
kiroSessionsDir: path5.resolve(
|
|
343
327
|
setting(
|
|
344
328
|
"CASPER_KIRO_SESSIONS_DIR",
|
|
@@ -346,16 +330,10 @@ var init_config = __esm({
|
|
|
346
330
|
path5.join(home, ".kiro", "sessions", "cli")
|
|
347
331
|
)
|
|
348
332
|
),
|
|
349
|
-
/** Where casper.db lives. Env-only: it says where data is, and the config
|
|
350
|
-
* file isn't there. */
|
|
351
333
|
casperDataDir: dataDirPath(),
|
|
352
|
-
/** Built web app to serve. Env-only: install layout, not a user preference. */
|
|
353
334
|
webDist: env("CASPER_WEB_DIST", defaultWebDist()),
|
|
354
|
-
/** Per-session in-memory event ring buffer size. */
|
|
355
335
|
eventBufferSize: settingInt("EVENT_BUFFER_SIZE", "eventBufferSize", 5e3),
|
|
356
|
-
/** Max size (bytes) for a single uploaded file. Default 100 MB. */
|
|
357
336
|
maxUploadBytes: settingInt("CASPER_MAX_UPLOAD_BYTES", "maxUploadBytes", 100 * 1024 * 1024),
|
|
358
|
-
/** Absolute path of the settings file, for diagnostics. */
|
|
359
337
|
configFile: configFilePath()
|
|
360
338
|
};
|
|
361
339
|
}
|
|
@@ -414,6 +392,13 @@ CREATE TABLE IF NOT EXISTS message_attachments (
|
|
|
414
392
|
kind TEXT NOT NULL,
|
|
415
393
|
PRIMARY KEY (chat_id, ordinal, path)
|
|
416
394
|
);
|
|
395
|
+
CREATE TABLE IF NOT EXISTS subagent_links (
|
|
396
|
+
child_session_id TEXT PRIMARY KEY,
|
|
397
|
+
parent_session_id TEXT NOT NULL,
|
|
398
|
+
tool_call_id TEXT NOT NULL,
|
|
399
|
+
stage_name TEXT NOT NULL
|
|
400
|
+
);
|
|
401
|
+
CREATE INDEX IF NOT EXISTS subagent_links_parent ON subagent_links (parent_session_id);
|
|
417
402
|
`;
|
|
418
403
|
}
|
|
419
404
|
});
|
|
@@ -451,12 +436,11 @@ var init_logins = __esm({
|
|
|
451
436
|
ttlMs() {
|
|
452
437
|
return config.sessionTtlSeconds * 1e3;
|
|
453
438
|
}
|
|
454
|
-
/** Drop logins whose last activity is older than the TTL. */
|
|
455
439
|
pruneExpired(now = Date.now()) {
|
|
456
440
|
const cutoff = new Date(now - this.ttlMs()).toISOString();
|
|
457
441
|
db().prepare("DELETE FROM logins WHERE last_seen_at < ?").run(cutoff);
|
|
458
442
|
}
|
|
459
|
-
/**
|
|
443
|
+
/** Creates a login. Returns the raw token to set as the cookie value. */
|
|
460
444
|
create(userAgent) {
|
|
461
445
|
const token = randomBytes(32).toString("base64url");
|
|
462
446
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -473,10 +457,7 @@ var init_logins = __esm({
|
|
|
473
457
|
).run(record.id, record.hash, nowIso, nowIso, userAgent ?? null);
|
|
474
458
|
return { token, record };
|
|
475
459
|
}
|
|
476
|
-
/**
|
|
477
|
-
* Verify a raw token. Returns the record (sliding its expiry forward) or null
|
|
478
|
-
* if unknown/expired. Writes the lastSeen bump at most once a minute.
|
|
479
|
-
*/
|
|
460
|
+
/** Verifies a raw token, sliding its expiry forward. Null if unknown or expired. */
|
|
480
461
|
verify(token) {
|
|
481
462
|
if (!token) return null;
|
|
482
463
|
const now = Date.now();
|
|
@@ -493,7 +474,7 @@ var init_logins = __esm({
|
|
|
493
474
|
}
|
|
494
475
|
return record;
|
|
495
476
|
}
|
|
496
|
-
/**
|
|
477
|
+
/** All active devices, marking the one owning `currentToken`. */
|
|
497
478
|
list(currentToken) {
|
|
498
479
|
this.pruneExpired();
|
|
499
480
|
const currentHash = currentToken ? sha256(currentToken) : void 0;
|
|
@@ -506,17 +487,16 @@ var init_logins = __esm({
|
|
|
506
487
|
current: currentHash !== void 0 && sameHash(r.hash, currentHash)
|
|
507
488
|
}));
|
|
508
489
|
}
|
|
509
|
-
/**
|
|
490
|
+
/** Revokes the device holding this token (used on logout). */
|
|
510
491
|
revokeToken(token) {
|
|
511
492
|
if (!token) return;
|
|
512
493
|
db().prepare("DELETE FROM logins WHERE hash = ?").run(sha256(token));
|
|
513
494
|
}
|
|
514
|
-
/**
|
|
495
|
+
/** Revokes a device by its public id. False if there was no such device. */
|
|
515
496
|
revokeId(id) {
|
|
516
497
|
const res = db().prepare("DELETE FROM logins WHERE id = ?").run(id);
|
|
517
498
|
return res.changes > 0;
|
|
518
499
|
}
|
|
519
|
-
/** Log out every device. */
|
|
520
500
|
revokeAll() {
|
|
521
501
|
db().prepare("DELETE FROM logins").run();
|
|
522
502
|
}
|
|
@@ -972,7 +952,6 @@ var init_AcpClient = __esm({
|
|
|
972
952
|
this.emit("serverRequest", msg);
|
|
973
953
|
}
|
|
974
954
|
}
|
|
975
|
-
/** Send a request and await its result. */
|
|
976
955
|
request(method, params, timeoutMs = 12e4) {
|
|
977
956
|
if (this.closed) return Promise.reject(new Error("ACP client is closed"));
|
|
978
957
|
const id = this.nextId++;
|
|
@@ -1002,12 +981,8 @@ var init_AcpClient = __esm({
|
|
|
1002
981
|
write(msg) {
|
|
1003
982
|
this.writable.write(JSON.stringify(msg) + "\n");
|
|
1004
983
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
*
|
|
1007
|
-
* is passed through verbatim because it reaches the user as a turn_error -
|
|
1008
|
-
* the caller supplies something readable, so don't bury it behind a prefix
|
|
1009
|
-
* about ACP internals.
|
|
1010
|
-
*/
|
|
984
|
+
/** Rejects all pending requests, called when the child process exits. The
|
|
985
|
+
* reason passes through verbatim, since it reaches the user as a turn_error. */
|
|
1011
986
|
fail(reason) {
|
|
1012
987
|
this.closed = true;
|
|
1013
988
|
for (const [id, pending] of this.pending) {
|
|
@@ -1088,7 +1063,7 @@ var init_KiroProcess = __esm({
|
|
|
1088
1063
|
this.child.stdout.on("error", () => {
|
|
1089
1064
|
});
|
|
1090
1065
|
}
|
|
1091
|
-
/**
|
|
1066
|
+
/** The ACP initialize handshake. kiro's reply is not used. */
|
|
1092
1067
|
async initialize() {
|
|
1093
1068
|
await this.client.request(ACP_METHODS.initialize, {
|
|
1094
1069
|
protocolVersion: 1,
|
|
@@ -1096,8 +1071,6 @@ var init_KiroProcess = __esm({
|
|
|
1096
1071
|
fs: { readTextFile: false, writeTextFile: false },
|
|
1097
1072
|
terminal: false
|
|
1098
1073
|
},
|
|
1099
|
-
// Informational only - kiro logs it and nothing reads it back, so it isn't
|
|
1100
|
-
// worth wiring to package.json.
|
|
1101
1074
|
clientInfo: { name: "casper", version: "0.5.0" }
|
|
1102
1075
|
});
|
|
1103
1076
|
}
|
|
@@ -1107,7 +1080,7 @@ var init_KiroProcess = __esm({
|
|
|
1107
1080
|
loadSession(params) {
|
|
1108
1081
|
return this.client.request(ACP_METHODS.sessionLoad, params);
|
|
1109
1082
|
}
|
|
1110
|
-
/**
|
|
1083
|
+
/** Runs a prompt turn to completion. */
|
|
1111
1084
|
prompt(params) {
|
|
1112
1085
|
return this.client.request(
|
|
1113
1086
|
ACP_METHODS.sessionPrompt,
|
|
@@ -1130,27 +1103,17 @@ var init_KiroProcess = __esm({
|
|
|
1130
1103
|
command: { command: command.replace(/^\//, ""), args: {} }
|
|
1131
1104
|
});
|
|
1132
1105
|
}
|
|
1133
|
-
/**
|
|
1134
|
-
/**
|
|
1135
|
-
* Recent stderr from kiro, for attaching to a failure.
|
|
1136
|
-
*
|
|
1137
|
-
* A failed request doesn't always carry its cause in the JSON-RPC error - when
|
|
1138
|
-
* it doesn't, kiro has usually written something here. Shared across turns, so
|
|
1139
|
-
* treat it as context rather than proof.
|
|
1140
|
-
*/
|
|
1106
|
+
/** Recent stderr from kiro, for attaching to a failure. */
|
|
1141
1107
|
stderrTail() {
|
|
1142
1108
|
return this.recentStderr.join("\n").trim();
|
|
1143
1109
|
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Why the child died, in terms a user can act on. The exit code alone is
|
|
1146
|
-
* opaque, so append what kiro printed - that's where "credentials have
|
|
1147
|
-
* expired, run kiro-cli login" and friends show up.
|
|
1148
|
-
*/
|
|
1110
|
+
/** Why the child died: exit code plus whatever kiro printed. */
|
|
1149
1111
|
exitReason(code, signal) {
|
|
1150
1112
|
const base = signal ? `kiro-cli exited on ${signal}` : `kiro-cli exited with code ${code}`;
|
|
1151
1113
|
const tail = this.recentStderr.join("\n").trim();
|
|
1152
1114
|
return tail ? `${base}: ${tail}` : base;
|
|
1153
1115
|
}
|
|
1116
|
+
// Closes stdin to trigger kiro's graceful exit, then force-kills if it lingers.
|
|
1154
1117
|
dispose() {
|
|
1155
1118
|
if (this.disposed) return;
|
|
1156
1119
|
this.disposed = true;
|
|
@@ -1162,11 +1125,8 @@ var init_KiroProcess = __esm({
|
|
|
1162
1125
|
if (this.child.exitCode === null) this.child.kill("SIGTERM");
|
|
1163
1126
|
}, 1e3).unref();
|
|
1164
1127
|
}
|
|
1165
|
-
/**
|
|
1166
|
-
*
|
|
1167
|
-
* session file on shutdown, so a caller that then deletes those files must
|
|
1168
|
-
* wait for exit first, or kiro's write recreates them.
|
|
1169
|
-
*/
|
|
1128
|
+
/** Resolves once the child has exited. Caller must wait before deleting
|
|
1129
|
+
* session files, or kiro's shutdown write recreates them. */
|
|
1170
1130
|
disposeAndWait(timeoutMs = 4e3) {
|
|
1171
1131
|
if (this.child.exitCode !== null) {
|
|
1172
1132
|
this.dispose();
|
|
@@ -1205,7 +1165,6 @@ var init_EventStore = __esm({
|
|
|
1205
1165
|
this.chatId = chatId;
|
|
1206
1166
|
this.capacity = config.eventBufferSize;
|
|
1207
1167
|
}
|
|
1208
|
-
/** Append an event, assign it the next seq, and fan it out. */
|
|
1209
1168
|
append(payload) {
|
|
1210
1169
|
this.seq += 1;
|
|
1211
1170
|
const event = {
|
|
@@ -1219,19 +1178,15 @@ var init_EventStore = __esm({
|
|
|
1219
1178
|
this.emit("event", event);
|
|
1220
1179
|
return event;
|
|
1221
1180
|
}
|
|
1222
|
-
/** Highest assigned seq
|
|
1181
|
+
/** Highest assigned seq; clients start their cursor here after a full refetch. */
|
|
1223
1182
|
head() {
|
|
1224
1183
|
return this.seq;
|
|
1225
1184
|
}
|
|
1226
|
-
/** Oldest seq still in the buffer (0 if empty). */
|
|
1227
1185
|
tail() {
|
|
1228
1186
|
return this.buffer.length > 0 ? this.buffer[0].seq : 0;
|
|
1229
1187
|
}
|
|
1230
|
-
/**
|
|
1231
|
-
*
|
|
1232
|
-
* when the cursor is older than the buffer tail, meaning some events were
|
|
1233
|
-
* evicted and the client must resync (full transcript refetch).
|
|
1234
|
-
*/
|
|
1188
|
+
/** Events with seq > cursor. `gap` is true when the cursor is older than the
|
|
1189
|
+
* buffer tail, meaning evicted events the client must resync for instead. */
|
|
1235
1190
|
getSince(cursor) {
|
|
1236
1191
|
if (this.buffer.length === 0) {
|
|
1237
1192
|
return { events: [], gap: cursor > 0 };
|
|
@@ -1294,6 +1249,153 @@ var init_TurnState = __esm({
|
|
|
1294
1249
|
}
|
|
1295
1250
|
});
|
|
1296
1251
|
|
|
1252
|
+
// server/src/session/SubagentTracker.ts
|
|
1253
|
+
var SubagentTracker;
|
|
1254
|
+
var init_SubagentTracker = __esm({
|
|
1255
|
+
"server/src/session/SubagentTracker.ts"() {
|
|
1256
|
+
"use strict";
|
|
1257
|
+
SubagentTracker = class {
|
|
1258
|
+
byChild = /* @__PURE__ */ new Map();
|
|
1259
|
+
pendingCalls = [];
|
|
1260
|
+
callStarted(toolCallId, stages) {
|
|
1261
|
+
this.pendingCalls.push({ toolCallId, stages });
|
|
1262
|
+
}
|
|
1263
|
+
callFinished(toolCallId) {
|
|
1264
|
+
const i = this.pendingCalls.findIndex((c) => c.toolCallId === toolCallId);
|
|
1265
|
+
if (i >= 0) this.pendingCalls.splice(i, 1);
|
|
1266
|
+
}
|
|
1267
|
+
callFor(stageName) {
|
|
1268
|
+
for (let i = this.pendingCalls.length - 1; i >= 0; i--) {
|
|
1269
|
+
if (this.pendingCalls[i].stages.some((s) => s.name === stageName)) return this.pendingCalls[i];
|
|
1270
|
+
}
|
|
1271
|
+
return void 0;
|
|
1272
|
+
}
|
|
1273
|
+
/** Folds a list_update into the tracked state, returning links newly resolved
|
|
1274
|
+
* this call, so the caller persists each one once. */
|
|
1275
|
+
apply(subagents, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1276
|
+
const resolved = [];
|
|
1277
|
+
for (const info of subagents) {
|
|
1278
|
+
const existing = this.byChild.get(info.sessionId);
|
|
1279
|
+
const nowIso = now();
|
|
1280
|
+
const toolCallId = existing?.toolCallId ?? this.callFor(info.sessionName)?.toolCallId;
|
|
1281
|
+
if (toolCallId && !existing?.toolCallId) {
|
|
1282
|
+
resolved.push({ sessionId: info.sessionId, toolCallId, stageName: info.sessionName });
|
|
1283
|
+
}
|
|
1284
|
+
this.byChild.set(info.sessionId, {
|
|
1285
|
+
sessionId: info.sessionId,
|
|
1286
|
+
stageName: info.sessionName,
|
|
1287
|
+
toolCallId,
|
|
1288
|
+
status: info.status.type === "terminated" ? "completed" : "working",
|
|
1289
|
+
activity: info.status.message,
|
|
1290
|
+
createdAt: existing?.createdAt ?? nowIso,
|
|
1291
|
+
updatedAt: nowIso
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
return resolved;
|
|
1295
|
+
}
|
|
1296
|
+
/** Started stages plus a synthetic 'pending' row for each declared stage that
|
|
1297
|
+
* hasn't started, so a stage waiting on a dependency still shows up. */
|
|
1298
|
+
listForCall(toolCallId) {
|
|
1299
|
+
const call = this.pendingCalls.find((c) => c.toolCallId === toolCallId);
|
|
1300
|
+
const started = [...this.byChild.values()].filter((s) => s.toolCallId === toolCallId);
|
|
1301
|
+
if (!call) return started.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
1302
|
+
const startedNames = new Set(started.map((s) => s.stageName));
|
|
1303
|
+
const pending = call.stages.filter((s) => !startedNames.has(s.name)).map((s) => ({
|
|
1304
|
+
sessionId: `pending:${toolCallId}:${s.name}`,
|
|
1305
|
+
stageName: s.name,
|
|
1306
|
+
toolCallId,
|
|
1307
|
+
status: "pending",
|
|
1308
|
+
activity: s.depends_on?.length ? `Waits for ${s.depends_on.join(" and ")}` : void 0,
|
|
1309
|
+
createdAt: "",
|
|
1310
|
+
updatedAt: ""
|
|
1311
|
+
}));
|
|
1312
|
+
return [...started.sort((a, b) => a.createdAt.localeCompare(b.createdAt)), ...pending];
|
|
1313
|
+
}
|
|
1314
|
+
list() {
|
|
1315
|
+
return [...this.byChild.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
1316
|
+
}
|
|
1317
|
+
get(sessionId) {
|
|
1318
|
+
return this.byChild.get(sessionId);
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
// server/src/session/subagentResult.ts
|
|
1325
|
+
function showSummaryAsAnswer(transcript) {
|
|
1326
|
+
const out = [];
|
|
1327
|
+
for (const item of transcript) {
|
|
1328
|
+
if (item.type === "message" && item.message.role === "assistant" && item.message.text.trim() === INTERRUPTED) {
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
if (item.type === "tool_call" && item.tool.name === "summary") {
|
|
1332
|
+
const input = item.tool.input;
|
|
1333
|
+
const result = typeof input?.taskResult === "string" ? input.taskResult.trim() : "";
|
|
1334
|
+
if (result) out.push({ type: "message", message: { id: item.tool.id, role: "assistant", text: result } });
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
out.push(item);
|
|
1338
|
+
}
|
|
1339
|
+
return out;
|
|
1340
|
+
}
|
|
1341
|
+
var INTERRUPTED;
|
|
1342
|
+
var init_subagentResult = __esm({
|
|
1343
|
+
"server/src/session/subagentResult.ts"() {
|
|
1344
|
+
"use strict";
|
|
1345
|
+
INTERRUPTED = "Tool uses were interrupted, waiting for the next user prompt";
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
// server/src/session/subagentFallbackMatch.ts
|
|
1350
|
+
function isStageInput(v) {
|
|
1351
|
+
return typeof v === "object" && v !== null && typeof v.name === "string";
|
|
1352
|
+
}
|
|
1353
|
+
function subagentCallInput(tool) {
|
|
1354
|
+
if (tool.name !== "subagent") return null;
|
|
1355
|
+
const input = tool.input;
|
|
1356
|
+
if (!input || !Array.isArray(input.stages)) return null;
|
|
1357
|
+
const stages = input.stages.filter(isStageInput);
|
|
1358
|
+
return { task: typeof input.task === "string" ? input.task : void 0, stages };
|
|
1359
|
+
}
|
|
1360
|
+
function expectedTitle(call, stage) {
|
|
1361
|
+
const task = call.task ?? "";
|
|
1362
|
+
const expanded = stage.prompt_template ? stage.prompt_template.replace("{task}", task) : task;
|
|
1363
|
+
return expanded.slice(0, TITLE_MAX_LENGTH);
|
|
1364
|
+
}
|
|
1365
|
+
function subagentCallsIn(transcript) {
|
|
1366
|
+
const calls = [];
|
|
1367
|
+
transcript.forEach((item, itemIndex) => {
|
|
1368
|
+
if (item.type !== "tool_call") return;
|
|
1369
|
+
const input = subagentCallInput(item.tool);
|
|
1370
|
+
if (!input) return;
|
|
1371
|
+
calls.push({ toolCallId: item.tool.id, itemIndex, input });
|
|
1372
|
+
});
|
|
1373
|
+
return calls;
|
|
1374
|
+
}
|
|
1375
|
+
function matchSubagentsFallback(calls, children) {
|
|
1376
|
+
const sortedCalls = [...calls].sort((a, b) => a.itemIndex - b.itemIndex);
|
|
1377
|
+
const unclaimed = [...children].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
1378
|
+
const matches = [];
|
|
1379
|
+
for (const call of sortedCalls) {
|
|
1380
|
+
for (const stage of call.input.stages) {
|
|
1381
|
+
const want = expectedTitle(call.input, stage);
|
|
1382
|
+
if (!want) continue;
|
|
1383
|
+
const i = unclaimed.findIndex((c) => want.startsWith(c.title) || c.title.startsWith(want));
|
|
1384
|
+
if (i < 0) continue;
|
|
1385
|
+
const child = unclaimed.splice(i, 1)[0];
|
|
1386
|
+
matches.push({ sessionId: child.sessionId, toolCallId: call.toolCallId, stageName: stage.name });
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return matches;
|
|
1390
|
+
}
|
|
1391
|
+
var TITLE_MAX_LENGTH;
|
|
1392
|
+
var init_subagentFallbackMatch = __esm({
|
|
1393
|
+
"server/src/session/subagentFallbackMatch.ts"() {
|
|
1394
|
+
"use strict";
|
|
1395
|
+
TITLE_MAX_LENGTH = 150;
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1297
1399
|
// server/src/session/kiroFiles.ts
|
|
1298
1400
|
import fs9 from "node:fs/promises";
|
|
1299
1401
|
import path10 from "node:path";
|
|
@@ -1329,7 +1431,6 @@ function summarize(j) {
|
|
|
1329
1431
|
const contextUsagePercentage = state?.rts_model_state?.context_usage_percentage ?? turns[turns.length - 1]?.context_usage_percentage;
|
|
1330
1432
|
return {
|
|
1331
1433
|
sessionId: j.session_id,
|
|
1332
|
-
// Left empty when kiro has not named it; resolveSessionTitle decides what to show.
|
|
1333
1434
|
title: j.title?.trim() ?? "",
|
|
1334
1435
|
cwd: j.cwd,
|
|
1335
1436
|
createdAt: j.created_at,
|
|
@@ -1362,6 +1463,29 @@ async function readPersistedSession(sessionId) {
|
|
|
1362
1463
|
return null;
|
|
1363
1464
|
}
|
|
1364
1465
|
}
|
|
1466
|
+
async function listChildSessions(parentSessionId) {
|
|
1467
|
+
if (!isValidSessionId(parentSessionId)) return [];
|
|
1468
|
+
let names;
|
|
1469
|
+
try {
|
|
1470
|
+
names = await fs9.readdir(config.kiroSessionsDir);
|
|
1471
|
+
} catch {
|
|
1472
|
+
return [];
|
|
1473
|
+
}
|
|
1474
|
+
const out = [];
|
|
1475
|
+
await Promise.all(
|
|
1476
|
+
names.filter((n) => n.endsWith(".json")).map(async (n) => {
|
|
1477
|
+
try {
|
|
1478
|
+
const raw = await fs9.readFile(path10.join(config.kiroSessionsDir, n), "utf8");
|
|
1479
|
+
const j = JSON.parse(raw);
|
|
1480
|
+
if (j.parent_session_id === parentSessionId) {
|
|
1481
|
+
out.push({ ...summarize(j), parentSessionId });
|
|
1482
|
+
}
|
|
1483
|
+
} catch {
|
|
1484
|
+
}
|
|
1485
|
+
})
|
|
1486
|
+
);
|
|
1487
|
+
return out;
|
|
1488
|
+
}
|
|
1365
1489
|
async function promptCount(sessionId) {
|
|
1366
1490
|
const entries = await readJsonlEntries(sessionId);
|
|
1367
1491
|
return entries.filter((e) => e.kind === "Prompt").length;
|
|
@@ -1466,7 +1590,6 @@ var init_chatStore = __esm({
|
|
|
1466
1590
|
"use strict";
|
|
1467
1591
|
init_db();
|
|
1468
1592
|
ChatStore = class {
|
|
1469
|
-
/** Every chat. Ordering is kiro's business, applied once the file is joined on. */
|
|
1470
1593
|
all() {
|
|
1471
1594
|
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 }));
|
|
1472
1595
|
}
|
|
@@ -1477,10 +1600,8 @@ var init_chatStore = __esm({
|
|
|
1477
1600
|
create(chatId) {
|
|
1478
1601
|
db().prepare("INSERT INTO chats (chat_id) VALUES (?) ON CONFLICT(chat_id) DO NOTHING").run(chatId);
|
|
1479
1602
|
}
|
|
1480
|
-
/**
|
|
1481
|
-
*
|
|
1482
|
-
* belongs to exactly one chat, so any earlier claim on it is released.
|
|
1483
|
-
*/
|
|
1603
|
+
/** Binds the session kiro named once it started one. A session belongs to
|
|
1604
|
+
* exactly one chat, so any earlier claim on it is released. */
|
|
1484
1605
|
bindSession(chatId, sessionId) {
|
|
1485
1606
|
db().prepare("UPDATE chats SET session_id = NULL WHERE session_id = ? AND chat_id <> ?").run(sessionId, chatId);
|
|
1486
1607
|
this.write(chatId, "session_id", sessionId);
|
|
@@ -1501,7 +1622,7 @@ var init_chatStore = __esm({
|
|
|
1501
1622
|
setCwd(chatId, cwd) {
|
|
1502
1623
|
this.write(chatId, "cwd", cwd);
|
|
1503
1624
|
}
|
|
1504
|
-
/**
|
|
1625
|
+
/** What was attached to one prompt, keyed by ordinal (see db.ts). */
|
|
1505
1626
|
setAttachments(chatId, ordinal, files) {
|
|
1506
1627
|
if (files.length === 0) return;
|
|
1507
1628
|
const stmt = db().prepare(
|
|
@@ -1550,6 +1671,38 @@ var init_chatStore = __esm({
|
|
|
1550
1671
|
}
|
|
1551
1672
|
});
|
|
1552
1673
|
|
|
1674
|
+
// server/src/session/subagentLinks.ts
|
|
1675
|
+
var SubagentLinkStore;
|
|
1676
|
+
var init_subagentLinks = __esm({
|
|
1677
|
+
"server/src/session/subagentLinks.ts"() {
|
|
1678
|
+
"use strict";
|
|
1679
|
+
init_db();
|
|
1680
|
+
SubagentLinkStore = class {
|
|
1681
|
+
/** Record (or correct) one child's link. Idempotent: a repeat call with the same
|
|
1682
|
+
* values is a no-op, and the row always reflects the latest call to resolve it. */
|
|
1683
|
+
record(childSessionId, parentSessionId, toolCallId, stageName) {
|
|
1684
|
+
db().prepare(
|
|
1685
|
+
`INSERT INTO subagent_links (child_session_id, parent_session_id, tool_call_id, stage_name)
|
|
1686
|
+
VALUES (?, ?, ?, ?)
|
|
1687
|
+
ON CONFLICT(child_session_id) DO UPDATE SET
|
|
1688
|
+
parent_session_id = excluded.parent_session_id,
|
|
1689
|
+
tool_call_id = excluded.tool_call_id,
|
|
1690
|
+
stage_name = excluded.stage_name`
|
|
1691
|
+
).run(childSessionId, parentSessionId, toolCallId, stageName);
|
|
1692
|
+
}
|
|
1693
|
+
/** Every link recorded for one parent session's children, keyed by child session id. */
|
|
1694
|
+
forParent(parentSessionId) {
|
|
1695
|
+
const rows = db().prepare(
|
|
1696
|
+
"SELECT child_session_id, tool_call_id, stage_name FROM subagent_links WHERE parent_session_id = ?"
|
|
1697
|
+
).all(parentSessionId);
|
|
1698
|
+
const out = /* @__PURE__ */ new Map();
|
|
1699
|
+
for (const r of rows) out.set(r.child_session_id, { toolCallId: r.tool_call_id, stageName: r.stage_name });
|
|
1700
|
+
return out;
|
|
1701
|
+
}
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1553
1706
|
// server/src/session/SessionManager.ts
|
|
1554
1707
|
import fs10 from "node:fs";
|
|
1555
1708
|
import path11 from "node:path";
|
|
@@ -1585,6 +1738,22 @@ function mapNotification(n) {
|
|
|
1585
1738
|
return null;
|
|
1586
1739
|
}
|
|
1587
1740
|
}
|
|
1741
|
+
function notificationSessionId(n) {
|
|
1742
|
+
const params = n.params;
|
|
1743
|
+
return params?.sessionId;
|
|
1744
|
+
}
|
|
1745
|
+
function subagentCallStages(update) {
|
|
1746
|
+
if (update.sessionUpdate !== "tool_call") return null;
|
|
1747
|
+
const meta = update._meta;
|
|
1748
|
+
if (meta?.kiro?.toolName !== "subagent") return null;
|
|
1749
|
+
const toolCallId = update.toolCallId;
|
|
1750
|
+
const rawInput = update.rawInput;
|
|
1751
|
+
if (typeof toolCallId !== "string" || !Array.isArray(rawInput?.stages)) return null;
|
|
1752
|
+
const stages = rawInput.stages.filter(
|
|
1753
|
+
(s) => typeof s === "object" && s !== null && typeof s.name === "string"
|
|
1754
|
+
);
|
|
1755
|
+
return { toolCallId, stages };
|
|
1756
|
+
}
|
|
1588
1757
|
function newerOf(a, b) {
|
|
1589
1758
|
return a && a.localeCompare(b) > 0 ? a : b;
|
|
1590
1759
|
}
|
|
@@ -1607,38 +1776,34 @@ var init_SessionManager = __esm({
|
|
|
1607
1776
|
init_KiroProcess();
|
|
1608
1777
|
init_EventStore();
|
|
1609
1778
|
init_TurnState();
|
|
1779
|
+
init_SubagentTracker();
|
|
1780
|
+
init_subagentResult();
|
|
1781
|
+
init_subagentFallbackMatch();
|
|
1610
1782
|
init_kiroFiles();
|
|
1611
1783
|
init_chatStore();
|
|
1784
|
+
init_subagentLinks();
|
|
1612
1785
|
Session = class {
|
|
1613
1786
|
sessionId;
|
|
1614
1787
|
store;
|
|
1615
1788
|
turnState = new TurnState();
|
|
1789
|
+
subagents = new SubagentTracker();
|
|
1616
1790
|
cwd;
|
|
1617
1791
|
agentId;
|
|
1618
1792
|
modelId;
|
|
1619
1793
|
currentModeId;
|
|
1620
1794
|
availableModes = [];
|
|
1621
|
-
/** Empty until something names it; resolveSessionTitle owns the fallback. */
|
|
1622
1795
|
title = "";
|
|
1623
1796
|
createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1624
1797
|
updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1625
1798
|
lastActivity = Date.now();
|
|
1626
1799
|
running = false;
|
|
1627
|
-
// True once kiro has created or loaded this session id.
|
|
1628
1800
|
everLive = false;
|
|
1629
1801
|
proc;
|
|
1630
|
-
// In-flight spawn, so concurrent actions share one process.
|
|
1631
1802
|
spawning;
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
* replacement is ready. Claimed synchronously with the reload's guard, so an
|
|
1635
|
-
* action arriving during one waits for the new process instead of acting on the
|
|
1636
|
-
* one about to be disposed.
|
|
1637
|
-
*/
|
|
1803
|
+
// An action during a reload waits on this instead of acting on the process
|
|
1804
|
+
// about to be disposed.
|
|
1638
1805
|
reloading;
|
|
1639
|
-
// True while kiro
|
|
1640
|
-
// already hydrated from disk, so replayed notifications must not be appended
|
|
1641
|
-
// to the live store (doing so floods the chat with duplicate tool calls).
|
|
1806
|
+
// True while kiro replays history during session/load.
|
|
1642
1807
|
replaying = false;
|
|
1643
1808
|
constructor(sessionId, store, cwd) {
|
|
1644
1809
|
this.sessionId = sessionId;
|
|
@@ -1651,12 +1816,7 @@ var init_SessionManager = __esm({
|
|
|
1651
1816
|
get hasBeenLive() {
|
|
1652
1817
|
return this.everLive;
|
|
1653
1818
|
}
|
|
1654
|
-
|
|
1655
|
-
* Single append path: fold the event into the live snapshot AND persist/fan
|
|
1656
|
-
* it out. Using this everywhere keeps turnState in sync with the event log,
|
|
1657
|
-
* so a client that refetches mid-turn sees turnStatus 'running' rather than
|
|
1658
|
-
* a stale 'idle'.
|
|
1659
|
-
*/
|
|
1819
|
+
// Folds the event into the live snapshot and persists it in one call.
|
|
1660
1820
|
record(payload) {
|
|
1661
1821
|
this.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1662
1822
|
this.turnState.apply(payload);
|
|
@@ -1668,15 +1828,13 @@ var init_SessionManager = __esm({
|
|
|
1668
1828
|
sessions = /* @__PURE__ */ new Map();
|
|
1669
1829
|
log;
|
|
1670
1830
|
store = new ChatStore();
|
|
1831
|
+
subagentLinks = new SubagentLinkStore();
|
|
1671
1832
|
spawnProcess;
|
|
1672
1833
|
constructor(log, opts = {}) {
|
|
1673
1834
|
this.log = log;
|
|
1674
1835
|
this.spawnProcess = opts.spawn ?? ((o, l) => new KiroProcess(o, l));
|
|
1675
1836
|
}
|
|
1676
|
-
/**
|
|
1677
|
-
* What a session is called, from every read path. The folder is skipped for a workspace of
|
|
1678
|
-
* ours, whose name is a uuid.
|
|
1679
|
-
*/
|
|
1837
|
+
/** What a session is called, from every read path. */
|
|
1680
1838
|
titleOf(chat, parts) {
|
|
1681
1839
|
return resolveSessionTitle({
|
|
1682
1840
|
override: chat.title ?? void 0,
|
|
@@ -1691,10 +1849,8 @@ var init_SessionManager = __esm({
|
|
|
1691
1849
|
const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
|
|
1692
1850
|
if (s) s.title = clean || s.title;
|
|
1693
1851
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
* old cwd, so it is disposed and the next turn respawns in the new one, transcript intact.
|
|
1697
|
-
*/
|
|
1852
|
+
// A live process was spawned with the old cwd, so it's disposed and the next
|
|
1853
|
+
// turn respawns.
|
|
1698
1854
|
async setChatCwd(chatId, input) {
|
|
1699
1855
|
const resolved = resolveCwd(input);
|
|
1700
1856
|
const s = await this.ensureOpen(chatId);
|
|
@@ -1716,15 +1872,8 @@ var init_SessionManager = __esm({
|
|
|
1716
1872
|
}
|
|
1717
1873
|
return resolved;
|
|
1718
1874
|
}
|
|
1719
|
-
/**
|
|
1720
|
-
*
|
|
1721
|
-
* agent definition, the workspace's `.kiro`, and the MCP servers kiro launches itself.
|
|
1722
|
-
* Replacing the process is the only way - no ACP method does it, and `session/set_mode`
|
|
1723
|
-
* leaves those MCP servers alone.
|
|
1724
|
-
*
|
|
1725
|
-
* The old child must be awaited out first: kiro flushes its session file on shutdown and
|
|
1726
|
-
* the replacement reads that file to reload the conversation.
|
|
1727
|
-
*/
|
|
1875
|
+
/** Restarts the session's kiro child so startup-only state (agent definition,
|
|
1876
|
+
* workspace `.kiro`, MCP servers) is read again. */
|
|
1728
1877
|
async reloadChat(chatId) {
|
|
1729
1878
|
const s = await this.ensureOpen(chatId);
|
|
1730
1879
|
if (s.running) {
|
|
@@ -1747,7 +1896,7 @@ var init_SessionManager = __esm({
|
|
|
1747
1896
|
ready();
|
|
1748
1897
|
}
|
|
1749
1898
|
}
|
|
1750
|
-
/** The reload itself
|
|
1899
|
+
/** The reload itself, called with the session's reload claim held. */
|
|
1751
1900
|
async replaceProcess(chatId, s) {
|
|
1752
1901
|
if (!await hasRecordedTurns(s.sessionId)) {
|
|
1753
1902
|
throw new Error(
|
|
@@ -1769,10 +1918,6 @@ var init_SessionManager = __esm({
|
|
|
1769
1918
|
this.log.info({ sessionId: s.sessionId, cwd: s.cwd }, "session reloaded");
|
|
1770
1919
|
return this.getDetail(chatId);
|
|
1771
1920
|
}
|
|
1772
|
-
/**
|
|
1773
|
-
* Wait out a reload before touching the session's process. Without this an action
|
|
1774
|
-
* can be handed the process that the reload is about to dispose.
|
|
1775
|
-
*/
|
|
1776
1921
|
async settleReload(s) {
|
|
1777
1922
|
if (s.reloading) await s.reloading;
|
|
1778
1923
|
}
|
|
@@ -1781,9 +1926,6 @@ var init_SessionManager = __esm({
|
|
|
1781
1926
|
for (const s of this.sessions.values()) if (s.proc) n++;
|
|
1782
1927
|
return n;
|
|
1783
1928
|
}
|
|
1784
|
-
// -------------------------------------------------------------------------
|
|
1785
|
-
// Event subscription - works for any opened session, spawned or not.
|
|
1786
|
-
// -------------------------------------------------------------------------
|
|
1787
1929
|
onEvent(chatId, cb) {
|
|
1788
1930
|
const s = this.sessions.get(this.store.sessionIdForChat(chatId) ?? "");
|
|
1789
1931
|
if (!s) return null;
|
|
@@ -1793,12 +1935,12 @@ var init_SessionManager = __esm({
|
|
|
1793
1935
|
getStore(chatId) {
|
|
1794
1936
|
return this.sessions.get(this.store.sessionIdForChat(chatId) ?? "")?.store;
|
|
1795
1937
|
}
|
|
1796
|
-
/** A chat's working directory
|
|
1938
|
+
/** A chat's working directory, opened in memory if needed. */
|
|
1797
1939
|
async getChatCwd(chatId) {
|
|
1798
1940
|
const s = await this.ensureOpen(chatId);
|
|
1799
1941
|
return s.cwd;
|
|
1800
1942
|
}
|
|
1801
|
-
/**
|
|
1943
|
+
/** Opens a chat's session in memory without spawning a process. */
|
|
1802
1944
|
async ensureOpen(chatId) {
|
|
1803
1945
|
const sessionId = this.sessionIdOf(chatId);
|
|
1804
1946
|
const existing = this.sessions.get(sessionId);
|
|
@@ -1824,15 +1966,33 @@ var init_SessionManager = __esm({
|
|
|
1824
1966
|
this.sessions.set(sessionId, s);
|
|
1825
1967
|
return s;
|
|
1826
1968
|
}
|
|
1827
|
-
// -------------------------------------------------------------------------
|
|
1828
|
-
// Lazy process spawning
|
|
1829
|
-
// -------------------------------------------------------------------------
|
|
1830
1969
|
wire(s, proc) {
|
|
1831
1970
|
proc.on("notification", (n) => {
|
|
1832
1971
|
if (s.replaying) return;
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1972
|
+
if (n.method === KIRO_NOTIFICATIONS.subagentListUpdate) {
|
|
1973
|
+
const params = n.params;
|
|
1974
|
+
const resolved = s.subagents.apply(params.subagents);
|
|
1975
|
+
for (const link of resolved) {
|
|
1976
|
+
this.subagentLinks.record(link.sessionId, s.sessionId, link.toolCallId, link.stageName);
|
|
1977
|
+
}
|
|
1978
|
+
s.record({ kind: "subagents_changed", subagents: s.subagents.list() });
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
const notifSessionId = notificationSessionId(n);
|
|
1982
|
+
if (!notifSessionId || notifSessionId === s.sessionId) {
|
|
1983
|
+
const payload = mapNotification(n);
|
|
1984
|
+
if (!payload) return;
|
|
1985
|
+
if (payload.kind === "session_update") {
|
|
1986
|
+
const call = subagentCallStages(payload.update);
|
|
1987
|
+
if (call) s.subagents.callStarted(call.toolCallId, call.stages);
|
|
1988
|
+
const update = payload.update;
|
|
1989
|
+
if (update.sessionUpdate === "tool_call_update" && typeof update.toolCallId === "string" && (update.status === "completed" || update.status === "failed")) {
|
|
1990
|
+
s.subagents.callFinished(update.toolCallId);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
s.record(payload);
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1836
1996
|
});
|
|
1837
1997
|
proc.on("exit", (code, signal) => {
|
|
1838
1998
|
if (s.proc !== proc) return;
|
|
@@ -1841,7 +2001,7 @@ var init_SessionManager = __esm({
|
|
|
1841
2001
|
s.running = false;
|
|
1842
2002
|
});
|
|
1843
2003
|
}
|
|
1844
|
-
/**
|
|
2004
|
+
/** Gets or spawns the kiro process for a session. */
|
|
1845
2005
|
async ensureProc(s) {
|
|
1846
2006
|
if (s.proc) return s.proc;
|
|
1847
2007
|
if (s.spawning) return s.spawning;
|
|
@@ -1903,10 +2063,7 @@ var init_SessionManager = __esm({
|
|
|
1903
2063
|
this.log.warn("at capacity but all processes are busy");
|
|
1904
2064
|
}
|
|
1905
2065
|
}
|
|
1906
|
-
|
|
1907
|
-
// Creating / opening
|
|
1908
|
-
// -------------------------------------------------------------------------
|
|
1909
|
-
/** Create a new session. Spawns immediately so we get a real kiro sessionId. */
|
|
2066
|
+
/** Creates a new session. Spawns immediately to get a real kiro sessionId. */
|
|
1910
2067
|
async createChat(opts) {
|
|
1911
2068
|
const chatId = isValidChatId(opts.chatId) ? opts.chatId : crypto.randomUUID();
|
|
1912
2069
|
const cwd = opts.freshWorkspace ? createChatWorkspace(chatId) : resolveCwd(opts.cwd);
|
|
@@ -1932,9 +2089,6 @@ var init_SessionManager = __esm({
|
|
|
1932
2089
|
}
|
|
1933
2090
|
return this.buildDetail(this.store.get(chatId), s, []);
|
|
1934
2091
|
}
|
|
1935
|
-
// -------------------------------------------------------------------------
|
|
1936
|
-
// Actions - these spawn the process lazily.
|
|
1937
|
-
// -------------------------------------------------------------------------
|
|
1938
2092
|
async runPrompt(chatId, content, attachments) {
|
|
1939
2093
|
const s = await this.ensureOpen(chatId);
|
|
1940
2094
|
await this.settleReload(s);
|
|
@@ -2003,14 +2157,8 @@ ${tail}` : err.message;
|
|
|
2003
2157
|
await proc.execCommand(s.sessionId, command);
|
|
2004
2158
|
s.lastActivity = Date.now();
|
|
2005
2159
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
// -------------------------------------------------------------------------
|
|
2009
|
-
/**
|
|
2010
|
-
* The one place a ChatSummary is assembled: kiro's file and Casper's live state each hold
|
|
2011
|
-
* part of the truth, so every read path projects them through here. `persisted` is absent for
|
|
2012
|
-
* a session with no file yet, `live` for a dormant one, and callers guarantee one of them.
|
|
2013
|
-
*/
|
|
2160
|
+
/** The one place a ChatSummary is assembled, since kiro's file and Casper's
|
|
2161
|
+
* live state each hold part of the truth. */
|
|
2014
2162
|
summaryOf(chat, live, persisted, transcript) {
|
|
2015
2163
|
const snap = live?.turnState.get();
|
|
2016
2164
|
const cwd = live?.cwd ?? chat.cwd ?? persisted?.cwd ?? config.defaultCwd;
|
|
@@ -2018,21 +2166,18 @@ ${tail}` : err.message;
|
|
|
2018
2166
|
chatId: chat.chatId,
|
|
2019
2167
|
sessionId: live?.sessionId ?? chat.sessionId ?? void 0,
|
|
2020
2168
|
title: this.titleOf(chat, {
|
|
2021
|
-
// kiro's file is what kiro called it; the live copy is only a cache of it.
|
|
2022
2169
|
kiroTitle: persisted?.title || live?.title,
|
|
2023
2170
|
firstPrompt: transcript && firstPromptText(transcript),
|
|
2024
2171
|
cwd
|
|
2025
2172
|
}),
|
|
2026
2173
|
cwd,
|
|
2027
2174
|
createdAt: persisted?.createdAt ?? live?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2028
|
-
// kiro's file and our own activity move separately.
|
|
2029
2175
|
updatedAt: live ? newerOf(persisted?.updatedAt, live.updatedAt) : persisted?.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2030
2176
|
liveness: live?.proc ? "live" : "dormant",
|
|
2031
2177
|
agentId: live?.agentId ?? persisted?.agentId,
|
|
2032
2178
|
modelId: live?.modelId ?? persisted?.modelId,
|
|
2033
2179
|
running: live?.running ?? false,
|
|
2034
|
-
// A
|
|
2035
|
-
// session's value comes from kiro's file.
|
|
2180
|
+
// A dormant session falls back to kiro's file.
|
|
2036
2181
|
contextUsagePercentage: snap?.contextUsagePercentage || persisted?.contextUsagePercentage
|
|
2037
2182
|
};
|
|
2038
2183
|
}
|
|
@@ -2072,11 +2217,8 @@ ${tail}` : err.message;
|
|
|
2072
2217
|
if (!s && !persisted) throw new Error(`Unknown chat: ${chatId}`);
|
|
2073
2218
|
return this.buildDetail(chat, s, transcript, persisted ?? void 0);
|
|
2074
2219
|
}
|
|
2075
|
-
/**
|
|
2076
|
-
*
|
|
2077
|
-
* from disk (fast: ~150ms even for a multi-MB session) and returns the
|
|
2078
|
-
* requested window. offset/limit are clamped to the transcript bounds.
|
|
2079
|
-
*/
|
|
2220
|
+
/** A transcript slice for lazy "load older on scroll up". offset/limit are
|
|
2221
|
+
* clamped to the transcript bounds. */
|
|
2080
2222
|
async getTranscriptPage(chatId, offset, limit) {
|
|
2081
2223
|
const transcript = await hydrateTranscript(
|
|
2082
2224
|
this.sessionIdOf(chatId),
|
|
@@ -2086,13 +2228,58 @@ ${tail}` : err.message;
|
|
|
2086
2228
|
const end = Math.max(start, Math.min(start + limit, transcript.length));
|
|
2087
2229
|
return transcript.slice(start, end);
|
|
2088
2230
|
}
|
|
2231
|
+
/**
|
|
2232
|
+
* A chat's subagents: live ones from the tracker, joined with children found on disk.
|
|
2233
|
+
* A child the tracker never saw falls back to a recorded link or title matching
|
|
2234
|
+
* (subagentFallbackMatch.ts).
|
|
2235
|
+
*/
|
|
2236
|
+
async getSubagents(chatId) {
|
|
2237
|
+
const sessionId = this.sessionIdOf(chatId);
|
|
2238
|
+
const s = this.sessions.get(sessionId);
|
|
2239
|
+
const live = s ? s.subagents.list() : [];
|
|
2240
|
+
const liveToolCalls = new Set(live.map((a) => a.toolCallId).filter((id) => !!id));
|
|
2241
|
+
const pending = s ? [...liveToolCalls].flatMap((id) => s.subagents.listForCall(id).filter((a) => a.status === "pending")) : [];
|
|
2242
|
+
const onDisk = await listChildSessions(sessionId);
|
|
2243
|
+
const links = this.subagentLinks.forParent(sessionId);
|
|
2244
|
+
const liveIds = new Set(live.map((a) => a.sessionId));
|
|
2245
|
+
const unresolved = onDisk.filter((c) => !liveIds.has(c.sessionId) && !links.has(c.sessionId));
|
|
2246
|
+
const fallback = unresolved.length ? matchSubagentsFallback(subagentCallsIn(await hydrateTranscript(sessionId)), unresolved) : [];
|
|
2247
|
+
const fallbackByChild = new Map(fallback.map((m) => [m.sessionId, m]));
|
|
2248
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2249
|
+
for (const child of onDisk) {
|
|
2250
|
+
const link = links.get(child.sessionId) ?? fallbackByChild.get(child.sessionId);
|
|
2251
|
+
byId.set(child.sessionId, {
|
|
2252
|
+
sessionId: child.sessionId,
|
|
2253
|
+
stageName: link?.stageName ?? (child.title || child.sessionId),
|
|
2254
|
+
toolCallId: link?.toolCallId,
|
|
2255
|
+
status: "completed",
|
|
2256
|
+
createdAt: child.createdAt,
|
|
2257
|
+
updatedAt: child.updatedAt
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2260
|
+
for (const a of live) byId.set(a.sessionId, a);
|
|
2261
|
+
const startedThenPending = [...byId.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
2262
|
+
return [...startedThenPending, ...pending];
|
|
2263
|
+
}
|
|
2264
|
+
/** One subagent's own transcript, hydrated the same way a chat's is. */
|
|
2265
|
+
async getSubagentDetail(chatId, childSessionId) {
|
|
2266
|
+
const parentSessionId = this.sessionIdOf(chatId);
|
|
2267
|
+
const subagents = await this.getSubagents(chatId);
|
|
2268
|
+
const summary = subagents.find((a) => a.sessionId === childSessionId);
|
|
2269
|
+
if (!summary) throw new Error(`Unknown subagent: ${childSessionId}`);
|
|
2270
|
+
const onDisk = await listChildSessions(parentSessionId);
|
|
2271
|
+
if (!onDisk.some((c) => c.sessionId === childSessionId)) {
|
|
2272
|
+
throw new Error(`Unknown subagent: ${childSessionId}`);
|
|
2273
|
+
}
|
|
2274
|
+
const transcript = showSummaryAsAnswer(await hydrateTranscript(childSessionId));
|
|
2275
|
+
return { subagent: summary, transcript };
|
|
2276
|
+
}
|
|
2089
2277
|
/** One projection, so a dormant session and a live one cannot disagree. */
|
|
2090
2278
|
buildDetail(chat, s, transcript, persisted) {
|
|
2091
2279
|
return {
|
|
2092
2280
|
summary: this.summaryOf(chat, s, persisted, transcript),
|
|
2093
2281
|
modes: s?.availableModes ?? [],
|
|
2094
2282
|
currentModeId: s?.currentModeId ?? persisted?.agentId,
|
|
2095
|
-
// Only the tail is sent on load; replayHead/title use the full transcript.
|
|
2096
2283
|
transcript: transcript.slice(-TRANSCRIPT_PAGE_SIZE),
|
|
2097
2284
|
transcriptTotal: transcript.length,
|
|
2098
2285
|
observability: s?.turnState.get() ?? {
|
|
@@ -2103,9 +2290,8 @@ ${tail}` : err.message;
|
|
|
2103
2290
|
};
|
|
2104
2291
|
}
|
|
2105
2292
|
/**
|
|
2106
|
-
* The cursor a reconnecting client starts from
|
|
2107
|
-
*
|
|
2108
|
-
* rewind to just before its turn_started and let the socket replay the whole turn.
|
|
2293
|
+
* The cursor a reconnecting client starts from: the store head, unless the in-flight
|
|
2294
|
+
* turn isn't in kiro's jsonl yet, in which case rewind to its turn_started.
|
|
2109
2295
|
*/
|
|
2110
2296
|
replayHead(s, transcript) {
|
|
2111
2297
|
const head = s.store.head();
|
|
@@ -2131,9 +2317,6 @@ ${tail}` : err.message;
|
|
|
2131
2317
|
}
|
|
2132
2318
|
return started.seq - 1;
|
|
2133
2319
|
}
|
|
2134
|
-
// -------------------------------------------------------------------------
|
|
2135
|
-
// Teardown
|
|
2136
|
-
// -------------------------------------------------------------------------
|
|
2137
2320
|
evict(sessionId) {
|
|
2138
2321
|
const s = this.sessions.get(sessionId);
|
|
2139
2322
|
if (!s) return;
|
|
@@ -2141,8 +2324,7 @@ ${tail}` : err.message;
|
|
|
2141
2324
|
s.proc?.dispose();
|
|
2142
2325
|
s.store.dispose();
|
|
2143
2326
|
}
|
|
2144
|
-
//
|
|
2145
|
-
// files and the directory the chat owns, and drop any title override.
|
|
2327
|
+
// Evicts from memory, removes on-disk files and the chat's directory.
|
|
2146
2328
|
async deleteChat(chatId) {
|
|
2147
2329
|
const sessionId = this.store.sessionIdForChat(chatId);
|
|
2148
2330
|
const s = sessionId ? this.sessions.get(sessionId) : void 0;
|
|
@@ -2946,6 +3128,37 @@ var init_chats2 = __esm({
|
|
|
2946
3128
|
}
|
|
2947
3129
|
});
|
|
2948
3130
|
|
|
3131
|
+
// server/src/routes/subagents.ts
|
|
3132
|
+
function registerSubagentRoutes(app, manager) {
|
|
3133
|
+
app.get(
|
|
3134
|
+
"/api/chats/:id/subagents",
|
|
3135
|
+
async (req, reply) => {
|
|
3136
|
+
try {
|
|
3137
|
+
return { subagents: await manager.getSubagents(req.params.id) };
|
|
3138
|
+
} catch (err) {
|
|
3139
|
+
reply.code(404);
|
|
3140
|
+
return { error: err.message };
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
);
|
|
3144
|
+
app.get(
|
|
3145
|
+
"/api/chats/:id/subagents/:subagentId",
|
|
3146
|
+
async (req, reply) => {
|
|
3147
|
+
try {
|
|
3148
|
+
return await manager.getSubagentDetail(req.params.id, req.params.subagentId);
|
|
3149
|
+
} catch (err) {
|
|
3150
|
+
reply.code(404);
|
|
3151
|
+
return { error: err.message };
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
var init_subagents = __esm({
|
|
3157
|
+
"server/src/routes/subagents.ts"() {
|
|
3158
|
+
"use strict";
|
|
3159
|
+
}
|
|
3160
|
+
});
|
|
3161
|
+
|
|
2949
3162
|
// server/src/routes/workspace.ts
|
|
2950
3163
|
import fs14 from "node:fs/promises";
|
|
2951
3164
|
import { createReadStream as createReadStream3 } from "node:fs";
|
|
@@ -3140,9 +3353,6 @@ function registerUploadRoutes(app) {
|
|
|
3140
3353
|
const kind = classifyKind(dest);
|
|
3141
3354
|
const uploaded = {
|
|
3142
3355
|
name: path17.basename(dest),
|
|
3143
|
-
// Absolute, because the file sits outside the session's workspace: a
|
|
3144
|
-
// relative path would resolve against the wrong root for both the agent
|
|
3145
|
-
// and the image endpoint.
|
|
3146
3356
|
path: dest,
|
|
3147
3357
|
size: stat.size,
|
|
3148
3358
|
mimeType: mimeForExt(ext),
|
|
@@ -3421,11 +3631,11 @@ import multipart from "@fastify/multipart";
|
|
|
3421
3631
|
async function buildApp() {
|
|
3422
3632
|
const isProd = process.env.NODE_ENV === "production";
|
|
3423
3633
|
const app = Fastify({
|
|
3424
|
-
//
|
|
3425
|
-
//
|
|
3634
|
+
// Honors X-Forwarded-Proto so secure: 'auto' cookies detect HTTPS behind a
|
|
3635
|
+
// TLS-terminating tunnel or reverse proxy.
|
|
3426
3636
|
trustProxy: true,
|
|
3427
|
-
//
|
|
3428
|
-
//
|
|
3637
|
+
// Skip the two-lines-per-request access log in production; logger.info/warn/error
|
|
3638
|
+
// calls still fire. Dev keeps it for debugging.
|
|
3429
3639
|
disableRequestLogging: isProd,
|
|
3430
3640
|
logger: {
|
|
3431
3641
|
level: process.env.LOG_LEVEL ?? "info",
|
|
@@ -3446,6 +3656,7 @@ async function buildApp() {
|
|
|
3446
3656
|
registerAgentRoutes(app);
|
|
3447
3657
|
registerFsRoutes(app);
|
|
3448
3658
|
registerChatRoutes(app, manager);
|
|
3659
|
+
registerSubagentRoutes(app, manager);
|
|
3449
3660
|
registerWorkspaceRoutes(app, manager);
|
|
3450
3661
|
registerUploadRoutes(app);
|
|
3451
3662
|
registerWsGateway(app, manager);
|
|
@@ -3482,6 +3693,7 @@ var init_app = __esm({
|
|
|
3482
3693
|
init_agents2();
|
|
3483
3694
|
init_fs();
|
|
3484
3695
|
init_chats2();
|
|
3696
|
+
init_subagents();
|
|
3485
3697
|
init_workspace();
|
|
3486
3698
|
init_uploads();
|
|
3487
3699
|
init_health();
|
|
@@ -4112,7 +4324,7 @@ var init_server2 = __esm({
|
|
|
4112
4324
|
"server/src/mcp/server.ts"() {
|
|
4113
4325
|
"use strict";
|
|
4114
4326
|
init_protocol();
|
|
4115
|
-
VERSION = true ? "0.
|
|
4327
|
+
VERSION = true ? "0.15.1" : "dev";
|
|
4116
4328
|
}
|
|
4117
4329
|
});
|
|
4118
4330
|
|
|
@@ -7492,7 +7704,7 @@ var program = new Command();
|
|
|
7492
7704
|
// server/src/cli/program.ts
|
|
7493
7705
|
init_paths();
|
|
7494
7706
|
init_settings();
|
|
7495
|
-
var VERSION2 = true ? "0.
|
|
7707
|
+
var VERSION2 = true ? "0.15.1" : "dev";
|
|
7496
7708
|
async function bootstrap() {
|
|
7497
7709
|
const file = configFilePath();
|
|
7498
7710
|
const settings = readSettings(file);
|