@openacp/cli 2026.327.5 → 2026.330.2
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/README.md +13 -13
- package/dist/adapter-4U6MC5ZS.js +13 -0
- package/dist/agent-catalog-SZQQERV7.js +10 -0
- package/dist/{agent-dependencies-WS7Z2DFW.js → agent-dependencies-ED2ZTUHG.js} +1 -2
- package/dist/{agent-registry-5LZT7CUB.js → agent-registry-YOGP656W.js} +1 -2
- package/dist/agent-store-5UHZH2XI.js +8 -0
- package/dist/{api-client-AQPNKXI2.js → api-client-XTLRRFPX.js} +1 -2
- package/dist/api-server-5VNYFWJE.js +7 -0
- package/dist/api-server-JLBDKCU4.js +10 -0
- package/dist/{autostart-6JS565RY.js → autostart-CUPZMKKC.js} +3 -4
- package/dist/{chunk-WIIZNPCR.js → chunk-2KT6TROD.js} +12 -33
- package/dist/chunk-2KT6TROD.js.map +1 -0
- package/dist/{chunk-PPSMUECX.js → chunk-2R5XM3ES.js} +2 -2
- package/dist/{chunk-SNPYTMPR.js → chunk-3EWTPOF7.js} +2 -2
- package/dist/{chunk-YEULD3SG.js → chunk-3NAFXVQM.js} +7 -2
- package/dist/{chunk-YEULD3SG.js.map → chunk-3NAFXVQM.js.map} +1 -1
- package/dist/{chunk-A6Y4GZM3.js → chunk-566W6INH.js} +2 -2
- package/dist/{chunk-ODUM3D6X.js → chunk-5HKQCYOI.js} +1 -39
- package/dist/chunk-5HKQCYOI.js.map +1 -0
- package/dist/{plugin-installer-QVJP6VKV.js → chunk-5WGVYX3C.js} +18 -5
- package/dist/chunk-5WGVYX3C.js.map +1 -0
- package/dist/{chunk-XWDW3XBE.js → chunk-5ZNBNIK3.js} +1331 -237
- package/dist/chunk-5ZNBNIK3.js.map +1 -0
- package/dist/{chunk-XIBG7LSL.js → chunk-7RKPIM3E.js} +482 -175
- package/dist/chunk-7RKPIM3E.js.map +1 -0
- package/dist/{chunk-WXVT3AOY.js → chunk-7ZCQF6QM.js} +8 -3
- package/dist/chunk-7ZCQF6QM.js.map +1 -0
- package/dist/{chunk-2YCW3QDV.js → chunk-BTJHGSLM.js} +8 -7
- package/dist/chunk-BTJHGSLM.js.map +1 -0
- package/dist/{chunk-P2G275VD.js → chunk-CFUJGWOP.js} +3 -3
- package/dist/{chunk-RBYBSSGO.js → chunk-FCTC7KDT.js} +2 -2
- package/dist/{chunk-QAQDGPB4.js → chunk-GEOXPGCO.js} +3 -3
- package/dist/{chunk-BLQUXO7S.js → chunk-IZ5UEZF7.js} +27 -2
- package/dist/chunk-IZ5UEZF7.js.map +1 -0
- package/dist/{chunk-4GMLGCF2.js → chunk-KDU3ZEWT.js} +2 -2
- package/dist/{chunk-QWP76EBW.js → chunk-MITTQMGZ.js} +16 -9
- package/dist/chunk-MITTQMGZ.js.map +1 -0
- package/dist/{chunk-HRKAXFWR.js → chunk-MPGEHTGE.js} +9 -9
- package/dist/{chunk-KMMEFXIE.js → chunk-PA6MNBG4.js} +41 -9
- package/dist/chunk-PA6MNBG4.js.map +1 -0
- package/dist/{chunk-BQ6FR32N.js → chunk-QWVHCTCA.js} +2 -2
- package/dist/{chunk-XMMAGAT4.js → chunk-R6KZYF7D.js} +8 -1
- package/dist/{chunk-XMMAGAT4.js.map → chunk-R6KZYF7D.js.map} +1 -1
- package/dist/{chunk-AD3X6DGK.js → chunk-TMVTSWVH.js} +75 -13
- package/dist/chunk-TMVTSWVH.js.map +1 -0
- package/dist/chunk-UWH7KIAA.js +701 -0
- package/dist/chunk-UWH7KIAA.js.map +1 -0
- package/dist/{chunk-SHTGQGAU.js → chunk-V2YZWYXT.js} +3 -3
- package/dist/{chunk-QVMEF6FB.js → chunk-W4LK6WJP.js} +38 -27
- package/dist/chunk-W4LK6WJP.js.map +1 -0
- package/dist/{chunk-S3ZGPPXY.js → chunk-YIGBJFJL.js} +9 -13
- package/dist/{chunk-S3ZGPPXY.js.map → chunk-YIGBJFJL.js.map} +1 -1
- package/dist/cli.js +133 -132
- package/dist/cli.js.map +1 -1
- package/dist/config-KN6NKKPF.js +20 -0
- package/dist/config-editor-76RVZS4B.js +10 -0
- package/dist/{config-registry-CUMNXFGK.js → config-registry-ZXAIJNYB.js} +2 -3
- package/dist/context-NXXW62NJ.js +9 -0
- package/dist/core-plugins-OCHKGCIZ.js +22 -0
- package/dist/{daemon-PXO5QPCR.js → daemon-XFEMMJSZ.js} +4 -5
- package/dist/{dev-loader-DRU3R7ZM.js → dev-loader-7P3HZCIA.js} +1 -3
- package/dist/{dev-loader-DRU3R7ZM.js.map → dev-loader-7P3HZCIA.js.map} +1 -1
- package/dist/doctor-AV6AUO22.js +9 -0
- package/dist/file-service-HHB3JQIO.js +8 -0
- package/dist/index.d.ts +141 -187
- package/dist/index.js +33 -29
- package/dist/index.js.map +1 -1
- package/dist/{install-cloudflared-AN24L4DP.js → install-cloudflared-JRJ4BSOM.js} +3 -4
- package/dist/{install-cloudflared-AN24L4DP.js.map → install-cloudflared-JRJ4BSOM.js.map} +1 -1
- package/dist/{install-context-XPWTFT3J.js → install-context-EHYV5WRY.js} +2 -3
- package/dist/{install-context-XPWTFT3J.js.map → install-context-EHYV5WRY.js.map} +1 -1
- package/dist/{install-jq-CRVDJGF3.js → install-jq-ISTGT263.js} +3 -4
- package/dist/{install-jq-CRVDJGF3.js.map → install-jq-ISTGT263.js.map} +1 -1
- package/dist/{integrate-G6CVXTGT.js → integrate-JIEZYDOR.js} +1 -2
- package/dist/{integrate-G6CVXTGT.js.map → integrate-JIEZYDOR.js.map} +1 -1
- package/dist/{log-LZ7FTRKG.js → log-YZ243M5G.js} +4 -3
- package/dist/{main-3GF3EQTE.js → main-L2M4NTJY.js} +135 -50
- package/dist/main-L2M4NTJY.js.map +1 -0
- package/dist/{menu-YDQ2LWAR.js → menu-ALFN37IR.js} +1 -2
- package/dist/notifications-MO23S7S3.js +8 -0
- package/dist/{plugin-create-5HQRF2ID.js → plugin-create-EHL76ZZG.js} +1 -2
- package/dist/{plugin-create-5HQRF2ID.js.map → plugin-create-EHL76ZZG.js.map} +1 -1
- package/dist/plugin-installer-VSTYZSXC.js +9 -0
- package/dist/{plugin-registry-WB3DR67H.js → plugin-registry-6J3YSFHF.js} +1 -2
- package/dist/{plugin-search-HQ4WQKOF.js → plugin-search-MGKAL5JM.js} +1 -2
- package/dist/{plugin-search-HQ4WQKOF.js.map → plugin-search-MGKAL5JM.js.map} +1 -1
- package/dist/{post-upgrade-3ADZRMYJ.js → post-upgrade-Y26S2ZQ7.js} +6 -7
- package/dist/{post-upgrade-3ADZRMYJ.js.map → post-upgrade-Y26S2ZQ7.js.map} +1 -1
- package/dist/{read-text-file-IRZM3QLM.js → read-text-file-DJBTITIB.js} +1 -2
- package/dist/{registry-client-AVGRE4CF.js → registry-client-GTBWLXYU.js} +1 -2
- package/dist/{security-YNRBW6S7.js → security-2BA265LN.js} +1 -2
- package/dist/{settings-manager-MD2U4ZV2.js → settings-manager-B4UN2LAC.js} +1 -2
- package/dist/{setup-A7VPW46C.js → setup-E6BNEYCS.js} +109 -83
- package/dist/setup-E6BNEYCS.js.map +1 -0
- package/dist/speech-SG62JYIF.js +9 -0
- package/dist/{suggest-7D6B542M.js → suggest-RST5VOHB.js} +1 -3
- package/dist/{suggest-7D6B542M.js.map → suggest-RST5VOHB.js.map} +1 -1
- package/dist/telegram-EAVRDNFU.js +7 -0
- package/dist/tunnel-HWJ27WDH.js +7 -0
- package/dist/{tunnel-service-QJPUYEKU.js → tunnel-service-ZMO4THKE.js} +90 -11
- package/dist/tunnel-service-ZMO4THKE.js.map +1 -0
- package/dist/{validators-WSTBNKRW.js → validators-GITLOFXC.js} +1 -2
- package/dist/{version-NQZBM5M7.js → version-AXXV6IV2.js} +1 -2
- package/package.json +1 -3
- package/dist/adapter-JQFQ3JAO.js +0 -15
- package/dist/adapter-UORRGHNH.js +0 -1030
- package/dist/adapter-UORRGHNH.js.map +0 -1
- package/dist/agent-catalog-YHBFERYO.js +0 -11
- package/dist/agent-store-VSHNY5GT.js +0 -9
- package/dist/api-server-7G3ZUZRM.js +0 -8
- package/dist/api-server-CAYNPUF2.js +0 -11
- package/dist/chunk-2YCW3QDV.js.map +0 -1
- package/dist/chunk-32LVIEPW.js +0 -477
- package/dist/chunk-32LVIEPW.js.map +0 -1
- package/dist/chunk-AD3X6DGK.js.map +0 -1
- package/dist/chunk-BLQUXO7S.js.map +0 -1
- package/dist/chunk-KMMEFXIE.js.map +0 -1
- package/dist/chunk-ODUM3D6X.js.map +0 -1
- package/dist/chunk-QVMEF6FB.js.map +0 -1
- package/dist/chunk-QWP76EBW.js.map +0 -1
- package/dist/chunk-VUNV25KB.js +0 -16
- package/dist/chunk-WIIZNPCR.js.map +0 -1
- package/dist/chunk-WXVT3AOY.js.map +0 -1
- package/dist/chunk-XIBG7LSL.js.map +0 -1
- package/dist/chunk-XWDW3XBE.js.map +0 -1
- package/dist/chunk-ZHGPZBS4.js +0 -49
- package/dist/chunk-ZHGPZBS4.js.map +0 -1
- package/dist/chunk-ZNSO2QVC.js +0 -124
- package/dist/chunk-ZNSO2QVC.js.map +0 -1
- package/dist/config-I4FMCJGZ.js +0 -15
- package/dist/config-editor-7PKW42GZ.js +0 -11
- package/dist/context-XM6E22LM.js +0 -10
- package/dist/core-plugins-Y5US6RED.js +0 -23
- package/dist/dist-UHQK5CXN.js +0 -21151
- package/dist/dist-UHQK5CXN.js.map +0 -1
- package/dist/doctor-QZQAP46W.js +0 -10
- package/dist/file-service-EUODJAIT.js +0 -9
- package/dist/main-3GF3EQTE.js.map +0 -1
- package/dist/notifications-D5BRDNSU.js +0 -9
- package/dist/plugin-installer-QVJP6VKV.js.map +0 -1
- package/dist/setup-A7VPW46C.js.map +0 -1
- package/dist/slack-2XNWBOWH.js +0 -8
- package/dist/speech-2GHQNRIO.js +0 -9
- package/dist/telegram-E65IWFBW.js +0 -8
- package/dist/tunnel-45HA72MB.js +0 -8
- package/dist/tunnel-service-QJPUYEKU.js.map +0 -1
- package/dist/version-NQZBM5M7.js.map +0 -1
- /package/dist/{adapter-JQFQ3JAO.js.map → adapter-4U6MC5ZS.js.map} +0 -0
- /package/dist/{agent-catalog-YHBFERYO.js.map → agent-catalog-SZQQERV7.js.map} +0 -0
- /package/dist/{agent-dependencies-WS7Z2DFW.js.map → agent-dependencies-ED2ZTUHG.js.map} +0 -0
- /package/dist/{agent-registry-5LZT7CUB.js.map → agent-registry-YOGP656W.js.map} +0 -0
- /package/dist/{agent-store-VSHNY5GT.js.map → agent-store-5UHZH2XI.js.map} +0 -0
- /package/dist/{api-client-AQPNKXI2.js.map → api-client-XTLRRFPX.js.map} +0 -0
- /package/dist/{api-server-7G3ZUZRM.js.map → api-server-5VNYFWJE.js.map} +0 -0
- /package/dist/{api-server-CAYNPUF2.js.map → api-server-JLBDKCU4.js.map} +0 -0
- /package/dist/{autostart-6JS565RY.js.map → autostart-CUPZMKKC.js.map} +0 -0
- /package/dist/{chunk-PPSMUECX.js.map → chunk-2R5XM3ES.js.map} +0 -0
- /package/dist/{chunk-SNPYTMPR.js.map → chunk-3EWTPOF7.js.map} +0 -0
- /package/dist/{chunk-A6Y4GZM3.js.map → chunk-566W6INH.js.map} +0 -0
- /package/dist/{chunk-P2G275VD.js.map → chunk-CFUJGWOP.js.map} +0 -0
- /package/dist/{chunk-RBYBSSGO.js.map → chunk-FCTC7KDT.js.map} +0 -0
- /package/dist/{chunk-QAQDGPB4.js.map → chunk-GEOXPGCO.js.map} +0 -0
- /package/dist/{chunk-4GMLGCF2.js.map → chunk-KDU3ZEWT.js.map} +0 -0
- /package/dist/{chunk-HRKAXFWR.js.map → chunk-MPGEHTGE.js.map} +0 -0
- /package/dist/{chunk-BQ6FR32N.js.map → chunk-QWVHCTCA.js.map} +0 -0
- /package/dist/{chunk-SHTGQGAU.js.map → chunk-V2YZWYXT.js.map} +0 -0
- /package/dist/{chunk-VUNV25KB.js.map → config-KN6NKKPF.js.map} +0 -0
- /package/dist/{config-I4FMCJGZ.js.map → config-editor-76RVZS4B.js.map} +0 -0
- /package/dist/{config-editor-7PKW42GZ.js.map → config-registry-ZXAIJNYB.js.map} +0 -0
- /package/dist/{config-registry-CUMNXFGK.js.map → context-NXXW62NJ.js.map} +0 -0
- /package/dist/{context-XM6E22LM.js.map → core-plugins-OCHKGCIZ.js.map} +0 -0
- /package/dist/{core-plugins-Y5US6RED.js.map → daemon-XFEMMJSZ.js.map} +0 -0
- /package/dist/{daemon-PXO5QPCR.js.map → doctor-AV6AUO22.js.map} +0 -0
- /package/dist/{doctor-QZQAP46W.js.map → file-service-HHB3JQIO.js.map} +0 -0
- /package/dist/{file-service-EUODJAIT.js.map → log-YZ243M5G.js.map} +0 -0
- /package/dist/{log-LZ7FTRKG.js.map → menu-ALFN37IR.js.map} +0 -0
- /package/dist/{menu-YDQ2LWAR.js.map → notifications-MO23S7S3.js.map} +0 -0
- /package/dist/{notifications-D5BRDNSU.js.map → plugin-installer-VSTYZSXC.js.map} +0 -0
- /package/dist/{plugin-registry-WB3DR67H.js.map → plugin-registry-6J3YSFHF.js.map} +0 -0
- /package/dist/{read-text-file-IRZM3QLM.js.map → read-text-file-DJBTITIB.js.map} +0 -0
- /package/dist/{registry-client-AVGRE4CF.js.map → registry-client-GTBWLXYU.js.map} +0 -0
- /package/dist/{security-YNRBW6S7.js.map → security-2BA265LN.js.map} +0 -0
- /package/dist/{settings-manager-MD2U4ZV2.js.map → settings-manager-B4UN2LAC.js.map} +0 -0
- /package/dist/{slack-2XNWBOWH.js.map → speech-SG62JYIF.js.map} +0 -0
- /package/dist/{speech-2GHQNRIO.js.map → telegram-EAVRDNFU.js.map} +0 -0
- /package/dist/{telegram-E65IWFBW.js.map → tunnel-HWJ27WDH.js.map} +0 -0
- /package/dist/{tunnel-45HA72MB.js.map → validators-GITLOFXC.js.map} +0 -0
- /package/dist/{validators-WSTBNKRW.js.map → version-AXXV6IV2.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentCatalog
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-V2YZWYXT.js";
|
|
4
4
|
import {
|
|
5
5
|
getAgentCapabilities
|
|
6
6
|
} from "./chunk-ZSLHHQPQ.js";
|
|
@@ -8,31 +8,42 @@ import {
|
|
|
8
8
|
readTextFileWithRange
|
|
9
9
|
} from "./chunk-OYSAN7UX.js";
|
|
10
10
|
import {
|
|
11
|
+
closeSessionLogger,
|
|
11
12
|
createChildLogger,
|
|
12
13
|
createSessionLogger
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-R6KZYF7D.js";
|
|
14
15
|
|
|
15
16
|
// src/core/utils/streams.ts
|
|
16
17
|
function nodeToWebWritable(nodeStream) {
|
|
17
18
|
return new WritableStream({
|
|
18
19
|
write(chunk) {
|
|
19
20
|
return new Promise((resolve, reject) => {
|
|
20
|
-
nodeStream.write(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const ok = nodeStream.write(chunk);
|
|
22
|
+
if (ok) {
|
|
23
|
+
resolve();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
nodeStream.once("drain", resolve);
|
|
27
|
+
nodeStream.once("error", reject);
|
|
24
28
|
});
|
|
29
|
+
},
|
|
30
|
+
close() {
|
|
31
|
+
nodeStream.end();
|
|
32
|
+
},
|
|
33
|
+
abort(reason) {
|
|
34
|
+
nodeStream.destroy(reason instanceof Error ? reason : new Error(String(reason)));
|
|
25
35
|
}
|
|
26
36
|
});
|
|
27
37
|
}
|
|
28
38
|
function nodeToWebReadable(nodeStream) {
|
|
29
39
|
return new ReadableStream({
|
|
30
40
|
start(controller) {
|
|
31
|
-
nodeStream.on("data", (chunk) =>
|
|
32
|
-
controller.enqueue(new Uint8Array(chunk));
|
|
33
|
-
});
|
|
41
|
+
nodeStream.on("data", (chunk) => controller.enqueue(new Uint8Array(chunk)));
|
|
34
42
|
nodeStream.on("end", () => controller.close());
|
|
35
43
|
nodeStream.on("error", (err) => controller.error(err));
|
|
44
|
+
},
|
|
45
|
+
cancel() {
|
|
46
|
+
nodeStream.destroy();
|
|
36
47
|
}
|
|
37
48
|
});
|
|
38
49
|
}
|
|
@@ -131,8 +142,8 @@ var TypedEmitter = class {
|
|
|
131
142
|
// src/core/agents/agent-instance.ts
|
|
132
143
|
import { spawn as spawn2, execFileSync } from "child_process";
|
|
133
144
|
import { Transform } from "stream";
|
|
134
|
-
import
|
|
135
|
-
import
|
|
145
|
+
import fs2 from "fs";
|
|
146
|
+
import path2 from "path";
|
|
136
147
|
import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
137
148
|
import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
138
149
|
|
|
@@ -286,15 +297,48 @@ var McpManager = class {
|
|
|
286
297
|
}
|
|
287
298
|
};
|
|
288
299
|
|
|
300
|
+
// src/core/utils/debug-tracer.ts
|
|
301
|
+
import fs from "fs";
|
|
302
|
+
import path from "path";
|
|
303
|
+
var DEBUG_ENABLED = process.env.OPENACP_DEBUG === "true" || process.env.OPENACP_DEBUG === "1";
|
|
304
|
+
var DebugTracer = class {
|
|
305
|
+
constructor(sessionId, workingDirectory) {
|
|
306
|
+
this.sessionId = sessionId;
|
|
307
|
+
this.workingDirectory = workingDirectory;
|
|
308
|
+
this.logDir = path.join(workingDirectory, ".log");
|
|
309
|
+
}
|
|
310
|
+
dirCreated = false;
|
|
311
|
+
logDir;
|
|
312
|
+
log(layer, data) {
|
|
313
|
+
try {
|
|
314
|
+
if (!this.dirCreated) {
|
|
315
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
316
|
+
this.dirCreated = true;
|
|
317
|
+
}
|
|
318
|
+
const filePath = path.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);
|
|
319
|
+
const line = JSON.stringify({ ts: Date.now(), ...data }) + "\n";
|
|
320
|
+
fs.appendFileSync(filePath, line);
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/** No-op cleanup — establishes the pattern for future async implementations */
|
|
325
|
+
destroy() {
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
function createDebugTracer(sessionId, workingDirectory) {
|
|
329
|
+
if (!DEBUG_ENABLED) return null;
|
|
330
|
+
return new DebugTracer(sessionId, workingDirectory);
|
|
331
|
+
}
|
|
332
|
+
|
|
289
333
|
// src/core/agents/agent-instance.ts
|
|
290
334
|
var log = createChildLogger({ module: "agent-instance" });
|
|
291
335
|
function findPackageRoot(startDir) {
|
|
292
336
|
let dir = startDir;
|
|
293
|
-
while (dir !==
|
|
294
|
-
if (
|
|
337
|
+
while (dir !== path2.dirname(dir)) {
|
|
338
|
+
if (fs2.existsSync(path2.join(dir, "package.json"))) {
|
|
295
339
|
return dir;
|
|
296
340
|
}
|
|
297
|
-
dir =
|
|
341
|
+
dir = path2.dirname(dir);
|
|
298
342
|
}
|
|
299
343
|
return startDir;
|
|
300
344
|
}
|
|
@@ -306,26 +350,26 @@ function resolveAgentCommand(cmd) {
|
|
|
306
350
|
}
|
|
307
351
|
for (const root of searchRoots) {
|
|
308
352
|
const packageDirs = [
|
|
309
|
-
|
|
310
|
-
|
|
353
|
+
path2.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
|
|
354
|
+
path2.resolve(root, "node_modules", cmd, "dist", "index.js")
|
|
311
355
|
];
|
|
312
356
|
for (const jsPath of packageDirs) {
|
|
313
|
-
if (
|
|
357
|
+
if (fs2.existsSync(jsPath)) {
|
|
314
358
|
return { command: process.execPath, args: [jsPath] };
|
|
315
359
|
}
|
|
316
360
|
}
|
|
317
361
|
}
|
|
318
362
|
for (const root of searchRoots) {
|
|
319
|
-
const localBin =
|
|
320
|
-
if (
|
|
321
|
-
const content =
|
|
363
|
+
const localBin = path2.resolve(root, "node_modules", ".bin", cmd);
|
|
364
|
+
if (fs2.existsSync(localBin)) {
|
|
365
|
+
const content = fs2.readFileSync(localBin, "utf-8");
|
|
322
366
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
323
367
|
return { command: process.execPath, args: [localBin] };
|
|
324
368
|
}
|
|
325
369
|
const match = content.match(/"([^"]+\.js)"/);
|
|
326
370
|
if (match) {
|
|
327
|
-
const target =
|
|
328
|
-
if (
|
|
371
|
+
const target = path2.resolve(path2.dirname(localBin), match[1]);
|
|
372
|
+
if (fs2.existsSync(target)) {
|
|
329
373
|
return { command: process.execPath, args: [target] };
|
|
330
374
|
}
|
|
331
375
|
}
|
|
@@ -334,7 +378,7 @@ function resolveAgentCommand(cmd) {
|
|
|
334
378
|
try {
|
|
335
379
|
const fullPath = execFileSync("which", [cmd], { encoding: "utf-8" }).trim();
|
|
336
380
|
if (fullPath) {
|
|
337
|
-
const content =
|
|
381
|
+
const content = fs2.readFileSync(fullPath, "utf-8");
|
|
338
382
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
339
383
|
return { command: process.execPath, args: [fullPath] };
|
|
340
384
|
}
|
|
@@ -349,10 +393,12 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
349
393
|
stderrCapture;
|
|
350
394
|
terminalManager = new TerminalManager();
|
|
351
395
|
static mcpManager = new McpManager();
|
|
396
|
+
_destroying = false;
|
|
352
397
|
sessionId;
|
|
353
398
|
agentName;
|
|
354
399
|
promptCapabilities;
|
|
355
400
|
middlewareChain;
|
|
401
|
+
debugTracer = null;
|
|
356
402
|
// Callback — set by core when wiring events
|
|
357
403
|
onPermissionRequest = async () => "";
|
|
358
404
|
constructor(agentName) {
|
|
@@ -395,20 +441,28 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
395
441
|
});
|
|
396
442
|
const stdinLogger = new Transform({
|
|
397
443
|
transform(chunk, _enc, cb) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
444
|
+
if (instance.debugTracer) {
|
|
445
|
+
const raw = chunk.toString().trimEnd();
|
|
446
|
+
try {
|
|
447
|
+
instance.debugTracer.log("acp", { dir: "send", data: JSON.parse(raw) });
|
|
448
|
+
} catch {
|
|
449
|
+
instance.debugTracer.log("acp", { dir: "send", data: raw });
|
|
450
|
+
}
|
|
451
|
+
}
|
|
402
452
|
cb(null, chunk);
|
|
403
453
|
}
|
|
404
454
|
});
|
|
405
455
|
stdinLogger.pipe(instance.child.stdin);
|
|
406
456
|
const stdoutLogger = new Transform({
|
|
407
457
|
transform(chunk, _enc, cb) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
458
|
+
if (instance.debugTracer) {
|
|
459
|
+
const raw = chunk.toString().trimEnd();
|
|
460
|
+
try {
|
|
461
|
+
instance.debugTracer.log("acp", { dir: "recv", data: JSON.parse(raw) });
|
|
462
|
+
} catch {
|
|
463
|
+
instance.debugTracer.log("acp", { dir: "recv", data: raw });
|
|
464
|
+
}
|
|
465
|
+
}
|
|
412
466
|
cb(null, chunk);
|
|
413
467
|
}
|
|
414
468
|
});
|
|
@@ -442,15 +496,17 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
442
496
|
}
|
|
443
497
|
setupCrashDetection() {
|
|
444
498
|
this.child.on("exit", (code, signal) => {
|
|
499
|
+
if (this._destroying) return;
|
|
445
500
|
log.info(
|
|
446
501
|
{ sessionId: this.sessionId, exitCode: code, signal },
|
|
447
502
|
"Agent process exited"
|
|
448
503
|
);
|
|
449
|
-
if (code !== 0 && code !== null) {
|
|
504
|
+
if (code !== 0 && code !== null || signal) {
|
|
450
505
|
const stderr = this.stderrCapture.getLastLines();
|
|
451
506
|
this.emit("agent_event", {
|
|
452
507
|
type: "error",
|
|
453
|
-
message: `Agent
|
|
508
|
+
message: signal ? `Agent killed by signal ${signal}
|
|
509
|
+
${stderr}` : `Agent crashed (exit code ${code})
|
|
454
510
|
${stderr}`
|
|
455
511
|
});
|
|
456
512
|
}
|
|
@@ -475,6 +531,7 @@ ${stderr}`
|
|
|
475
531
|
mcpServers: resolvedMcp
|
|
476
532
|
});
|
|
477
533
|
instance.sessionId = response.sessionId;
|
|
534
|
+
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
478
535
|
instance.setupCrashDetection();
|
|
479
536
|
log.info(
|
|
480
537
|
{ sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
|
|
@@ -495,6 +552,7 @@ ${stderr}`
|
|
|
495
552
|
cwd: workingDirectory
|
|
496
553
|
});
|
|
497
554
|
instance.sessionId = response.sessionId;
|
|
555
|
+
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
498
556
|
log.info(
|
|
499
557
|
{ sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
|
|
500
558
|
"Agent resume complete"
|
|
@@ -510,6 +568,7 @@ ${stderr}`
|
|
|
510
568
|
mcpServers: resolvedMcp
|
|
511
569
|
});
|
|
512
570
|
instance.sessionId = response.sessionId;
|
|
571
|
+
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
513
572
|
log.info(
|
|
514
573
|
{ sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
|
|
515
574
|
"Agent fallback spawn complete"
|
|
@@ -698,8 +757,8 @@ ${stderr}`
|
|
|
698
757
|
writePath = result.path;
|
|
699
758
|
writeContent = result.content;
|
|
700
759
|
}
|
|
701
|
-
await
|
|
702
|
-
await
|
|
760
|
+
await fs2.promises.mkdir(path2.dirname(writePath), { recursive: true });
|
|
761
|
+
await fs2.promises.writeFile(writePath, writeContent, "utf-8");
|
|
703
762
|
return {};
|
|
704
763
|
},
|
|
705
764
|
// ── Terminal operations (delegated to TerminalManager) ─────────────
|
|
@@ -783,10 +842,10 @@ ${stderr}`
|
|
|
783
842
|
for (const att of attachments ?? []) {
|
|
784
843
|
const tooLarge = att.size > 10 * 1024 * 1024;
|
|
785
844
|
if (att.type === "image" && this.promptCapabilities?.image && !tooLarge && SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {
|
|
786
|
-
const data = await
|
|
845
|
+
const data = await fs2.promises.readFile(att.filePath);
|
|
787
846
|
contentBlocks.push({ type: "image", data: data.toString("base64"), mimeType: att.mimeType });
|
|
788
847
|
} else if (att.type === "audio" && this.promptCapabilities?.audio && !tooLarge) {
|
|
789
|
-
const data = await
|
|
848
|
+
const data = await fs2.promises.readFile(att.filePath);
|
|
790
849
|
contentBlocks.push({ type: "audio", data: data.toString("base64"), mimeType: att.mimeType });
|
|
791
850
|
} else {
|
|
792
851
|
if ((att.type === "image" || att.type === "audio") && !tooLarge) {
|
|
@@ -810,11 +869,23 @@ ${stderr}`
|
|
|
810
869
|
await this.connection.cancel({ sessionId: this.sessionId });
|
|
811
870
|
}
|
|
812
871
|
async destroy() {
|
|
872
|
+
this._destroying = true;
|
|
813
873
|
this.terminalManager.destroyAll();
|
|
814
|
-
this.child.
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
874
|
+
if (this.child.exitCode !== null) return;
|
|
875
|
+
await new Promise((resolve) => {
|
|
876
|
+
this.child.on("exit", () => {
|
|
877
|
+
clearTimeout(forceKillTimer);
|
|
878
|
+
resolve();
|
|
879
|
+
});
|
|
880
|
+
this.child.kill("SIGTERM");
|
|
881
|
+
const forceKillTimer = setTimeout(() => {
|
|
882
|
+
if (this.child.exitCode === null) this.child.kill("SIGKILL");
|
|
883
|
+
resolve();
|
|
884
|
+
}, 1e4);
|
|
885
|
+
if (typeof forceKillTimer === "object" && forceKillTimer !== null && "unref" in forceKillTimer) {
|
|
886
|
+
forceKillTimer.unref();
|
|
887
|
+
}
|
|
888
|
+
});
|
|
818
889
|
}
|
|
819
890
|
};
|
|
820
891
|
|
|
@@ -921,6 +992,9 @@ var PermissionGate = class {
|
|
|
921
992
|
this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
922
993
|
}
|
|
923
994
|
setPending(request) {
|
|
995
|
+
if (!this.settled && this.rejectFn) {
|
|
996
|
+
this.rejectFn(new Error("Superseded by new permission request"));
|
|
997
|
+
}
|
|
924
998
|
this.request = request;
|
|
925
999
|
this.settled = false;
|
|
926
1000
|
this.clearTimeout();
|
|
@@ -930,6 +1004,9 @@ var PermissionGate = class {
|
|
|
930
1004
|
this.timeoutTimer = setTimeout(() => {
|
|
931
1005
|
this.reject("Permission request timed out (no response received)");
|
|
932
1006
|
}, this.timeoutMs);
|
|
1007
|
+
if (typeof this.timeoutTimer === "object" && "unref" in this.timeoutTimer) {
|
|
1008
|
+
this.timeoutTimer.unref();
|
|
1009
|
+
}
|
|
933
1010
|
});
|
|
934
1011
|
}
|
|
935
1012
|
resolve(optionId) {
|
|
@@ -971,7 +1048,7 @@ var PermissionGate = class {
|
|
|
971
1048
|
|
|
972
1049
|
// src/core/sessions/session.ts
|
|
973
1050
|
import { nanoid } from "nanoid";
|
|
974
|
-
import * as
|
|
1051
|
+
import * as fs3 from "fs";
|
|
975
1052
|
var moduleLog = createChildLogger({ module: "session" });
|
|
976
1053
|
var TTS_PROMPT_INSTRUCTION = `
|
|
977
1054
|
|
|
@@ -982,7 +1059,7 @@ var TTS_TIMEOUT_MS = 3e4;
|
|
|
982
1059
|
var VALID_TRANSITIONS = {
|
|
983
1060
|
initializing: /* @__PURE__ */ new Set(["active", "error"]),
|
|
984
1061
|
active: /* @__PURE__ */ new Set(["error", "finished", "cancelled"]),
|
|
985
|
-
error: /* @__PURE__ */ new Set(["active"]),
|
|
1062
|
+
error: /* @__PURE__ */ new Set(["active", "cancelled"]),
|
|
986
1063
|
cancelled: /* @__PURE__ */ new Set(["active"]),
|
|
987
1064
|
finished: /* @__PURE__ */ new Set()
|
|
988
1065
|
};
|
|
@@ -1097,30 +1174,28 @@ var Session = class extends TypedEmitter {
|
|
|
1097
1174
|
await this.runWarmup();
|
|
1098
1175
|
return;
|
|
1099
1176
|
}
|
|
1177
|
+
if (this._status === "finished") return;
|
|
1100
1178
|
this.promptCount++;
|
|
1101
|
-
if (this._status === "initializing") {
|
|
1179
|
+
if (this._status === "initializing" || this._status === "cancelled" || this._status === "error") {
|
|
1102
1180
|
this.activate();
|
|
1103
1181
|
}
|
|
1104
1182
|
const promptStart = Date.now();
|
|
1105
1183
|
this.log.debug("Prompt execution started");
|
|
1106
|
-
|
|
1184
|
+
const contextUsed = this.pendingContext;
|
|
1185
|
+
if (contextUsed) {
|
|
1107
1186
|
text = `[CONVERSATION HISTORY - This is context from previous sessions, not current conversation]
|
|
1108
1187
|
|
|
1109
|
-
${
|
|
1188
|
+
${contextUsed}
|
|
1110
1189
|
|
|
1111
1190
|
[END CONVERSATION HISTORY]
|
|
1112
1191
|
|
|
1113
1192
|
${text}`;
|
|
1114
|
-
this.pendingContext = null;
|
|
1115
1193
|
this.log.debug("Context injected into prompt");
|
|
1116
1194
|
}
|
|
1117
1195
|
const processed = await this.maybeTranscribeAudio(text, attachments);
|
|
1118
1196
|
const ttsActive = this.voiceMode !== "off" && !!this.speechService?.isTTSAvailable();
|
|
1119
1197
|
if (ttsActive) {
|
|
1120
1198
|
processed.text += TTS_PROMPT_INSTRUCTION;
|
|
1121
|
-
if (this.voiceMode === "next") {
|
|
1122
|
-
this.voiceMode = "off";
|
|
1123
|
-
}
|
|
1124
1199
|
}
|
|
1125
1200
|
let accumulatedText = "";
|
|
1126
1201
|
const accumulatorListener = ttsActive ? (event) => {
|
|
@@ -1141,6 +1216,12 @@ ${text}`;
|
|
|
1141
1216
|
if (response && typeof response === "object" && "stopReason" in response) {
|
|
1142
1217
|
stopReason = response.stopReason ?? "end_turn";
|
|
1143
1218
|
}
|
|
1219
|
+
if (contextUsed) {
|
|
1220
|
+
this.pendingContext = null;
|
|
1221
|
+
}
|
|
1222
|
+
if (ttsActive && this.voiceMode === "next") {
|
|
1223
|
+
this.voiceMode = "off";
|
|
1224
|
+
}
|
|
1144
1225
|
} finally {
|
|
1145
1226
|
if (accumulatorListener) {
|
|
1146
1227
|
this.off("agent_event", accumulatorListener);
|
|
@@ -1184,7 +1265,7 @@ ${text}`;
|
|
|
1184
1265
|
try {
|
|
1185
1266
|
const audioPath = att.originalFilePath || att.filePath;
|
|
1186
1267
|
const audioMime = att.originalFilePath ? "audio/ogg" : att.mimeType;
|
|
1187
|
-
const audioBuffer = await
|
|
1268
|
+
const audioBuffer = await fs3.promises.readFile(audioPath);
|
|
1188
1269
|
const result = await this.speechService.transcribe(audioBuffer, audioMime);
|
|
1189
1270
|
this.log.info({ provider: "stt", duration: result.duration }, "Voice transcribed");
|
|
1190
1271
|
this.emit("agent_event", {
|
|
@@ -1220,20 +1301,26 @@ ${result.text}` : result.text;
|
|
|
1220
1301
|
ttsText = ttsText.slice(0, TTS_MAX_LENGTH);
|
|
1221
1302
|
}
|
|
1222
1303
|
try {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
const result = await Promise.race([
|
|
1227
|
-
this.speechService.synthesize(ttsText),
|
|
1228
|
-
timeoutPromise
|
|
1229
|
-
]);
|
|
1230
|
-
const base64 = result.audioBuffer.toString("base64");
|
|
1231
|
-
this.emit("agent_event", {
|
|
1232
|
-
type: "audio_content",
|
|
1233
|
-
data: base64,
|
|
1234
|
-
mimeType: result.mimeType
|
|
1304
|
+
let ttsTimer;
|
|
1305
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1306
|
+
ttsTimer = setTimeout(() => reject(new Error("TTS synthesis timed out")), TTS_TIMEOUT_MS);
|
|
1235
1307
|
});
|
|
1236
|
-
|
|
1308
|
+
try {
|
|
1309
|
+
const result = await Promise.race([
|
|
1310
|
+
this.speechService.synthesize(ttsText),
|
|
1311
|
+
timeoutPromise
|
|
1312
|
+
]);
|
|
1313
|
+
const base64 = result.audioBuffer.toString("base64");
|
|
1314
|
+
this.emit("agent_event", {
|
|
1315
|
+
type: "audio_content",
|
|
1316
|
+
data: base64,
|
|
1317
|
+
mimeType: result.mimeType
|
|
1318
|
+
});
|
|
1319
|
+
this.emit("agent_event", { type: "tts_strip" });
|
|
1320
|
+
this.log.info("TTS synthesis completed");
|
|
1321
|
+
} finally {
|
|
1322
|
+
clearTimeout(ttsTimer);
|
|
1323
|
+
}
|
|
1237
1324
|
} catch (err) {
|
|
1238
1325
|
this.log.warn({ err }, "TTS synthesis failed, skipping");
|
|
1239
1326
|
}
|
|
@@ -1337,7 +1424,12 @@ ${result.text}` : result.text;
|
|
|
1337
1424
|
}
|
|
1338
1425
|
async destroy() {
|
|
1339
1426
|
this.log.info("Session destroyed");
|
|
1427
|
+
if (this.permissionGate.isPending) {
|
|
1428
|
+
this.permissionGate.reject("Session destroyed");
|
|
1429
|
+
}
|
|
1430
|
+
this.queue.clear();
|
|
1340
1431
|
await this.agentInstance.destroy();
|
|
1432
|
+
closeSessionLogger(this.log);
|
|
1341
1433
|
}
|
|
1342
1434
|
};
|
|
1343
1435
|
|
|
@@ -1426,8 +1518,12 @@ var SessionManager = class {
|
|
|
1426
1518
|
async cancelSession(sessionId) {
|
|
1427
1519
|
const session = this.sessions.get(sessionId);
|
|
1428
1520
|
if (session) {
|
|
1429
|
-
|
|
1521
|
+
try {
|
|
1522
|
+
await session.abortPrompt();
|
|
1523
|
+
} catch {
|
|
1524
|
+
}
|
|
1430
1525
|
session.markCancelled();
|
|
1526
|
+
this.sessions.delete(sessionId);
|
|
1431
1527
|
}
|
|
1432
1528
|
if (this.store) {
|
|
1433
1529
|
const record = this.store.get(sessionId);
|
|
@@ -1458,6 +1554,25 @@ var SessionManager = class {
|
|
|
1458
1554
|
await this.store.remove(sessionId);
|
|
1459
1555
|
this.eventBus?.emit("session:deleted", { sessionId });
|
|
1460
1556
|
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Graceful shutdown: persist session state without killing agent subprocesses.
|
|
1559
|
+
* Agent processes will exit naturally when the parent process terminates.
|
|
1560
|
+
*/
|
|
1561
|
+
async shutdownAll() {
|
|
1562
|
+
if (this.store) {
|
|
1563
|
+
for (const session of this.sessions.values()) {
|
|
1564
|
+
const record = this.store.get(session.id);
|
|
1565
|
+
if (record) {
|
|
1566
|
+
await this.store.save({ ...record, status: "finished" });
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
this.sessions.clear();
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Forcefully destroy all sessions (kill agent subprocesses).
|
|
1574
|
+
* Use only when sessions must be fully torn down (e.g. archive).
|
|
1575
|
+
*/
|
|
1461
1576
|
async destroyAll() {
|
|
1462
1577
|
if (this.store) {
|
|
1463
1578
|
for (const session of this.sessions.values()) {
|
|
@@ -1494,15 +1609,29 @@ var SessionBridge = class {
|
|
|
1494
1609
|
sessionEventHandler;
|
|
1495
1610
|
statusChangeHandler;
|
|
1496
1611
|
namedHandler;
|
|
1612
|
+
get tracer() {
|
|
1613
|
+
return this.session.agentInstance.debugTracer ?? null;
|
|
1614
|
+
}
|
|
1497
1615
|
/** Send message to adapter, optionally running through message:outgoing middleware */
|
|
1498
1616
|
async sendMessage(sessionId, message) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1617
|
+
try {
|
|
1618
|
+
const mw = this.deps.middlewareChain;
|
|
1619
|
+
if (mw) {
|
|
1620
|
+
const result = await mw.execute("message:outgoing", { sessionId, message }, async (m) => m);
|
|
1621
|
+
this.tracer?.log("core", { step: "middleware:outgoing", sessionId, hook: "message:outgoing", blocked: !result });
|
|
1622
|
+
if (!result) return;
|
|
1623
|
+
this.tracer?.log("core", { step: "dispatch", sessionId, message: result.message });
|
|
1624
|
+
this.adapter.sendMessage(sessionId, result.message).catch((err) => {
|
|
1625
|
+
log2.error({ err, sessionId }, "Failed to send message to adapter");
|
|
1626
|
+
});
|
|
1627
|
+
} else {
|
|
1628
|
+
this.tracer?.log("core", { step: "dispatch", sessionId, message });
|
|
1629
|
+
this.adapter.sendMessage(sessionId, message).catch((err) => {
|
|
1630
|
+
log2.error({ err, sessionId }, "Failed to send message to adapter");
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
} catch (err) {
|
|
1634
|
+
log2.error({ err, sessionId }, "Error in sendMessage middleware");
|
|
1506
1635
|
}
|
|
1507
1636
|
}
|
|
1508
1637
|
connect() {
|
|
@@ -1538,23 +1667,37 @@ var SessionBridge = class {
|
|
|
1538
1667
|
}
|
|
1539
1668
|
wireSessionToAdapter() {
|
|
1540
1669
|
this.sessionEventHandler = (event) => {
|
|
1670
|
+
this.tracer?.log("core", { step: "agent_event", sessionId: this.session.id, event });
|
|
1541
1671
|
const mw = this.deps.middlewareChain;
|
|
1542
1672
|
if (mw) {
|
|
1543
1673
|
mw.execute("agent:beforeEvent", { sessionId: this.session.id, event }, async (e) => e).then((result) => {
|
|
1674
|
+
this.tracer?.log("core", { step: "middleware:before", sessionId: this.session.id, hook: "agent:beforeEvent", blocked: !result });
|
|
1544
1675
|
if (!result) return;
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1676
|
+
try {
|
|
1677
|
+
const transformedEvent = result.event;
|
|
1678
|
+
const outgoing = this.handleAgentEvent(transformedEvent);
|
|
1679
|
+
mw.execute("agent:afterEvent", {
|
|
1680
|
+
sessionId: this.session.id,
|
|
1681
|
+
event: transformedEvent,
|
|
1682
|
+
outgoingMessage: outgoing ?? { type: "text", text: "" }
|
|
1683
|
+
}, async (e) => e).catch(() => {
|
|
1684
|
+
});
|
|
1685
|
+
} catch (err) {
|
|
1686
|
+
log2.error({ err, sessionId: this.session.id }, "Error handling agent event after middleware");
|
|
1687
|
+
}
|
|
1553
1688
|
}).catch(() => {
|
|
1554
|
-
|
|
1689
|
+
try {
|
|
1690
|
+
this.handleAgentEvent(event);
|
|
1691
|
+
} catch (err) {
|
|
1692
|
+
log2.error({ err, sessionId: this.session.id }, "Error handling agent event (middleware fallback)");
|
|
1693
|
+
}
|
|
1555
1694
|
});
|
|
1556
1695
|
} else {
|
|
1557
|
-
|
|
1696
|
+
try {
|
|
1697
|
+
this.handleAgentEvent(event);
|
|
1698
|
+
} catch (err) {
|
|
1699
|
+
log2.error({ err, sessionId: this.session.id }, "Error handling agent event");
|
|
1700
|
+
}
|
|
1558
1701
|
}
|
|
1559
1702
|
};
|
|
1560
1703
|
this.session.on("agent_event", this.sessionEventHandler);
|
|
@@ -1578,6 +1721,7 @@ var SessionBridge = class {
|
|
|
1578
1721
|
case "plan":
|
|
1579
1722
|
case "usage":
|
|
1580
1723
|
outgoing = this.deps.messageTransformer.transform(event, ctx);
|
|
1724
|
+
this.tracer?.log("core", { step: "transform", sessionId: this.session.id, input: event, output: outgoing });
|
|
1581
1725
|
this.sendMessage(this.session.id, outgoing);
|
|
1582
1726
|
break;
|
|
1583
1727
|
case "session_end":
|
|
@@ -1607,12 +1751,12 @@ var SessionBridge = class {
|
|
|
1607
1751
|
break;
|
|
1608
1752
|
case "image_content": {
|
|
1609
1753
|
if (this.deps.fileService) {
|
|
1610
|
-
const
|
|
1754
|
+
const fs6 = this.deps.fileService;
|
|
1611
1755
|
const sid = this.session.id;
|
|
1612
1756
|
const { data, mimeType } = event;
|
|
1613
1757
|
const buffer = Buffer.from(data, "base64");
|
|
1614
|
-
const ext =
|
|
1615
|
-
|
|
1758
|
+
const ext = fs6.extensionFromMime(mimeType);
|
|
1759
|
+
fs6.saveFile(sid, `agent-image${ext}`, buffer, mimeType).then((att) => {
|
|
1616
1760
|
this.sendMessage(sid, {
|
|
1617
1761
|
type: "attachment",
|
|
1618
1762
|
text: "",
|
|
@@ -1624,12 +1768,12 @@ var SessionBridge = class {
|
|
|
1624
1768
|
}
|
|
1625
1769
|
case "audio_content": {
|
|
1626
1770
|
if (this.deps.fileService) {
|
|
1627
|
-
const
|
|
1771
|
+
const fs6 = this.deps.fileService;
|
|
1628
1772
|
const sid = this.session.id;
|
|
1629
1773
|
const { data, mimeType } = event;
|
|
1630
1774
|
const buffer = Buffer.from(data, "base64");
|
|
1631
|
-
const ext =
|
|
1632
|
-
|
|
1775
|
+
const ext = fs6.extensionFromMime(mimeType);
|
|
1776
|
+
fs6.saveFile(sid, `agent-audio${ext}`, buffer, mimeType).then((att) => {
|
|
1633
1777
|
this.sendMessage(sid, {
|
|
1634
1778
|
type: "attachment",
|
|
1635
1779
|
text: "",
|
|
@@ -1678,6 +1822,9 @@ var SessionBridge = class {
|
|
|
1678
1822
|
outgoing = this.deps.messageTransformer.transform(event);
|
|
1679
1823
|
this.sendMessage(this.session.id, outgoing);
|
|
1680
1824
|
break;
|
|
1825
|
+
case "tts_strip":
|
|
1826
|
+
this.adapter.stripTTSBlock?.(this.session.id);
|
|
1827
|
+
break;
|
|
1681
1828
|
}
|
|
1682
1829
|
this.deps.eventBus?.emit("agent:event", {
|
|
1683
1830
|
sessionId: this.session.id,
|
|
@@ -1780,7 +1927,7 @@ var SessionBridge = class {
|
|
|
1780
1927
|
sessionId: this.session.id,
|
|
1781
1928
|
status: to
|
|
1782
1929
|
});
|
|
1783
|
-
if (to === "finished"
|
|
1930
|
+
if (to === "finished") {
|
|
1784
1931
|
queueMicrotask(() => this.disconnect());
|
|
1785
1932
|
}
|
|
1786
1933
|
};
|
|
@@ -1803,14 +1950,22 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
|
1803
1950
|
let info = null;
|
|
1804
1951
|
if (meta) {
|
|
1805
1952
|
const m = meta;
|
|
1806
|
-
const
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1953
|
+
const toolResponse = resolveToolResponse(m);
|
|
1954
|
+
if (toolResponse) {
|
|
1955
|
+
const file = toolResponse.file;
|
|
1956
|
+
if (typeof file?.filePath === "string" && typeof file?.content === "string") {
|
|
1957
|
+
info = { filePath: file.filePath, content: file.content };
|
|
1958
|
+
}
|
|
1959
|
+
if (!info && typeof toolResponse.filePath === "string" && typeof toolResponse.originalFile === "string") {
|
|
1960
|
+
const originalFile = toolResponse.originalFile;
|
|
1961
|
+
const oldString = typeof toolResponse.oldString === "string" ? toolResponse.oldString : void 0;
|
|
1962
|
+
const newString = typeof toolResponse.newString === "string" ? toolResponse.newString : void 0;
|
|
1963
|
+
const newContent = oldString && newString ? originalFile.replace(oldString, newString) : originalFile;
|
|
1964
|
+
info = { filePath: toolResponse.filePath, content: newContent, oldContent: originalFile };
|
|
1965
|
+
}
|
|
1966
|
+
if (!info && typeof toolResponse.filePath === "string" && typeof toolResponse.content === "string") {
|
|
1967
|
+
info = { filePath: toolResponse.filePath, content: toolResponse.content };
|
|
1968
|
+
}
|
|
1814
1969
|
}
|
|
1815
1970
|
}
|
|
1816
1971
|
if (!info && rawInput && typeof rawInput === "object") {
|
|
@@ -1819,7 +1974,19 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
|
1819
1974
|
if (typeof filePath === "string") {
|
|
1820
1975
|
const parsed = content ? parseContent(content) : null;
|
|
1821
1976
|
const riContent = typeof ri?.content === "string" ? ri.content : void 0;
|
|
1822
|
-
|
|
1977
|
+
if (kind === "edit") {
|
|
1978
|
+
const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : void 0;
|
|
1979
|
+
const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : void 0;
|
|
1980
|
+
if (newStr) {
|
|
1981
|
+
info = { filePath, content: newStr, oldContent: oldStr };
|
|
1982
|
+
} else {
|
|
1983
|
+
info = { filePath, content: riContent || parsed?.content, oldContent: parsed?.oldContent };
|
|
1984
|
+
}
|
|
1985
|
+
} else if (kind === "write") {
|
|
1986
|
+
info = { filePath, content: riContent || parsed?.content, oldContent: parsed?.oldContent };
|
|
1987
|
+
} else {
|
|
1988
|
+
info = { filePath, content: parsed?.content || riContent, oldContent: parsed?.oldContent };
|
|
1989
|
+
}
|
|
1823
1990
|
}
|
|
1824
1991
|
}
|
|
1825
1992
|
if (!info && content) {
|
|
@@ -1833,6 +2000,16 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
|
1833
2000
|
if (!info.filePath || !info.content) return null;
|
|
1834
2001
|
return info;
|
|
1835
2002
|
}
|
|
2003
|
+
function resolveToolResponse(meta) {
|
|
2004
|
+
const claudeCode = meta.claudeCode;
|
|
2005
|
+
if (claudeCode?.toolResponse && typeof claudeCode.toolResponse === "object") {
|
|
2006
|
+
return claudeCode.toolResponse;
|
|
2007
|
+
}
|
|
2008
|
+
if (meta.toolResponse && typeof meta.toolResponse === "object") {
|
|
2009
|
+
return meta.toolResponse;
|
|
2010
|
+
}
|
|
2011
|
+
return void 0;
|
|
2012
|
+
}
|
|
1836
2013
|
function parseContent(content) {
|
|
1837
2014
|
if (typeof content === "string") {
|
|
1838
2015
|
return { content };
|
|
@@ -1891,7 +2068,30 @@ function parseContent(content) {
|
|
|
1891
2068
|
|
|
1892
2069
|
// src/core/message-transformer.ts
|
|
1893
2070
|
var log3 = createChildLogger({ module: "message-transformer" });
|
|
2071
|
+
function computeLineDiff(oldStr, newStr) {
|
|
2072
|
+
const oldLines = oldStr ? oldStr.split("\n") : [];
|
|
2073
|
+
const newLines = newStr ? newStr.split("\n") : [];
|
|
2074
|
+
let prefixLen = 0;
|
|
2075
|
+
const minLen = Math.min(oldLines.length, newLines.length);
|
|
2076
|
+
while (prefixLen < minLen && oldLines[prefixLen] === newLines[prefixLen]) {
|
|
2077
|
+
prefixLen++;
|
|
2078
|
+
}
|
|
2079
|
+
let suffixLen = 0;
|
|
2080
|
+
const maxSuffix = minLen - prefixLen;
|
|
2081
|
+
while (suffixLen < maxSuffix && oldLines[oldLines.length - 1 - suffixLen] === newLines[newLines.length - 1 - suffixLen]) {
|
|
2082
|
+
suffixLen++;
|
|
2083
|
+
}
|
|
2084
|
+
return {
|
|
2085
|
+
added: Math.max(0, newLines.length - prefixLen - suffixLen),
|
|
2086
|
+
removed: Math.max(0, oldLines.length - prefixLen - suffixLen)
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
1894
2089
|
var MessageTransformer = class {
|
|
2090
|
+
tunnelService;
|
|
2091
|
+
/** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */
|
|
2092
|
+
toolRawInputCache = /* @__PURE__ */ new Map();
|
|
2093
|
+
/** Cache viewer links generated from intermediate updates so completion events carry them */
|
|
2094
|
+
toolViewerCache = /* @__PURE__ */ new Map();
|
|
1895
2095
|
constructor(tunnelService) {
|
|
1896
2096
|
this.tunnelService = tunnelService;
|
|
1897
2097
|
}
|
|
@@ -1902,6 +2102,9 @@ var MessageTransformer = class {
|
|
|
1902
2102
|
case "thought":
|
|
1903
2103
|
return { type: "thought", text: event.content };
|
|
1904
2104
|
case "tool_call": {
|
|
2105
|
+
if (event.id && this.isNonEmptyInput(event.rawInput)) {
|
|
2106
|
+
this.toolRawInputCache.set(event.id, event.rawInput);
|
|
2107
|
+
}
|
|
1905
2108
|
const meta = event.meta;
|
|
1906
2109
|
const metadata = {
|
|
1907
2110
|
id: event.id,
|
|
@@ -1919,6 +2122,14 @@ var MessageTransformer = class {
|
|
|
1919
2122
|
return { type: "tool_call", text: event.name, metadata };
|
|
1920
2123
|
}
|
|
1921
2124
|
case "tool_update": {
|
|
2125
|
+
if (event.id && this.isNonEmptyInput(event.rawInput)) {
|
|
2126
|
+
this.toolRawInputCache.set(event.id, event.rawInput);
|
|
2127
|
+
}
|
|
2128
|
+
const cachedRawInput = event.id ? this.toolRawInputCache.get(event.id) : void 0;
|
|
2129
|
+
const effectiveRawInput = this.isNonEmptyInput(event.rawInput) ? event.rawInput : cachedRawInput;
|
|
2130
|
+
if (event.id && (event.status === "completed" || event.status === "done" || event.status === "failed" || event.status === "error")) {
|
|
2131
|
+
this.toolRawInputCache.delete(event.id);
|
|
2132
|
+
}
|
|
1922
2133
|
const meta = event.meta;
|
|
1923
2134
|
const metadata = {
|
|
1924
2135
|
id: event.id,
|
|
@@ -1926,12 +2137,28 @@ var MessageTransformer = class {
|
|
|
1926
2137
|
kind: event.kind,
|
|
1927
2138
|
status: event.status,
|
|
1928
2139
|
content: event.content,
|
|
1929
|
-
rawInput:
|
|
2140
|
+
rawInput: effectiveRawInput,
|
|
1930
2141
|
displaySummary: meta?.displaySummary,
|
|
1931
2142
|
displayTitle: meta?.displayTitle,
|
|
1932
2143
|
displayKind: meta?.displayKind
|
|
1933
2144
|
};
|
|
1934
|
-
|
|
2145
|
+
const enrichEvent = { ...event, rawInput: effectiveRawInput };
|
|
2146
|
+
this.enrichWithViewerLinks(enrichEvent, metadata, sessionContext);
|
|
2147
|
+
if (event.id) {
|
|
2148
|
+
const cached = this.toolViewerCache.get(event.id);
|
|
2149
|
+
if (cached) {
|
|
2150
|
+
metadata.viewerLinks = cached.viewerLinks;
|
|
2151
|
+
metadata.viewerFilePath = cached.viewerFilePath;
|
|
2152
|
+
} else if (metadata.viewerLinks) {
|
|
2153
|
+
this.toolViewerCache.set(event.id, {
|
|
2154
|
+
viewerLinks: metadata.viewerLinks,
|
|
2155
|
+
viewerFilePath: metadata.viewerFilePath
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
if (event.status === "completed" || event.status === "done" || event.status === "failed" || event.status === "error") {
|
|
2159
|
+
this.toolViewerCache.delete(event.id);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
1935
2162
|
return { type: "tool_update", text: "", metadata };
|
|
1936
2163
|
}
|
|
1937
2164
|
case "plan":
|
|
@@ -1947,7 +2174,7 @@ var MessageTransformer = class {
|
|
|
1947
2174
|
metadata: {
|
|
1948
2175
|
tokensUsed: event.tokensUsed,
|
|
1949
2176
|
contextSize: event.contextSize,
|
|
1950
|
-
cost: event.cost
|
|
2177
|
+
cost: event.cost?.amount
|
|
1951
2178
|
}
|
|
1952
2179
|
};
|
|
1953
2180
|
case "session_end":
|
|
@@ -2001,12 +2228,38 @@ var MessageTransformer = class {
|
|
|
2001
2228
|
return { type: "text", text: "" };
|
|
2002
2229
|
}
|
|
2003
2230
|
}
|
|
2231
|
+
/** Check if rawInput is a non-empty object (not null, not {}) */
|
|
2232
|
+
isNonEmptyInput(input) {
|
|
2233
|
+
return input !== null && input !== void 0 && typeof input === "object" && !Array.isArray(input) && Object.keys(input).length > 0;
|
|
2234
|
+
}
|
|
2004
2235
|
enrichWithViewerLinks(event, metadata, sessionContext) {
|
|
2005
|
-
if (!this.tunnelService || !sessionContext) return;
|
|
2006
|
-
const name = "name" in event ? event.name || "" : "";
|
|
2007
2236
|
const kind = "kind" in event ? event.kind : void 0;
|
|
2237
|
+
if (!metadata.diffStats && (kind === "edit" || kind === "write")) {
|
|
2238
|
+
const ri = event.rawInput;
|
|
2239
|
+
if (ri) {
|
|
2240
|
+
const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
|
|
2241
|
+
const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
|
|
2242
|
+
if (oldStr !== null && newStr !== null) {
|
|
2243
|
+
const stats = computeLineDiff(oldStr, newStr);
|
|
2244
|
+
if (stats.added > 0 || stats.removed > 0) {
|
|
2245
|
+
metadata.diffStats = stats;
|
|
2246
|
+
}
|
|
2247
|
+
} else if (oldStr === null && newStr !== null && kind === "write") {
|
|
2248
|
+
const added = newStr.split("\n").length;
|
|
2249
|
+
if (added > 0) metadata.diffStats = { added, removed: 0 };
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
if (!this.tunnelService || !sessionContext) {
|
|
2254
|
+
log3.debug(
|
|
2255
|
+
{ hasTunnel: !!this.tunnelService, hasCtx: !!sessionContext, kind },
|
|
2256
|
+
"enrichWithViewerLinks: skipping (no tunnel or session context)"
|
|
2257
|
+
);
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
const name = "name" in event ? event.name || "" : "";
|
|
2008
2261
|
log3.debug(
|
|
2009
|
-
{ name, kind, status: event.status, hasContent: !!event.content },
|
|
2262
|
+
{ name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },
|
|
2010
2263
|
"enrichWithViewerLinks: inspecting event"
|
|
2011
2264
|
);
|
|
2012
2265
|
const fileInfo = extractFileInfo(
|
|
@@ -2016,7 +2269,18 @@ var MessageTransformer = class {
|
|
|
2016
2269
|
event.rawInput,
|
|
2017
2270
|
event.meta
|
|
2018
2271
|
);
|
|
2019
|
-
if (!fileInfo)
|
|
2272
|
+
if (!fileInfo) {
|
|
2273
|
+
log3.debug(
|
|
2274
|
+
{ name, kind, hasContent: !!event.content, hasRawInput: !!event.rawInput, hasMeta: !!event.meta },
|
|
2275
|
+
"enrichWithViewerLinks: extractFileInfo returned null"
|
|
2276
|
+
);
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
const publicUrl = this.tunnelService.getPublicUrl();
|
|
2280
|
+
if (publicUrl.startsWith("http://localhost") || publicUrl.startsWith("http://127.0.0.1")) {
|
|
2281
|
+
log3.debug({ kind, filePath: fileInfo.filePath }, "enrichWithViewerLinks: skipping (no public tunnel URL)");
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2020
2284
|
log3.info(
|
|
2021
2285
|
{
|
|
2022
2286
|
name,
|
|
@@ -2037,6 +2301,12 @@ var MessageTransformer = class {
|
|
|
2037
2301
|
sessionContext.workingDirectory
|
|
2038
2302
|
);
|
|
2039
2303
|
if (id2) viewerLinks.diff = this.tunnelService.diffUrl(id2);
|
|
2304
|
+
if (!metadata.diffStats) {
|
|
2305
|
+
const stats = computeLineDiff(fileInfo.oldContent, fileInfo.content);
|
|
2306
|
+
if (stats.added > 0 || stats.removed > 0) {
|
|
2307
|
+
metadata.diffStats = stats;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2040
2310
|
}
|
|
2041
2311
|
const id = store.storeFile(
|
|
2042
2312
|
sessionContext.id,
|
|
@@ -2152,12 +2422,12 @@ var EventBus = class extends TypedEmitter {
|
|
|
2152
2422
|
};
|
|
2153
2423
|
|
|
2154
2424
|
// src/core/core.ts
|
|
2155
|
-
import
|
|
2425
|
+
import path5 from "path";
|
|
2156
2426
|
import os from "os";
|
|
2157
2427
|
|
|
2158
2428
|
// src/core/sessions/session-store.ts
|
|
2159
|
-
import
|
|
2160
|
-
import
|
|
2429
|
+
import fs4 from "fs";
|
|
2430
|
+
import path3 from "path";
|
|
2161
2431
|
var log5 = createChildLogger({ module: "session-store" });
|
|
2162
2432
|
var DEBOUNCE_MS = 2e3;
|
|
2163
2433
|
var JsonFileSessionStore = class {
|
|
@@ -2219,9 +2489,9 @@ var JsonFileSessionStore = class {
|
|
|
2219
2489
|
version: 1,
|
|
2220
2490
|
sessions: Object.fromEntries(this.records)
|
|
2221
2491
|
};
|
|
2222
|
-
const dir =
|
|
2223
|
-
if (!
|
|
2224
|
-
|
|
2492
|
+
const dir = path3.dirname(this.filePath);
|
|
2493
|
+
if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
|
|
2494
|
+
fs4.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
|
|
2225
2495
|
}
|
|
2226
2496
|
destroy() {
|
|
2227
2497
|
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
@@ -2234,10 +2504,10 @@ var JsonFileSessionStore = class {
|
|
|
2234
2504
|
}
|
|
2235
2505
|
}
|
|
2236
2506
|
load() {
|
|
2237
|
-
if (!
|
|
2507
|
+
if (!fs4.existsSync(this.filePath)) return;
|
|
2238
2508
|
try {
|
|
2239
2509
|
const raw = JSON.parse(
|
|
2240
|
-
|
|
2510
|
+
fs4.readFileSync(this.filePath, "utf-8")
|
|
2241
2511
|
);
|
|
2242
2512
|
if (raw.version !== 1) {
|
|
2243
2513
|
log5.warn(
|
|
@@ -2251,7 +2521,11 @@ var JsonFileSessionStore = class {
|
|
|
2251
2521
|
}
|
|
2252
2522
|
log5.debug({ count: this.records.size }, "Loaded session records");
|
|
2253
2523
|
} catch (err) {
|
|
2254
|
-
log5.error({ err }, "Failed to load session store");
|
|
2524
|
+
log5.error({ err }, "Failed to load session store, backing up corrupt file");
|
|
2525
|
+
try {
|
|
2526
|
+
fs4.renameSync(this.filePath, `${this.filePath}.bak`);
|
|
2527
|
+
} catch {
|
|
2528
|
+
}
|
|
2255
2529
|
}
|
|
2256
2530
|
}
|
|
2257
2531
|
cleanup() {
|
|
@@ -2392,6 +2666,7 @@ var MiddlewareChain = class {
|
|
|
2392
2666
|
const existing = this.chains.get(hook);
|
|
2393
2667
|
if (existing) {
|
|
2394
2668
|
existing.push(entry);
|
|
2669
|
+
existing.sort((a, b) => a.priority - b.priority);
|
|
2395
2670
|
} else {
|
|
2396
2671
|
this.chains.set(hook, [entry]);
|
|
2397
2672
|
}
|
|
@@ -2401,7 +2676,7 @@ var MiddlewareChain = class {
|
|
|
2401
2676
|
if (!handlers || handlers.length === 0) {
|
|
2402
2677
|
return coreHandler(payload);
|
|
2403
2678
|
}
|
|
2404
|
-
const sorted =
|
|
2679
|
+
const sorted = handlers;
|
|
2405
2680
|
let cachedResult = void 0;
|
|
2406
2681
|
const buildNext = (index, currentPayload) => {
|
|
2407
2682
|
return async () => {
|
|
@@ -2431,15 +2706,16 @@ var MiddlewareChain = class {
|
|
|
2431
2706
|
return nextResult;
|
|
2432
2707
|
};
|
|
2433
2708
|
let handlerResult;
|
|
2709
|
+
let timeoutTimer;
|
|
2434
2710
|
try {
|
|
2435
2711
|
const timeoutPromise = new Promise((_, reject) => {
|
|
2436
|
-
|
|
2712
|
+
timeoutTimer = setTimeout(
|
|
2437
2713
|
() => reject(new Error(`Middleware timeout: ${entry.pluginName} on hook ${hook}`)),
|
|
2438
2714
|
MIDDLEWARE_TIMEOUT_MS
|
|
2439
2715
|
);
|
|
2440
|
-
if (typeof
|
|
2716
|
+
if (typeof timeoutTimer === "object" && timeoutTimer !== null && "unref" in timeoutTimer) {
|
|
2441
2717
|
;
|
|
2442
|
-
|
|
2718
|
+
timeoutTimer.unref();
|
|
2443
2719
|
}
|
|
2444
2720
|
});
|
|
2445
2721
|
handlerResult = await Promise.race([
|
|
@@ -2452,6 +2728,8 @@ var MiddlewareChain = class {
|
|
|
2452
2728
|
}
|
|
2453
2729
|
this.errorTracker?.increment(entry.pluginName);
|
|
2454
2730
|
return nextFn();
|
|
2731
|
+
} finally {
|
|
2732
|
+
clearTimeout(timeoutTimer);
|
|
2455
2733
|
}
|
|
2456
2734
|
if (handlerResult === null) {
|
|
2457
2735
|
return null;
|
|
@@ -2519,26 +2797,27 @@ var ErrorTracker = class {
|
|
|
2519
2797
|
};
|
|
2520
2798
|
|
|
2521
2799
|
// src/core/plugin/plugin-storage.ts
|
|
2522
|
-
import
|
|
2523
|
-
import
|
|
2800
|
+
import fs5 from "fs";
|
|
2801
|
+
import path4 from "path";
|
|
2524
2802
|
var PluginStorageImpl = class {
|
|
2525
2803
|
kvPath;
|
|
2526
2804
|
dataDir;
|
|
2527
2805
|
writeChain = Promise.resolve();
|
|
2528
2806
|
constructor(baseDir) {
|
|
2529
|
-
this.dataDir =
|
|
2530
|
-
this.kvPath =
|
|
2807
|
+
this.dataDir = path4.join(baseDir, "data");
|
|
2808
|
+
this.kvPath = path4.join(baseDir, "kv.json");
|
|
2809
|
+
fs5.mkdirSync(baseDir, { recursive: true });
|
|
2531
2810
|
}
|
|
2532
2811
|
readKv() {
|
|
2533
2812
|
try {
|
|
2534
|
-
const raw =
|
|
2813
|
+
const raw = fs5.readFileSync(this.kvPath, "utf-8");
|
|
2535
2814
|
return JSON.parse(raw);
|
|
2536
2815
|
} catch {
|
|
2537
2816
|
return {};
|
|
2538
2817
|
}
|
|
2539
2818
|
}
|
|
2540
2819
|
writeKv(data) {
|
|
2541
|
-
|
|
2820
|
+
fs5.writeFileSync(this.kvPath, JSON.stringify(data), "utf-8");
|
|
2542
2821
|
}
|
|
2543
2822
|
async get(key) {
|
|
2544
2823
|
const data = this.readKv();
|
|
@@ -2564,7 +2843,7 @@ var PluginStorageImpl = class {
|
|
|
2564
2843
|
return Object.keys(this.readKv());
|
|
2565
2844
|
}
|
|
2566
2845
|
getDataDir() {
|
|
2567
|
-
|
|
2846
|
+
fs5.mkdirSync(this.dataDir, { recursive: true });
|
|
2568
2847
|
return this.dataDir;
|
|
2569
2848
|
}
|
|
2570
2849
|
};
|
|
@@ -2780,6 +3059,9 @@ var LifecycleManager = class {
|
|
|
2780
3059
|
get failedPlugins() {
|
|
2781
3060
|
return [...this._failed];
|
|
2782
3061
|
}
|
|
3062
|
+
get registry() {
|
|
3063
|
+
return this.pluginRegistry;
|
|
3064
|
+
}
|
|
2783
3065
|
constructor(opts) {
|
|
2784
3066
|
this.serviceRegistry = opts?.serviceRegistry ?? new ServiceRegistry();
|
|
2785
3067
|
this.middlewareChain = opts?.middlewareChain ?? new MiddlewareChain();
|
|
@@ -2821,6 +3103,8 @@ var LifecycleManager = class {
|
|
|
2821
3103
|
try {
|
|
2822
3104
|
sorted = resolveLoadOrder(allForResolution);
|
|
2823
3105
|
} catch (err) {
|
|
3106
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
3107
|
+
this.log?.error(`Plugin dependency resolution failed: ${error.message}`);
|
|
2824
3108
|
for (const p of plugins) {
|
|
2825
3109
|
this._failed.add(p.name);
|
|
2826
3110
|
}
|
|
@@ -2859,7 +3143,11 @@ var LifecycleManager = class {
|
|
|
2859
3143
|
settings: this.settingsManager.createAPI(plugin.name),
|
|
2860
3144
|
log: pluginLog
|
|
2861
3145
|
};
|
|
2862
|
-
const newSettings = await
|
|
3146
|
+
const newSettings = await withTimeout(
|
|
3147
|
+
plugin.migrate(migrateCtx, oldSettings, registryEntry.version),
|
|
3148
|
+
SETUP_TIMEOUT_MS,
|
|
3149
|
+
`${plugin.name}.migrate()`
|
|
3150
|
+
);
|
|
2863
3151
|
if (newSettings && typeof newSettings === "object") {
|
|
2864
3152
|
await migrateCtx.settings.setAll(newSettings);
|
|
2865
3153
|
}
|
|
@@ -2872,11 +3160,23 @@ var LifecycleManager = class {
|
|
|
2872
3160
|
let pluginConfig;
|
|
2873
3161
|
if (this.settingsManager) {
|
|
2874
3162
|
pluginConfig = await this.settingsManager.loadSettings(plugin.name);
|
|
3163
|
+
const settingsPath = this.settingsManager.getSettingsPath(plugin.name);
|
|
3164
|
+
this.getPluginLogger(plugin.name).debug(`Settings loaded from ${settingsPath}: ${Object.keys(pluginConfig).length} keys`);
|
|
2875
3165
|
if (Object.keys(pluginConfig).length === 0) {
|
|
2876
3166
|
pluginConfig = resolvePluginConfig(plugin.name, this.config);
|
|
2877
3167
|
}
|
|
2878
3168
|
} else {
|
|
2879
3169
|
pluginConfig = resolvePluginConfig(plugin.name, this.config);
|
|
3170
|
+
this.getPluginLogger(plugin.name).debug("No settingsManager, using legacy config");
|
|
3171
|
+
}
|
|
3172
|
+
if (plugin.settingsSchema && this.settingsManager) {
|
|
3173
|
+
const validation = this.settingsManager.validateSettings(plugin.name, pluginConfig, plugin.settingsSchema);
|
|
3174
|
+
if (!validation.valid) {
|
|
3175
|
+
this._failed.add(plugin.name);
|
|
3176
|
+
this.getPluginLogger(plugin.name).error(`Settings validation failed: ${validation.errors?.join("; ")}`);
|
|
3177
|
+
this.eventBus?.emit("plugin:failed", { name: plugin.name, error: `Settings validation failed: ${validation.errors?.join("; ")}` });
|
|
3178
|
+
continue;
|
|
3179
|
+
}
|
|
2880
3180
|
}
|
|
2881
3181
|
const ctx = createPluginContext({
|
|
2882
3182
|
pluginName: plugin.name,
|
|
@@ -2964,20 +3264,25 @@ var OpenACPCore = class {
|
|
|
2964
3264
|
sessionFactory;
|
|
2965
3265
|
lifecycleManager;
|
|
2966
3266
|
// --- Lazy getters: resolve from ServiceRegistry (populated by plugins during boot) ---
|
|
3267
|
+
getService(name) {
|
|
3268
|
+
const svc = this.lifecycleManager.serviceRegistry.get(name);
|
|
3269
|
+
if (!svc) throw new Error(`Service '${name}' not registered \u2014 is the ${name} plugin loaded?`);
|
|
3270
|
+
return svc;
|
|
3271
|
+
}
|
|
2967
3272
|
get securityGuard() {
|
|
2968
|
-
return this.
|
|
3273
|
+
return this.getService("security");
|
|
2969
3274
|
}
|
|
2970
3275
|
get notificationManager() {
|
|
2971
|
-
return this.
|
|
3276
|
+
return this.getService("notifications");
|
|
2972
3277
|
}
|
|
2973
3278
|
get fileService() {
|
|
2974
|
-
return this.
|
|
3279
|
+
return this.getService("file-service");
|
|
2975
3280
|
}
|
|
2976
3281
|
get speechService() {
|
|
2977
|
-
return this.
|
|
3282
|
+
return this.getService("speech");
|
|
2978
3283
|
}
|
|
2979
3284
|
get contextManager() {
|
|
2980
|
-
return this.
|
|
3285
|
+
return this.getService("context");
|
|
2981
3286
|
}
|
|
2982
3287
|
constructor(configManager) {
|
|
2983
3288
|
this.configManager = configManager;
|
|
@@ -2985,7 +3290,7 @@ var OpenACPCore = class {
|
|
|
2985
3290
|
this.agentCatalog = new AgentCatalog();
|
|
2986
3291
|
this.agentCatalog.load();
|
|
2987
3292
|
this.agentManager = new AgentManager(this.agentCatalog);
|
|
2988
|
-
const storePath =
|
|
3293
|
+
const storePath = path5.join(os.homedir(), ".openacp", "sessions.json");
|
|
2989
3294
|
this.sessionStore = new JsonFileSessionStore(
|
|
2990
3295
|
storePath,
|
|
2991
3296
|
config.sessionStore.ttlDays
|
|
@@ -3008,7 +3313,7 @@ var OpenACPCore = class {
|
|
|
3008
3313
|
sessions: this.sessionManager,
|
|
3009
3314
|
config: this.configManager,
|
|
3010
3315
|
core: this,
|
|
3011
|
-
storagePath:
|
|
3316
|
+
storagePath: path5.join(os.homedir(), ".openacp", "plugins", "data"),
|
|
3012
3317
|
log: createChildLogger({ module: "plugin" })
|
|
3013
3318
|
});
|
|
3014
3319
|
this.sessionFactory.middlewareChain = this.lifecycleManager.middlewareChain;
|
|
@@ -3017,7 +3322,7 @@ var OpenACPCore = class {
|
|
|
3017
3322
|
"config:changed",
|
|
3018
3323
|
async ({ path: configPath, value }) => {
|
|
3019
3324
|
if (configPath === "logging.level" && typeof value === "string") {
|
|
3020
|
-
const { setLogLevel } = await import("./log-
|
|
3325
|
+
const { setLogLevel } = await import("./log-YZ243M5G.js");
|
|
3021
3326
|
setLogLevel(value);
|
|
3022
3327
|
log6.info({ level: value }, "Log level changed at runtime");
|
|
3023
3328
|
}
|
|
@@ -3041,7 +3346,7 @@ var OpenACPCore = class {
|
|
|
3041
3346
|
}
|
|
3042
3347
|
set tunnelService(service) {
|
|
3043
3348
|
this._tunnelService = service;
|
|
3044
|
-
this.messageTransformer =
|
|
3349
|
+
this.messageTransformer.tunnelService = service;
|
|
3045
3350
|
}
|
|
3046
3351
|
registerAdapter(name, adapter) {
|
|
3047
3352
|
this.adapters.set(name, adapter);
|
|
@@ -3056,14 +3361,17 @@ var OpenACPCore = class {
|
|
|
3056
3361
|
}
|
|
3057
3362
|
async stop() {
|
|
3058
3363
|
try {
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3364
|
+
const nm = this.lifecycleManager.serviceRegistry.get("notifications");
|
|
3365
|
+
if (nm) {
|
|
3366
|
+
await nm.notifyAll({
|
|
3367
|
+
sessionId: "system",
|
|
3368
|
+
type: "error",
|
|
3369
|
+
summary: "OpenACP is shutting down"
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3064
3372
|
} catch {
|
|
3065
3373
|
}
|
|
3066
|
-
await this.sessionManager.
|
|
3374
|
+
await this.sessionManager.shutdownAll();
|
|
3067
3375
|
for (const adapter of this.adapters.values()) {
|
|
3068
3376
|
await adapter.stop();
|
|
3069
3377
|
}
|
|
@@ -3071,30 +3379,30 @@ var OpenACPCore = class {
|
|
|
3071
3379
|
// --- Archive ---
|
|
3072
3380
|
async archiveSession(sessionId) {
|
|
3073
3381
|
const session = this.sessionManager.getSession(sessionId);
|
|
3074
|
-
|
|
3075
|
-
if (
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
const adapter = this.adapters.get(channelId);
|
|
3382
|
+
if (!session) return { ok: false, error: "Session not found (must be in memory)" };
|
|
3383
|
+
if (session.status !== "active" && session.status !== "cancelled" && session.status !== "error") {
|
|
3384
|
+
return { ok: false, error: `Cannot archive session in '${session.status}' state` };
|
|
3385
|
+
}
|
|
3386
|
+
const adapter = this.adapters.get(session.channelId);
|
|
3079
3387
|
if (!adapter) return { ok: false, error: "Adapter not found for session" };
|
|
3388
|
+
if (!adapter.archiveSessionTopic) return { ok: false, error: "Adapter does not support topic archiving" };
|
|
3080
3389
|
try {
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
} catch {
|
|
3090
|
-
} finally {
|
|
3091
|
-
session.archiving = false;
|
|
3390
|
+
const newThreadId = await adapter.archiveSessionTopic(session.id);
|
|
3391
|
+
session.threadId = newThreadId;
|
|
3392
|
+
try {
|
|
3393
|
+
const platform = {};
|
|
3394
|
+
if (session.channelId === "telegram") {
|
|
3395
|
+
platform.topicId = Number(newThreadId);
|
|
3396
|
+
} else {
|
|
3397
|
+
platform.threadId = newThreadId;
|
|
3092
3398
|
}
|
|
3399
|
+
await this.sessionManager.patchRecord(sessionId, { platform });
|
|
3400
|
+
} catch (patchErr) {
|
|
3401
|
+
log6.warn({ err: patchErr, sessionId }, "Failed to update session record after archive \u2014 session will work but may not survive restart");
|
|
3093
3402
|
}
|
|
3094
|
-
|
|
3095
|
-
return { ok: true };
|
|
3403
|
+
return { ok: true, newThreadId };
|
|
3096
3404
|
} catch (err) {
|
|
3097
|
-
|
|
3405
|
+
session.archiving = false;
|
|
3098
3406
|
return { ok: false, error: err.message };
|
|
3099
3407
|
}
|
|
3100
3408
|
}
|
|
@@ -3153,6 +3461,9 @@ var OpenACPCore = class {
|
|
|
3153
3461
|
// --- Unified Session Creation Pipeline ---
|
|
3154
3462
|
async createSession(params) {
|
|
3155
3463
|
const session = await this.sessionFactory.create(params);
|
|
3464
|
+
if (params.threadId) {
|
|
3465
|
+
session.threadId = params.threadId;
|
|
3466
|
+
}
|
|
3156
3467
|
const adapter = this.adapters.get(params.channelId);
|
|
3157
3468
|
if (params.createThread && adapter) {
|
|
3158
3469
|
const threadId = await adapter.createSessionThread(
|
|
@@ -3391,7 +3702,7 @@ var OpenACPCore = class {
|
|
|
3391
3702
|
);
|
|
3392
3703
|
return null;
|
|
3393
3704
|
}
|
|
3394
|
-
if (record.status === "error") {
|
|
3705
|
+
if (record.status === "error" || record.status === "cancelled") {
|
|
3395
3706
|
log6.debug(
|
|
3396
3707
|
{
|
|
3397
3708
|
threadId: message.threadId,
|
|
@@ -3418,9 +3729,9 @@ var OpenACPCore = class {
|
|
|
3418
3729
|
workingDirectory: record.workingDir,
|
|
3419
3730
|
resumeAgentSessionId: record.agentSessionId,
|
|
3420
3731
|
existingSessionId: record.sessionId,
|
|
3421
|
-
initialName: record.name
|
|
3732
|
+
initialName: record.name,
|
|
3733
|
+
threadId: message.threadId
|
|
3422
3734
|
});
|
|
3423
|
-
session.threadId = message.threadId;
|
|
3424
3735
|
session.activate();
|
|
3425
3736
|
session.dangerousMode = record.dangerousMode ?? false;
|
|
3426
3737
|
log6.info(
|
|
@@ -3468,7 +3779,7 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
3468
3779
|
commands = /* @__PURE__ */ new Map();
|
|
3469
3780
|
/** Adapter-specific overrides: `channelId:commandName` → RegisteredCommand */
|
|
3470
3781
|
overrides = /* @__PURE__ */ new Map();
|
|
3471
|
-
static ADAPTER_SCOPES = /* @__PURE__ */ new Set(["telegram", "discord"
|
|
3782
|
+
static ADAPTER_SCOPES = /* @__PURE__ */ new Set(["telegram", "discord"]);
|
|
3472
3783
|
/**
|
|
3473
3784
|
* Register a command definition.
|
|
3474
3785
|
* @param def - Command definition
|
|
@@ -3503,22 +3814,18 @@ var CommandRegistry = class _CommandRegistry {
|
|
|
3503
3814
|
get(name) {
|
|
3504
3815
|
return this.commands.get(name);
|
|
3505
3816
|
}
|
|
3506
|
-
/** Remove a command by short
|
|
3817
|
+
/** Remove a command by name (short or qualified). Also removes its qualified name entry. */
|
|
3507
3818
|
unregister(name) {
|
|
3508
3819
|
const cmd = this.commands.get(name);
|
|
3509
|
-
if (cmd)
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
}
|
|
3515
|
-
if (!cmd) {
|
|
3516
|
-
this.commands.delete(name);
|
|
3820
|
+
if (!cmd) return;
|
|
3821
|
+
this.commands.delete(name);
|
|
3822
|
+
if (cmd.scope) {
|
|
3823
|
+
this.commands.delete(`${cmd.scope}:${cmd.name}`);
|
|
3824
|
+
this.commands.delete(cmd.name);
|
|
3517
3825
|
}
|
|
3518
3826
|
}
|
|
3519
3827
|
/** Remove all commands registered by a given plugin. */
|
|
3520
3828
|
unregisterByPlugin(pluginName) {
|
|
3521
|
-
const scope = _CommandRegistry.extractScope(pluginName);
|
|
3522
3829
|
const toDelete = [];
|
|
3523
3830
|
for (const [key, cmd] of this.commands) {
|
|
3524
3831
|
if (cmd.pluginName === pluginName) {
|
|
@@ -3607,4 +3914,4 @@ export {
|
|
|
3607
3914
|
OpenACPCore,
|
|
3608
3915
|
CommandRegistry
|
|
3609
3916
|
};
|
|
3610
|
-
//# sourceMappingURL=chunk-
|
|
3917
|
+
//# sourceMappingURL=chunk-7RKPIM3E.js.map
|