@openacp/cli 2026.403.7 → 2026.404.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/{channel-BL33p1ZP.d.ts → channel-CmTWKnpK.d.ts} +28 -1
- package/dist/cli.js +638 -148
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +67 -11
- package/dist/index.js +537 -82
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1078,7 +1078,7 @@ var BYPASS_KEYWORDS;
|
|
|
1078
1078
|
var init_bypass_detection = __esm({
|
|
1079
1079
|
"src/core/utils/bypass-detection.ts"() {
|
|
1080
1080
|
"use strict";
|
|
1081
|
-
BYPASS_KEYWORDS = ["bypass", "dangerous", "auto_accept"];
|
|
1081
|
+
BYPASS_KEYWORDS = ["bypass", "dangerous", "auto_accept", "yolo"];
|
|
1082
1082
|
}
|
|
1083
1083
|
});
|
|
1084
1084
|
|
|
@@ -8362,7 +8362,7 @@ var init_commands = __esm({
|
|
|
8362
8362
|
|
|
8363
8363
|
// src/plugins/telegram/permissions.ts
|
|
8364
8364
|
import { InlineKeyboard as InlineKeyboard11 } from "grammy";
|
|
8365
|
-
import { nanoid as
|
|
8365
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
8366
8366
|
var log30, PermissionHandler;
|
|
8367
8367
|
var init_permissions = __esm({
|
|
8368
8368
|
"src/plugins/telegram/permissions.ts"() {
|
|
@@ -8381,7 +8381,7 @@ var init_permissions = __esm({
|
|
|
8381
8381
|
pending = /* @__PURE__ */ new Map();
|
|
8382
8382
|
async sendPermissionRequest(session, request) {
|
|
8383
8383
|
const threadId = Number(session.threadId);
|
|
8384
|
-
const callbackKey =
|
|
8384
|
+
const callbackKey = nanoid3(8);
|
|
8385
8385
|
this.pending.set(callbackKey, {
|
|
8386
8386
|
sessionId: session.id,
|
|
8387
8387
|
requestId: request.id,
|
|
@@ -11182,6 +11182,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
11182
11182
|
{ sessionId: this.sessionId, exitCode: code, signal },
|
|
11183
11183
|
"Agent process exited"
|
|
11184
11184
|
);
|
|
11185
|
+
if (signal === "SIGINT" || signal === "SIGTERM") return;
|
|
11185
11186
|
if (code !== 0 && code !== null || signal) {
|
|
11186
11187
|
const stderr = this.stderrCapture.getLastLines();
|
|
11187
11188
|
this.emit("agent_event", {
|
|
@@ -11211,12 +11212,18 @@ ${stderr}`
|
|
|
11211
11212
|
cwd: workingDirectory,
|
|
11212
11213
|
mcpServers: resolvedMcp
|
|
11213
11214
|
});
|
|
11215
|
+
log4.info(response, "newSession response");
|
|
11214
11216
|
instance.sessionId = response.sessionId;
|
|
11215
11217
|
instance.initialSessionResponse = response;
|
|
11216
11218
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
11217
11219
|
instance.setupCrashDetection();
|
|
11218
11220
|
log4.info(
|
|
11219
|
-
{
|
|
11221
|
+
{
|
|
11222
|
+
sessionId: response.sessionId,
|
|
11223
|
+
durationMs: Date.now() - spawnStart,
|
|
11224
|
+
configOptions: response.configOptions ?? [],
|
|
11225
|
+
agentCapabilities: instance.agentCapabilities ?? null
|
|
11226
|
+
},
|
|
11220
11227
|
"Agent spawn complete"
|
|
11221
11228
|
);
|
|
11222
11229
|
return instance;
|
|
@@ -11228,24 +11235,47 @@ ${stderr}`
|
|
|
11228
11235
|
agentDef,
|
|
11229
11236
|
workingDirectory
|
|
11230
11237
|
);
|
|
11238
|
+
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
11231
11239
|
try {
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11240
|
+
if (instance.agentCapabilities?.loadSession) {
|
|
11241
|
+
const response = await instance.connection.loadSession({
|
|
11242
|
+
sessionId: agentSessionId,
|
|
11243
|
+
cwd: workingDirectory,
|
|
11244
|
+
mcpServers: resolvedMcp
|
|
11245
|
+
});
|
|
11246
|
+
instance.sessionId = agentSessionId;
|
|
11247
|
+
instance.initialSessionResponse = response;
|
|
11248
|
+
instance.debugTracer = createDebugTracer(agentSessionId, workingDirectory);
|
|
11249
|
+
log4.info(
|
|
11250
|
+
{
|
|
11251
|
+
sessionId: agentSessionId,
|
|
11252
|
+
durationMs: Date.now() - spawnStart,
|
|
11253
|
+
agentCapabilities: instance.agentCapabilities ?? null
|
|
11254
|
+
},
|
|
11255
|
+
"Agent load complete"
|
|
11256
|
+
);
|
|
11257
|
+
} else {
|
|
11258
|
+
const response = await instance.connection.unstable_resumeSession({
|
|
11259
|
+
sessionId: agentSessionId,
|
|
11260
|
+
cwd: workingDirectory
|
|
11261
|
+
});
|
|
11262
|
+
instance.sessionId = response.sessionId;
|
|
11263
|
+
instance.initialSessionResponse = response;
|
|
11264
|
+
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
11265
|
+
log4.info(
|
|
11266
|
+
{
|
|
11267
|
+
sessionId: response.sessionId,
|
|
11268
|
+
durationMs: Date.now() - spawnStart,
|
|
11269
|
+
agentCapabilities: instance.agentCapabilities ?? null
|
|
11270
|
+
},
|
|
11271
|
+
"Agent resume complete"
|
|
11272
|
+
);
|
|
11273
|
+
}
|
|
11243
11274
|
} catch (err) {
|
|
11244
11275
|
log4.warn(
|
|
11245
11276
|
{ err, agentSessionId },
|
|
11246
11277
|
"Resume failed, falling back to new session"
|
|
11247
11278
|
);
|
|
11248
|
-
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
11249
11279
|
const response = await instance.connection.newSession({
|
|
11250
11280
|
cwd: workingDirectory,
|
|
11251
11281
|
mcpServers: resolvedMcp
|
|
@@ -11476,11 +11506,31 @@ ${stderr}`
|
|
|
11476
11506
|
}
|
|
11477
11507
|
// ── New ACP methods ──────────────────────────────────────────────────
|
|
11478
11508
|
async setConfigOption(configId, value) {
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11509
|
+
try {
|
|
11510
|
+
return await this.connection.setSessionConfigOption({
|
|
11511
|
+
sessionId: this.sessionId,
|
|
11512
|
+
configId,
|
|
11513
|
+
...value
|
|
11514
|
+
});
|
|
11515
|
+
} catch (err) {
|
|
11516
|
+
if (typeof err === "object" && err !== null && err.code === -32601) {
|
|
11517
|
+
if (configId === "mode" && value.type === "select") {
|
|
11518
|
+
await this.connection.setSessionMode({
|
|
11519
|
+
sessionId: this.sessionId,
|
|
11520
|
+
modeId: value.value
|
|
11521
|
+
});
|
|
11522
|
+
return { configOptions: [] };
|
|
11523
|
+
}
|
|
11524
|
+
if (configId === "model" && value.type === "select") {
|
|
11525
|
+
await this.connection.unstable_setSessionModel({
|
|
11526
|
+
sessionId: this.sessionId,
|
|
11527
|
+
modelId: value.value
|
|
11528
|
+
});
|
|
11529
|
+
return { configOptions: [] };
|
|
11530
|
+
}
|
|
11531
|
+
}
|
|
11532
|
+
throw err;
|
|
11533
|
+
}
|
|
11484
11534
|
}
|
|
11485
11535
|
async listSessions(cwd, cursor) {
|
|
11486
11536
|
return await this.connection.listSessions({
|
|
@@ -11608,7 +11658,7 @@ var AgentManager = class {
|
|
|
11608
11658
|
};
|
|
11609
11659
|
|
|
11610
11660
|
// src/core/sessions/session.ts
|
|
11611
|
-
import { nanoid } from "nanoid";
|
|
11661
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
11612
11662
|
|
|
11613
11663
|
// src/core/sessions/prompt-queue.ts
|
|
11614
11664
|
var PromptQueue = class {
|
|
@@ -11619,21 +11669,21 @@ var PromptQueue = class {
|
|
|
11619
11669
|
queue = [];
|
|
11620
11670
|
processing = false;
|
|
11621
11671
|
abortController = null;
|
|
11622
|
-
async enqueue(text3, attachments) {
|
|
11672
|
+
async enqueue(text3, attachments, routing) {
|
|
11623
11673
|
if (this.processing) {
|
|
11624
11674
|
return new Promise((resolve6) => {
|
|
11625
|
-
this.queue.push({ text: text3, attachments, resolve: resolve6 });
|
|
11675
|
+
this.queue.push({ text: text3, attachments, routing, resolve: resolve6 });
|
|
11626
11676
|
});
|
|
11627
11677
|
}
|
|
11628
|
-
await this.process(text3, attachments);
|
|
11678
|
+
await this.process(text3, attachments, routing);
|
|
11629
11679
|
}
|
|
11630
|
-
async process(text3, attachments) {
|
|
11680
|
+
async process(text3, attachments, routing) {
|
|
11631
11681
|
this.processing = true;
|
|
11632
11682
|
this.abortController = new AbortController();
|
|
11633
11683
|
const { signal } = this.abortController;
|
|
11634
11684
|
try {
|
|
11635
11685
|
await Promise.race([
|
|
11636
|
-
this.processor(text3, attachments),
|
|
11686
|
+
this.processor(text3, attachments, routing),
|
|
11637
11687
|
new Promise((_, reject) => {
|
|
11638
11688
|
signal.addEventListener("abort", () => reject(new Error("Prompt aborted")), { once: true });
|
|
11639
11689
|
})
|
|
@@ -11651,7 +11701,7 @@ var PromptQueue = class {
|
|
|
11651
11701
|
drainNext() {
|
|
11652
11702
|
const next = this.queue.shift();
|
|
11653
11703
|
if (next) {
|
|
11654
|
-
this.process(next.text, next.attachments).then(next.resolve);
|
|
11704
|
+
this.process(next.text, next.attachments, next.routing).then(next.resolve);
|
|
11655
11705
|
}
|
|
11656
11706
|
}
|
|
11657
11707
|
clear() {
|
|
@@ -11741,6 +11791,33 @@ var PermissionGate = class {
|
|
|
11741
11791
|
// src/core/sessions/session.ts
|
|
11742
11792
|
init_log();
|
|
11743
11793
|
import * as fs10 from "fs";
|
|
11794
|
+
|
|
11795
|
+
// src/core/sessions/turn-context.ts
|
|
11796
|
+
import { nanoid } from "nanoid";
|
|
11797
|
+
function createTurnContext(sourceAdapterId, responseAdapterId) {
|
|
11798
|
+
return {
|
|
11799
|
+
turnId: nanoid(8),
|
|
11800
|
+
sourceAdapterId,
|
|
11801
|
+
responseAdapterId
|
|
11802
|
+
};
|
|
11803
|
+
}
|
|
11804
|
+
function getEffectiveTarget(ctx) {
|
|
11805
|
+
if (ctx.responseAdapterId === null) return null;
|
|
11806
|
+
return ctx.responseAdapterId ?? ctx.sourceAdapterId;
|
|
11807
|
+
}
|
|
11808
|
+
var SYSTEM_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
11809
|
+
"session_end",
|
|
11810
|
+
"system_message",
|
|
11811
|
+
"session_info_update",
|
|
11812
|
+
"config_option_update",
|
|
11813
|
+
"commands_update",
|
|
11814
|
+
"tts_strip"
|
|
11815
|
+
]);
|
|
11816
|
+
function isSystemEvent(event) {
|
|
11817
|
+
return SYSTEM_EVENT_TYPES.has(event.type);
|
|
11818
|
+
}
|
|
11819
|
+
|
|
11820
|
+
// src/core/sessions/session.ts
|
|
11744
11821
|
var moduleLog = createChildLogger({ module: "session" });
|
|
11745
11822
|
var TTS_PROMPT_INSTRUCTION = `
|
|
11746
11823
|
|
|
@@ -11758,7 +11835,15 @@ var VALID_TRANSITIONS = {
|
|
|
11758
11835
|
var Session = class extends TypedEmitter {
|
|
11759
11836
|
id;
|
|
11760
11837
|
channelId;
|
|
11761
|
-
threadId
|
|
11838
|
+
/** @deprecated Use threadIds map directly. Getter returns primary adapter's threadId. */
|
|
11839
|
+
get threadId() {
|
|
11840
|
+
return this.threadIds.get(this.channelId) ?? "";
|
|
11841
|
+
}
|
|
11842
|
+
set threadId(value) {
|
|
11843
|
+
if (value) {
|
|
11844
|
+
this.threadIds.set(this.channelId, value);
|
|
11845
|
+
}
|
|
11846
|
+
}
|
|
11762
11847
|
agentName;
|
|
11763
11848
|
workingDirectory;
|
|
11764
11849
|
agentInstance;
|
|
@@ -11779,14 +11864,21 @@ var Session = class extends TypedEmitter {
|
|
|
11779
11864
|
middlewareChain;
|
|
11780
11865
|
/** Latest commands emitted by the agent — buffered before bridge connects so they're not lost */
|
|
11781
11866
|
latestCommands = null;
|
|
11867
|
+
/** Adapters currently attached to this session (including primary) */
|
|
11868
|
+
attachedAdapters = [];
|
|
11869
|
+
/** Per-adapter thread IDs: adapterId → threadId */
|
|
11870
|
+
threadIds = /* @__PURE__ */ new Map();
|
|
11871
|
+
/** Active turn context — sealed on prompt dequeue, cleared on turn end */
|
|
11872
|
+
activeTurnContext = null;
|
|
11782
11873
|
permissionGate = new PermissionGate();
|
|
11783
11874
|
queue;
|
|
11784
11875
|
speechService;
|
|
11785
11876
|
pendingContext = null;
|
|
11786
11877
|
constructor(opts) {
|
|
11787
11878
|
super();
|
|
11788
|
-
this.id = opts.id ||
|
|
11879
|
+
this.id = opts.id || nanoid2(12);
|
|
11789
11880
|
this.channelId = opts.channelId;
|
|
11881
|
+
this.attachedAdapters = [opts.channelId];
|
|
11790
11882
|
this.agentName = opts.agentName;
|
|
11791
11883
|
this.firstAgent = opts.agentName;
|
|
11792
11884
|
this.workingDirectory = opts.workingDirectory;
|
|
@@ -11796,18 +11888,28 @@ var Session = class extends TypedEmitter {
|
|
|
11796
11888
|
this.log = createSessionLogger(this.id, moduleLog);
|
|
11797
11889
|
this.log.info({ agentName: this.agentName }, "Session created");
|
|
11798
11890
|
this.queue = new PromptQueue(
|
|
11799
|
-
(text3, attachments) => this.processPrompt(text3, attachments),
|
|
11891
|
+
(text3, attachments, routing) => this.processPrompt(text3, attachments, routing),
|
|
11800
11892
|
(err) => {
|
|
11801
11893
|
this.log.error({ err }, "Prompt execution failed");
|
|
11802
11894
|
const message = err instanceof Error ? err.message : String(err);
|
|
11895
|
+
this.fail(message);
|
|
11803
11896
|
this.emit("agent_event", { type: "error", message: `Prompt execution failed: ${message}` });
|
|
11804
11897
|
}
|
|
11805
11898
|
);
|
|
11806
|
-
this.
|
|
11899
|
+
this.wireCommandsBuffer();
|
|
11900
|
+
}
|
|
11901
|
+
/** Wire a listener on the current agentInstance to buffer commands_update events.
|
|
11902
|
+
* Must be called after every agentInstance replacement (constructor + switchAgent). */
|
|
11903
|
+
commandsBufferCleanup;
|
|
11904
|
+
wireCommandsBuffer() {
|
|
11905
|
+
this.commandsBufferCleanup?.();
|
|
11906
|
+
const handler = (event) => {
|
|
11807
11907
|
if (event.type === "commands_update") {
|
|
11808
11908
|
this.latestCommands = event.commands;
|
|
11809
11909
|
}
|
|
11810
|
-
}
|
|
11910
|
+
};
|
|
11911
|
+
this.agentInstance.on("agent_event", handler);
|
|
11912
|
+
this.commandsBufferCleanup = () => this.agentInstance.off("agent_event", handler);
|
|
11811
11913
|
}
|
|
11812
11914
|
// --- State Machine ---
|
|
11813
11915
|
get status() {
|
|
@@ -11861,7 +11963,7 @@ var Session = class extends TypedEmitter {
|
|
|
11861
11963
|
this.log.info({ voiceMode: mode }, "TTS mode changed");
|
|
11862
11964
|
}
|
|
11863
11965
|
// --- Public API ---
|
|
11864
|
-
async enqueuePrompt(text3, attachments) {
|
|
11966
|
+
async enqueuePrompt(text3, attachments, routing) {
|
|
11865
11967
|
if (this.middlewareChain) {
|
|
11866
11968
|
const payload = { text: text3, attachments, sessionId: this.id };
|
|
11867
11969
|
const result = await this.middlewareChain.execute("agent:beforePrompt", payload, async (p) => p);
|
|
@@ -11869,10 +11971,14 @@ var Session = class extends TypedEmitter {
|
|
|
11869
11971
|
text3 = result.text;
|
|
11870
11972
|
attachments = result.attachments;
|
|
11871
11973
|
}
|
|
11872
|
-
await this.queue.enqueue(text3, attachments);
|
|
11974
|
+
await this.queue.enqueue(text3, attachments, routing);
|
|
11873
11975
|
}
|
|
11874
|
-
async processPrompt(text3, attachments) {
|
|
11976
|
+
async processPrompt(text3, attachments, routing) {
|
|
11875
11977
|
if (this._status === "finished") return;
|
|
11978
|
+
this.activeTurnContext = createTurnContext(
|
|
11979
|
+
routing?.sourceAdapterId ?? this.channelId,
|
|
11980
|
+
routing?.responseAdapterId
|
|
11981
|
+
);
|
|
11876
11982
|
this.promptCount++;
|
|
11877
11983
|
this.emit("prompt_count_changed", this.promptCount);
|
|
11878
11984
|
if (this._status === "initializing" || this._status === "cancelled" || this._status === "error") {
|
|
@@ -11939,6 +12045,7 @@ ${text3}`;
|
|
|
11939
12045
|
this.log.warn({ err }, "TTS post-processing failed");
|
|
11940
12046
|
});
|
|
11941
12047
|
}
|
|
12048
|
+
this.activeTurnContext = null;
|
|
11942
12049
|
if (!this.name) {
|
|
11943
12050
|
await this.autoName();
|
|
11944
12051
|
}
|
|
@@ -12054,6 +12161,42 @@ ${result.text}` : result.text;
|
|
|
12054
12161
|
setAgentCapabilities(caps) {
|
|
12055
12162
|
this.agentCapabilities = caps;
|
|
12056
12163
|
}
|
|
12164
|
+
/**
|
|
12165
|
+
* Hydrate configOptions and agentCapabilities from a spawn response.
|
|
12166
|
+
* Handles both the native configOptions format and legacy modes/models fields.
|
|
12167
|
+
*/
|
|
12168
|
+
applySpawnResponse(resp, caps) {
|
|
12169
|
+
if (caps) this.agentCapabilities = caps;
|
|
12170
|
+
if (!resp) return;
|
|
12171
|
+
if (resp.configOptions) {
|
|
12172
|
+
this.configOptions = resp.configOptions;
|
|
12173
|
+
return;
|
|
12174
|
+
}
|
|
12175
|
+
const legacyOptions = [];
|
|
12176
|
+
if (resp.modes) {
|
|
12177
|
+
const m = resp.modes;
|
|
12178
|
+
legacyOptions.push({
|
|
12179
|
+
id: "mode",
|
|
12180
|
+
name: "Mode",
|
|
12181
|
+
category: "mode",
|
|
12182
|
+
type: "select",
|
|
12183
|
+
currentValue: m.currentModeId,
|
|
12184
|
+
options: m.availableModes.map((x) => ({ value: x.id, name: x.name, description: x.description }))
|
|
12185
|
+
});
|
|
12186
|
+
}
|
|
12187
|
+
if (resp.models) {
|
|
12188
|
+
const m = resp.models;
|
|
12189
|
+
legacyOptions.push({
|
|
12190
|
+
id: "model",
|
|
12191
|
+
name: "Model",
|
|
12192
|
+
category: "model",
|
|
12193
|
+
type: "select",
|
|
12194
|
+
currentValue: m.currentModelId,
|
|
12195
|
+
options: m.availableModels.map((x) => ({ value: x.modelId ?? x.id, name: x.name, description: x.description }))
|
|
12196
|
+
});
|
|
12197
|
+
}
|
|
12198
|
+
if (legacyOptions.length > 0) this.configOptions = legacyOptions;
|
|
12199
|
+
}
|
|
12057
12200
|
getConfigOption(id) {
|
|
12058
12201
|
return this.configOptions.find((o) => o.id === id);
|
|
12059
12202
|
}
|
|
@@ -12073,8 +12216,13 @@ ${result.text}` : result.text;
|
|
|
12073
12216
|
/** Send a config option change to the agent and update local state from the response. */
|
|
12074
12217
|
async setConfigOption(configId, value) {
|
|
12075
12218
|
const response = await this.agentInstance.setConfigOption(configId, value);
|
|
12076
|
-
if (response.configOptions) {
|
|
12219
|
+
if (response.configOptions && response.configOptions.length > 0) {
|
|
12077
12220
|
await this.updateConfigOptions(response.configOptions);
|
|
12221
|
+
} else if (value.type === "select") {
|
|
12222
|
+
const updated = this.configOptions.map(
|
|
12223
|
+
(o) => o.id === configId && o.type === "select" ? { ...o, currentValue: value.value } : o
|
|
12224
|
+
);
|
|
12225
|
+
await this.updateConfigOptions(updated);
|
|
12078
12226
|
}
|
|
12079
12227
|
}
|
|
12080
12228
|
async updateConfigOptions(options) {
|
|
@@ -12138,6 +12286,9 @@ ${result.text}` : result.text;
|
|
|
12138
12286
|
this.promptCount = 0;
|
|
12139
12287
|
this.agentCapabilities = void 0;
|
|
12140
12288
|
this.configOptions = [];
|
|
12289
|
+
this.latestCommands = null;
|
|
12290
|
+
this.applySpawnResponse(newAgent.initialSessionResponse, newAgent.agentCapabilities);
|
|
12291
|
+
this.wireCommandsBuffer();
|
|
12141
12292
|
this.log.info({ from: this.agentSwitchHistory.at(-1).agentName, to: agentName }, "Agent switched");
|
|
12142
12293
|
}
|
|
12143
12294
|
async destroy() {
|
|
@@ -12605,6 +12756,8 @@ var SessionManager = class {
|
|
|
12605
12756
|
}
|
|
12606
12757
|
getSessionByThread(channelId, threadId) {
|
|
12607
12758
|
for (const session of this.sessions.values()) {
|
|
12759
|
+
const adapterThread = session.threadIds.get(channelId);
|
|
12760
|
+
if (adapterThread === threadId) return session;
|
|
12608
12761
|
if (session.channelId === channelId && session.threadId === threadId) {
|
|
12609
12762
|
return session;
|
|
12610
12763
|
}
|
|
@@ -12672,6 +12825,67 @@ var SessionManager = class {
|
|
|
12672
12825
|
if (channelId) return all.filter((s) => s.channelId === channelId);
|
|
12673
12826
|
return all;
|
|
12674
12827
|
}
|
|
12828
|
+
listAllSessions(channelId) {
|
|
12829
|
+
if (this.store) {
|
|
12830
|
+
let records = this.store.list();
|
|
12831
|
+
if (channelId) records = records.filter((r) => r.channelId === channelId);
|
|
12832
|
+
return records.map((record) => {
|
|
12833
|
+
const live2 = this.sessions.get(record.sessionId);
|
|
12834
|
+
if (live2) {
|
|
12835
|
+
return {
|
|
12836
|
+
id: live2.id,
|
|
12837
|
+
agent: live2.agentName,
|
|
12838
|
+
status: live2.status,
|
|
12839
|
+
name: live2.name ?? null,
|
|
12840
|
+
workspace: live2.workingDirectory,
|
|
12841
|
+
channelId: live2.channelId,
|
|
12842
|
+
createdAt: live2.createdAt.toISOString(),
|
|
12843
|
+
lastActiveAt: record.lastActiveAt ?? null,
|
|
12844
|
+
dangerousMode: live2.clientOverrides.bypassPermissions ?? false,
|
|
12845
|
+
queueDepth: live2.queueDepth,
|
|
12846
|
+
promptRunning: live2.promptRunning,
|
|
12847
|
+
configOptions: live2.configOptions?.length ? live2.configOptions : void 0,
|
|
12848
|
+
capabilities: live2.agentCapabilities ?? null,
|
|
12849
|
+
isLive: true
|
|
12850
|
+
};
|
|
12851
|
+
}
|
|
12852
|
+
return {
|
|
12853
|
+
id: record.sessionId,
|
|
12854
|
+
agent: record.agentName,
|
|
12855
|
+
status: record.status,
|
|
12856
|
+
name: record.name ?? null,
|
|
12857
|
+
workspace: record.workingDir,
|
|
12858
|
+
channelId: record.channelId,
|
|
12859
|
+
createdAt: record.createdAt,
|
|
12860
|
+
lastActiveAt: record.lastActiveAt ?? null,
|
|
12861
|
+
dangerousMode: record.clientOverrides?.bypassPermissions ?? false,
|
|
12862
|
+
queueDepth: 0,
|
|
12863
|
+
promptRunning: false,
|
|
12864
|
+
configOptions: record.acpState?.configOptions,
|
|
12865
|
+
capabilities: record.acpState?.agentCapabilities ?? null,
|
|
12866
|
+
isLive: false
|
|
12867
|
+
};
|
|
12868
|
+
});
|
|
12869
|
+
}
|
|
12870
|
+
let live = Array.from(this.sessions.values());
|
|
12871
|
+
if (channelId) live = live.filter((s) => s.channelId === channelId);
|
|
12872
|
+
return live.map((s) => ({
|
|
12873
|
+
id: s.id,
|
|
12874
|
+
agent: s.agentName,
|
|
12875
|
+
status: s.status,
|
|
12876
|
+
name: s.name ?? null,
|
|
12877
|
+
workspace: s.workingDirectory,
|
|
12878
|
+
channelId: s.channelId,
|
|
12879
|
+
createdAt: s.createdAt.toISOString(),
|
|
12880
|
+
lastActiveAt: null,
|
|
12881
|
+
dangerousMode: s.clientOverrides.bypassPermissions ?? false,
|
|
12882
|
+
queueDepth: s.queueDepth,
|
|
12883
|
+
promptRunning: s.promptRunning,
|
|
12884
|
+
configOptions: s.configOptions?.length ? s.configOptions : void 0,
|
|
12885
|
+
capabilities: s.agentCapabilities ?? null,
|
|
12886
|
+
isLive: true
|
|
12887
|
+
}));
|
|
12888
|
+
}
|
|
12675
12889
|
listRecords(filter) {
|
|
12676
12890
|
if (!this.store) return [];
|
|
12677
12891
|
let records = this.store.list();
|
|
@@ -12694,7 +12908,14 @@ var SessionManager = class {
|
|
|
12694
12908
|
for (const session of this.sessions.values()) {
|
|
12695
12909
|
const record = this.store.get(session.id);
|
|
12696
12910
|
if (record) {
|
|
12697
|
-
await this.store.save({
|
|
12911
|
+
await this.store.save({
|
|
12912
|
+
...record,
|
|
12913
|
+
status: "finished",
|
|
12914
|
+
acpState: session.toAcpStateSnapshot(),
|
|
12915
|
+
clientOverrides: session.clientOverrides,
|
|
12916
|
+
currentPromptCount: session.promptCount,
|
|
12917
|
+
agentSwitchHistory: session.agentSwitchHistory
|
|
12918
|
+
});
|
|
12698
12919
|
}
|
|
12699
12920
|
}
|
|
12700
12921
|
this.store.flush();
|
|
@@ -12704,6 +12925,8 @@ var SessionManager = class {
|
|
|
12704
12925
|
/**
|
|
12705
12926
|
* Forcefully destroy all sessions (kill agent subprocesses).
|
|
12706
12927
|
* Use only when sessions must be fully torn down (e.g. archive).
|
|
12928
|
+
* Unlike shutdownAll(), this does NOT snapshot live session state (acpState, etc.)
|
|
12929
|
+
* because destroyed sessions are terminal and will not be resumed.
|
|
12707
12930
|
*/
|
|
12708
12931
|
async destroyAll() {
|
|
12709
12932
|
if (this.store) {
|
|
@@ -12734,13 +12957,15 @@ init_log();
|
|
|
12734
12957
|
init_bypass_detection();
|
|
12735
12958
|
var log6 = createChildLogger({ module: "session-bridge" });
|
|
12736
12959
|
var SessionBridge = class {
|
|
12737
|
-
constructor(session, adapter, deps) {
|
|
12960
|
+
constructor(session, adapter, deps, adapterId) {
|
|
12738
12961
|
this.session = session;
|
|
12739
12962
|
this.adapter = adapter;
|
|
12740
12963
|
this.deps = deps;
|
|
12964
|
+
this.adapterId = adapterId ?? adapter.name;
|
|
12741
12965
|
}
|
|
12742
12966
|
connected = false;
|
|
12743
12967
|
cleanupFns = [];
|
|
12968
|
+
adapterId;
|
|
12744
12969
|
get tracer() {
|
|
12745
12970
|
return this.session.agentInstance.debugTracer ?? null;
|
|
12746
12971
|
}
|
|
@@ -12771,6 +12996,15 @@ var SessionBridge = class {
|
|
|
12771
12996
|
log6.error({ err, sessionId }, "Error in sendMessage middleware");
|
|
12772
12997
|
}
|
|
12773
12998
|
}
|
|
12999
|
+
/** Determine if this bridge should forward the given event based on turn routing. */
|
|
13000
|
+
shouldForward(event) {
|
|
13001
|
+
if (isSystemEvent(event)) return true;
|
|
13002
|
+
const ctx = this.session.activeTurnContext;
|
|
13003
|
+
if (!ctx) return true;
|
|
13004
|
+
const target = getEffectiveTarget(ctx);
|
|
13005
|
+
if (target === null) return false;
|
|
13006
|
+
return this.adapterId === target;
|
|
13007
|
+
}
|
|
12774
13008
|
connect() {
|
|
12775
13009
|
if (this.connected) return;
|
|
12776
13010
|
this.connected = true;
|
|
@@ -12778,11 +13012,29 @@ var SessionBridge = class {
|
|
|
12778
13012
|
this.session.emit("agent_event", event);
|
|
12779
13013
|
});
|
|
12780
13014
|
this.listen(this.session, "agent_event", (event) => {
|
|
12781
|
-
this.
|
|
13015
|
+
if (this.shouldForward(event)) {
|
|
13016
|
+
this.dispatchAgentEvent(event);
|
|
13017
|
+
} else {
|
|
13018
|
+
this.deps.eventBus?.emit("agent:event", { sessionId: this.session.id, event });
|
|
13019
|
+
}
|
|
13020
|
+
});
|
|
13021
|
+
if (!this.session.agentInstance.onPermissionRequest || this.session.agentInstance.onPermissionRequest.__bridgeId === void 0) {
|
|
13022
|
+
const handler = async (request) => {
|
|
13023
|
+
return this.resolvePermission(request);
|
|
13024
|
+
};
|
|
13025
|
+
handler.__bridgeId = this.adapterId;
|
|
13026
|
+
this.session.agentInstance.onPermissionRequest = handler;
|
|
13027
|
+
}
|
|
13028
|
+
this.listen(this.session, "permission_request", async (request) => {
|
|
13029
|
+
const current = this.session.agentInstance.onPermissionRequest;
|
|
13030
|
+
if (current?.__bridgeId === this.adapterId) return;
|
|
13031
|
+
if (!this.session.permissionGate.isPending) return;
|
|
13032
|
+
try {
|
|
13033
|
+
await this.adapter.sendPermissionRequest(this.session.id, request);
|
|
13034
|
+
} catch (err) {
|
|
13035
|
+
log6.error({ err, sessionId: this.session.id, adapterId: this.adapterId }, "Failed to send permission request to adapter");
|
|
13036
|
+
}
|
|
12782
13037
|
});
|
|
12783
|
-
this.session.agentInstance.onPermissionRequest = async (request) => {
|
|
12784
|
-
return this.resolvePermission(request);
|
|
12785
|
-
};
|
|
12786
13038
|
this.listen(this.session, "status_change", (from, to) => {
|
|
12787
13039
|
this.deps.sessionManager.patchRecord(this.session.id, {
|
|
12788
13040
|
status: to,
|
|
@@ -12814,13 +13066,19 @@ var SessionBridge = class {
|
|
|
12814
13066
|
if (this.session.latestCommands !== null) {
|
|
12815
13067
|
this.session.emit("agent_event", { type: "commands_update", commands: this.session.latestCommands });
|
|
12816
13068
|
}
|
|
13069
|
+
if (this.session.configOptions.length > 0) {
|
|
13070
|
+
this.session.emit("agent_event", { type: "config_option_update", options: this.session.configOptions });
|
|
13071
|
+
}
|
|
12817
13072
|
}
|
|
12818
13073
|
disconnect() {
|
|
12819
13074
|
if (!this.connected) return;
|
|
12820
13075
|
this.connected = false;
|
|
12821
13076
|
this.cleanupFns.forEach((fn) => fn());
|
|
12822
13077
|
this.cleanupFns = [];
|
|
12823
|
-
this.session.agentInstance.onPermissionRequest
|
|
13078
|
+
const current = this.session.agentInstance.onPermissionRequest;
|
|
13079
|
+
if (current?.__bridgeId === this.adapterId) {
|
|
13080
|
+
this.session.agentInstance.onPermissionRequest = async () => "";
|
|
13081
|
+
}
|
|
12824
13082
|
}
|
|
12825
13083
|
/** Dispatch an agent event through middleware and to the adapter */
|
|
12826
13084
|
async dispatchAgentEvent(event) {
|
|
@@ -12951,8 +13209,10 @@ var SessionBridge = class {
|
|
|
12951
13209
|
this.sendMessage(this.session.id, outgoing);
|
|
12952
13210
|
break;
|
|
12953
13211
|
case "config_option_update":
|
|
12954
|
-
this.session.updateConfigOptions(event.options)
|
|
12955
|
-
|
|
13212
|
+
this.session.updateConfigOptions(event.options).then(() => {
|
|
13213
|
+
this.persistAcpState();
|
|
13214
|
+
}).catch(() => {
|
|
13215
|
+
});
|
|
12956
13216
|
outgoing = this.deps.messageTransformer.transform(event);
|
|
12957
13217
|
this.sendMessage(this.session.id, outgoing);
|
|
12958
13218
|
break;
|
|
@@ -12996,19 +13256,27 @@ var SessionBridge = class {
|
|
|
12996
13256
|
return result.autoResolve;
|
|
12997
13257
|
}
|
|
12998
13258
|
}
|
|
12999
|
-
this.session.emit("permission_request", permReq);
|
|
13000
13259
|
this.deps.eventBus?.emit("permission:request", {
|
|
13001
13260
|
sessionId: this.session.id,
|
|
13002
13261
|
permission: permReq
|
|
13003
13262
|
});
|
|
13004
13263
|
const autoDecision = this.checkAutoApprove(permReq);
|
|
13005
13264
|
if (autoDecision) {
|
|
13265
|
+
this.session.emit("permission_request", permReq);
|
|
13006
13266
|
this.emitAfterResolve(mw, permReq.id, autoDecision, "system", startTime);
|
|
13007
13267
|
return autoDecision;
|
|
13008
13268
|
}
|
|
13009
13269
|
const promise = this.session.permissionGate.setPending(permReq);
|
|
13270
|
+
this.session.emit("permission_request", permReq);
|
|
13010
13271
|
await this.adapter.sendPermissionRequest(this.session.id, permReq);
|
|
13011
13272
|
const optionId = await promise;
|
|
13273
|
+
this.deps.eventBus?.emit("permission:resolved", {
|
|
13274
|
+
sessionId: this.session.id,
|
|
13275
|
+
requestId: permReq.id,
|
|
13276
|
+
decision: optionId,
|
|
13277
|
+
optionId,
|
|
13278
|
+
resolvedBy: this.adapterId
|
|
13279
|
+
});
|
|
13012
13280
|
this.emitAfterResolve(mw, permReq.id, optionId, "user", startTime);
|
|
13013
13281
|
return optionId;
|
|
13014
13282
|
}
|
|
@@ -13184,17 +13452,7 @@ var SessionFactory = class {
|
|
|
13184
13452
|
if (createParams.initialName) {
|
|
13185
13453
|
session.name = createParams.initialName;
|
|
13186
13454
|
}
|
|
13187
|
-
|
|
13188
|
-
if (resp) {
|
|
13189
|
-
if (resp.configOptions) {
|
|
13190
|
-
session.setInitialConfigOptions(resp.configOptions);
|
|
13191
|
-
}
|
|
13192
|
-
if (agentInstance.agentCapabilities) {
|
|
13193
|
-
session.setAgentCapabilities(agentInstance.agentCapabilities);
|
|
13194
|
-
}
|
|
13195
|
-
} else if (agentInstance.agentCapabilities) {
|
|
13196
|
-
session.setAgentCapabilities(agentInstance.agentCapabilities);
|
|
13197
|
-
}
|
|
13455
|
+
session.applySpawnResponse(agentInstance.initialSessionResponse, agentInstance.agentCapabilities);
|
|
13198
13456
|
this.sessionManager.registerSession(session);
|
|
13199
13457
|
this.eventBus.emit("session:created", {
|
|
13200
13458
|
sessionId: session.id,
|
|
@@ -13212,6 +13470,65 @@ var SessionFactory = class {
|
|
|
13212
13470
|
if (session) return session;
|
|
13213
13471
|
return this.lazyResume(channelId, threadId);
|
|
13214
13472
|
}
|
|
13473
|
+
async getOrResumeById(sessionId) {
|
|
13474
|
+
const live = this.sessionManager.getSession(sessionId);
|
|
13475
|
+
if (live) return live;
|
|
13476
|
+
if (!this.sessionStore || !this.createFullSession) return null;
|
|
13477
|
+
const record = this.sessionStore.get(sessionId);
|
|
13478
|
+
if (!record) return null;
|
|
13479
|
+
if (record.status === "error" || record.status === "cancelled") return null;
|
|
13480
|
+
const existing = this.resumeLocks.get(sessionId);
|
|
13481
|
+
if (existing) return existing;
|
|
13482
|
+
const resumePromise = (async () => {
|
|
13483
|
+
try {
|
|
13484
|
+
const p = record.platform;
|
|
13485
|
+
const existingThreadId = p?.topicId ? String(p.topicId) : p?.threadId;
|
|
13486
|
+
const session = await this.createFullSession({
|
|
13487
|
+
channelId: record.channelId,
|
|
13488
|
+
agentName: record.agentName,
|
|
13489
|
+
workingDirectory: record.workingDir,
|
|
13490
|
+
resumeAgentSessionId: record.agentSessionId,
|
|
13491
|
+
existingSessionId: record.sessionId,
|
|
13492
|
+
initialName: record.name,
|
|
13493
|
+
threadId: existingThreadId
|
|
13494
|
+
});
|
|
13495
|
+
session.activate();
|
|
13496
|
+
if (record.clientOverrides) {
|
|
13497
|
+
session.clientOverrides = record.clientOverrides;
|
|
13498
|
+
} else if (record.dangerousMode) {
|
|
13499
|
+
session.clientOverrides = { bypassPermissions: true };
|
|
13500
|
+
}
|
|
13501
|
+
if (record.firstAgent) session.firstAgent = record.firstAgent;
|
|
13502
|
+
if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;
|
|
13503
|
+
if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;
|
|
13504
|
+
if (record.attachedAdapters) session.attachedAdapters = record.attachedAdapters;
|
|
13505
|
+
if (record.platforms) {
|
|
13506
|
+
for (const [adapterId, platformData] of Object.entries(record.platforms)) {
|
|
13507
|
+
const data = platformData;
|
|
13508
|
+
const tid = adapterId === "telegram" ? String(data.topicId ?? "") : String(data.threadId ?? "");
|
|
13509
|
+
if (tid) session.threadIds.set(adapterId, tid);
|
|
13510
|
+
}
|
|
13511
|
+
}
|
|
13512
|
+
if (record.acpState) {
|
|
13513
|
+
if (record.acpState.configOptions && session.configOptions.length === 0) {
|
|
13514
|
+
session.setInitialConfigOptions(record.acpState.configOptions);
|
|
13515
|
+
}
|
|
13516
|
+
if (record.acpState.agentCapabilities && !session.agentCapabilities) {
|
|
13517
|
+
session.setAgentCapabilities(record.acpState.agentCapabilities);
|
|
13518
|
+
}
|
|
13519
|
+
}
|
|
13520
|
+
log7.info({ sessionId }, "Lazy resume by ID successful");
|
|
13521
|
+
return session;
|
|
13522
|
+
} catch (err) {
|
|
13523
|
+
log7.error({ err, sessionId }, "Lazy resume by ID failed");
|
|
13524
|
+
return null;
|
|
13525
|
+
} finally {
|
|
13526
|
+
this.resumeLocks.delete(sessionId);
|
|
13527
|
+
}
|
|
13528
|
+
})();
|
|
13529
|
+
this.resumeLocks.set(sessionId, resumePromise);
|
|
13530
|
+
return resumePromise;
|
|
13531
|
+
}
|
|
13215
13532
|
async lazyResume(channelId, threadId) {
|
|
13216
13533
|
const store = this.sessionStore;
|
|
13217
13534
|
if (!store || !this.createFullSession) return null;
|
|
@@ -13220,7 +13537,7 @@ var SessionFactory = class {
|
|
|
13220
13537
|
if (existing) return existing;
|
|
13221
13538
|
const record = store.findByPlatform(
|
|
13222
13539
|
channelId,
|
|
13223
|
-
(p) => String(p.topicId) === threadId
|
|
13540
|
+
(p) => String(p.topicId) === threadId || String(p.threadId ?? "") === threadId
|
|
13224
13541
|
);
|
|
13225
13542
|
if (!record) {
|
|
13226
13543
|
log7.debug({ threadId, channelId }, "No session record found for thread");
|
|
@@ -13255,11 +13572,21 @@ var SessionFactory = class {
|
|
|
13255
13572
|
if (record.firstAgent) session.firstAgent = record.firstAgent;
|
|
13256
13573
|
if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;
|
|
13257
13574
|
if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;
|
|
13575
|
+
if (record.attachedAdapters) {
|
|
13576
|
+
session.attachedAdapters = record.attachedAdapters;
|
|
13577
|
+
}
|
|
13578
|
+
if (record.platforms) {
|
|
13579
|
+
for (const [adapterId, platformData] of Object.entries(record.platforms)) {
|
|
13580
|
+
const data = platformData;
|
|
13581
|
+
const tid = adapterId === "telegram" ? String(data.topicId ?? "") : String(data.threadId ?? "");
|
|
13582
|
+
if (tid) session.threadIds.set(adapterId, tid);
|
|
13583
|
+
}
|
|
13584
|
+
}
|
|
13258
13585
|
if (record.acpState) {
|
|
13259
|
-
if (record.acpState.configOptions) {
|
|
13586
|
+
if (record.acpState.configOptions && session.configOptions.length === 0) {
|
|
13260
13587
|
session.setInitialConfigOptions(record.acpState.configOptions);
|
|
13261
13588
|
}
|
|
13262
|
-
if (record.acpState.agentCapabilities) {
|
|
13589
|
+
if (record.acpState.agentCapabilities && !session.agentCapabilities) {
|
|
13263
13590
|
session.setAgentCapabilities(record.acpState.agentCapabilities);
|
|
13264
13591
|
}
|
|
13265
13592
|
}
|
|
@@ -13426,6 +13753,9 @@ var JsonFileSessionStore = class {
|
|
|
13426
13753
|
}
|
|
13427
13754
|
findByPlatform(channelId, predicate) {
|
|
13428
13755
|
for (const record of this.records.values()) {
|
|
13756
|
+
if (record.platforms?.[channelId]) {
|
|
13757
|
+
if (predicate(record.platforms[channelId])) return record;
|
|
13758
|
+
}
|
|
13429
13759
|
if (record.channelId === channelId && predicate(record.platform)) {
|
|
13430
13760
|
return record;
|
|
13431
13761
|
}
|
|
@@ -13492,7 +13822,7 @@ var JsonFileSessionStore = class {
|
|
|
13492
13822
|
return;
|
|
13493
13823
|
}
|
|
13494
13824
|
for (const [id, record] of Object.entries(raw.sessions)) {
|
|
13495
|
-
this.records.set(id, record);
|
|
13825
|
+
this.records.set(id, this.migrateRecord(record));
|
|
13496
13826
|
}
|
|
13497
13827
|
log8.debug({ count: this.records.size }, "Loaded session records");
|
|
13498
13828
|
} catch (err) {
|
|
@@ -13503,6 +13833,19 @@ var JsonFileSessionStore = class {
|
|
|
13503
13833
|
}
|
|
13504
13834
|
}
|
|
13505
13835
|
}
|
|
13836
|
+
/** Migrate old SessionRecord format to new multi-adapter format. */
|
|
13837
|
+
migrateRecord(record) {
|
|
13838
|
+
if (!record.platforms && record.platform && typeof record.platform === "object") {
|
|
13839
|
+
const platformData = record.platform;
|
|
13840
|
+
if (Object.keys(platformData).length > 0) {
|
|
13841
|
+
record.platforms = { [record.channelId]: platformData };
|
|
13842
|
+
}
|
|
13843
|
+
}
|
|
13844
|
+
if (!record.attachedAdapters) {
|
|
13845
|
+
record.attachedAdapters = [record.channelId];
|
|
13846
|
+
}
|
|
13847
|
+
return record;
|
|
13848
|
+
}
|
|
13506
13849
|
cleanup() {
|
|
13507
13850
|
const cutoff = Date.now() - this.ttlDays * 24 * 60 * 60 * 1e3;
|
|
13508
13851
|
let removed = 0;
|
|
@@ -13581,8 +13924,15 @@ var AgentSwitchHandler = class {
|
|
|
13581
13924
|
toAgent,
|
|
13582
13925
|
status: "starting"
|
|
13583
13926
|
});
|
|
13584
|
-
const
|
|
13585
|
-
|
|
13927
|
+
const sessionBridgeKeys = this.deps.getSessionBridgeKeys(sessionId);
|
|
13928
|
+
const hadBridges = sessionBridgeKeys.length > 0;
|
|
13929
|
+
for (const key of sessionBridgeKeys) {
|
|
13930
|
+
const bridge = bridges.get(key);
|
|
13931
|
+
if (bridge) {
|
|
13932
|
+
bridges.delete(key);
|
|
13933
|
+
bridge.disconnect();
|
|
13934
|
+
}
|
|
13935
|
+
}
|
|
13586
13936
|
const switchAdapter = adapters.get(session.channelId);
|
|
13587
13937
|
if (switchAdapter?.sendSkillCommands) {
|
|
13588
13938
|
await switchAdapter.sendSkillCommands(session.id, []);
|
|
@@ -13659,9 +14009,11 @@ var AgentSwitchHandler = class {
|
|
|
13659
14009
|
session.agentInstance = oldInstance;
|
|
13660
14010
|
session.agentName = fromAgent;
|
|
13661
14011
|
session.agentSessionId = oldInstance.sessionId;
|
|
13662
|
-
const
|
|
13663
|
-
|
|
13664
|
-
|
|
14012
|
+
for (const adapterId of session.attachedAdapters) {
|
|
14013
|
+
const adapter = adapters.get(adapterId);
|
|
14014
|
+
if (adapter) {
|
|
14015
|
+
createBridge(session, adapter, adapterId).connect();
|
|
14016
|
+
}
|
|
13665
14017
|
}
|
|
13666
14018
|
log9.warn({ sessionId, fromAgent, toAgent, err }, "Agent switch failed, rolled back to previous agent");
|
|
13667
14019
|
} catch (rollbackErr) {
|
|
@@ -13670,10 +14022,14 @@ var AgentSwitchHandler = class {
|
|
|
13670
14022
|
}
|
|
13671
14023
|
throw err;
|
|
13672
14024
|
}
|
|
13673
|
-
if (
|
|
13674
|
-
const
|
|
13675
|
-
|
|
13676
|
-
|
|
14025
|
+
if (hadBridges) {
|
|
14026
|
+
for (const adapterId of session.attachedAdapters) {
|
|
14027
|
+
const adapter = adapters.get(adapterId);
|
|
14028
|
+
if (adapter) {
|
|
14029
|
+
createBridge(session, adapter, adapterId).connect();
|
|
14030
|
+
} else {
|
|
14031
|
+
log9.warn({ sessionId, adapterId }, "Adapter not available during switch reconnect, skipping bridge");
|
|
14032
|
+
}
|
|
13677
14033
|
}
|
|
13678
14034
|
}
|
|
13679
14035
|
await sessionManager.patchRecord(sessionId, {
|
|
@@ -15126,7 +15482,7 @@ var OpenACPCore = class {
|
|
|
15126
15482
|
sessionManager;
|
|
15127
15483
|
messageTransformer;
|
|
15128
15484
|
adapters = /* @__PURE__ */ new Map();
|
|
15129
|
-
/** sessionId → SessionBridge — tracks active bridges for disconnect/reconnect
|
|
15485
|
+
/** "adapterId:sessionId" → SessionBridge — tracks active bridges for disconnect/reconnect */
|
|
15130
15486
|
bridges = /* @__PURE__ */ new Map();
|
|
15131
15487
|
/** Set by main.ts — triggers graceful shutdown with restart exit code */
|
|
15132
15488
|
requestRestart = null;
|
|
@@ -15222,7 +15578,8 @@ var OpenACPCore = class {
|
|
|
15222
15578
|
eventBus: this.eventBus,
|
|
15223
15579
|
adapters: this.adapters,
|
|
15224
15580
|
bridges: this.bridges,
|
|
15225
|
-
createBridge: (session, adapter) => this.createBridge(session, adapter),
|
|
15581
|
+
createBridge: (session, adapter, adapterId) => this.createBridge(session, adapter, adapterId),
|
|
15582
|
+
getSessionBridgeKeys: (sessionId) => this.getSessionBridgeKeys(sessionId),
|
|
15226
15583
|
getMiddlewareChain: () => this.lifecycleManager?.middlewareChain,
|
|
15227
15584
|
getService: (name) => this.lifecycleManager.serviceRegistry.get(name)
|
|
15228
15585
|
});
|
|
@@ -15402,7 +15759,7 @@ User message:
|
|
|
15402
15759
|
${text3}`;
|
|
15403
15760
|
}
|
|
15404
15761
|
}
|
|
15405
|
-
await session.enqueuePrompt(text3, message.attachments);
|
|
15762
|
+
await session.enqueuePrompt(text3, message.attachments, message.routing);
|
|
15406
15763
|
}
|
|
15407
15764
|
// --- Unified Session Creation Pipeline ---
|
|
15408
15765
|
async createSession(params) {
|
|
@@ -15429,6 +15786,12 @@ ${text3}`;
|
|
|
15429
15786
|
platform2.threadId = session.threadId;
|
|
15430
15787
|
}
|
|
15431
15788
|
}
|
|
15789
|
+
const platforms = {
|
|
15790
|
+
...existingRecord?.platforms ?? {}
|
|
15791
|
+
};
|
|
15792
|
+
if (session.threadId) {
|
|
15793
|
+
platforms[params.channelId] = params.channelId === "telegram" ? { topicId: Number(session.threadId) || session.threadId } : { threadId: session.threadId };
|
|
15794
|
+
}
|
|
15432
15795
|
await this.sessionManager.patchRecord(session.id, {
|
|
15433
15796
|
sessionId: session.id,
|
|
15434
15797
|
agentSessionId: session.agentSessionId,
|
|
@@ -15440,6 +15803,7 @@ ${text3}`;
|
|
|
15440
15803
|
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15441
15804
|
name: session.name,
|
|
15442
15805
|
platform: platform2,
|
|
15806
|
+
platforms,
|
|
15443
15807
|
firstAgent: session.firstAgent,
|
|
15444
15808
|
currentPromptCount: session.promptCount,
|
|
15445
15809
|
agentSwitchHistory: session.agentSwitchHistory,
|
|
@@ -15447,7 +15811,7 @@ ${text3}`;
|
|
|
15447
15811
|
acpState: session.toAcpStateSnapshot()
|
|
15448
15812
|
}, { immediate: true });
|
|
15449
15813
|
if (adapter) {
|
|
15450
|
-
const bridge = this.createBridge(session, adapter);
|
|
15814
|
+
const bridge = this.createBridge(session, adapter, session.channelId);
|
|
15451
15815
|
bridge.connect();
|
|
15452
15816
|
adapter.flushPendingSkillCommands?.(session.id).catch((err) => {
|
|
15453
15817
|
log16.warn({ err, sessionId: session.id }, "Failed to flush pending skill commands");
|
|
@@ -15586,9 +15950,14 @@ ${text3}`;
|
|
|
15586
15950
|
} else {
|
|
15587
15951
|
adoptPlatform.threadId = session.threadId;
|
|
15588
15952
|
}
|
|
15953
|
+
const adoptPlatforms = {};
|
|
15954
|
+
if (session.threadId) {
|
|
15955
|
+
adoptPlatforms[adapterChannelId] = adapterChannelId === "telegram" ? { topicId: Number(session.threadId) || session.threadId } : { threadId: session.threadId };
|
|
15956
|
+
}
|
|
15589
15957
|
await this.sessionManager.patchRecord(session.id, {
|
|
15590
15958
|
originalAgentSessionId: agentSessionId,
|
|
15591
|
-
platform: adoptPlatform
|
|
15959
|
+
platform: adoptPlatform,
|
|
15960
|
+
platforms: adoptPlatforms
|
|
15592
15961
|
});
|
|
15593
15962
|
return {
|
|
15594
15963
|
ok: true,
|
|
@@ -15610,18 +15979,101 @@ ${text3}`;
|
|
|
15610
15979
|
async getOrResumeSession(channelId, threadId) {
|
|
15611
15980
|
return this.sessionFactory.getOrResume(channelId, threadId);
|
|
15612
15981
|
}
|
|
15982
|
+
async getOrResumeSessionById(sessionId) {
|
|
15983
|
+
return this.sessionFactory.getOrResumeById(sessionId);
|
|
15984
|
+
}
|
|
15985
|
+
async attachAdapter(sessionId, adapterId) {
|
|
15986
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
15987
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
15988
|
+
const adapter = this.adapters.get(adapterId);
|
|
15989
|
+
if (!adapter) throw new Error(`Adapter "${adapterId}" not found or not running`);
|
|
15990
|
+
if (session.attachedAdapters.includes(adapterId)) {
|
|
15991
|
+
const existingThread = session.threadIds.get(adapterId) ?? session.id;
|
|
15992
|
+
return { threadId: existingThread };
|
|
15993
|
+
}
|
|
15994
|
+
const threadId = await adapter.createSessionThread(
|
|
15995
|
+
session.id,
|
|
15996
|
+
session.name ?? `Session ${session.id.slice(0, 6)}`
|
|
15997
|
+
);
|
|
15998
|
+
session.threadIds.set(adapterId, threadId);
|
|
15999
|
+
session.attachedAdapters.push(adapterId);
|
|
16000
|
+
const bridge = this.createBridge(session, adapter, adapterId);
|
|
16001
|
+
bridge.connect();
|
|
16002
|
+
await this.sessionManager.patchRecord(session.id, {
|
|
16003
|
+
attachedAdapters: session.attachedAdapters,
|
|
16004
|
+
platforms: this.buildPlatformsFromSession(session)
|
|
16005
|
+
});
|
|
16006
|
+
return { threadId };
|
|
16007
|
+
}
|
|
16008
|
+
async detachAdapter(sessionId, adapterId) {
|
|
16009
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
16010
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
16011
|
+
if (adapterId === session.channelId) {
|
|
16012
|
+
throw new Error("Cannot detach primary adapter (channelId)");
|
|
16013
|
+
}
|
|
16014
|
+
if (!session.attachedAdapters.includes(adapterId)) {
|
|
16015
|
+
return;
|
|
16016
|
+
}
|
|
16017
|
+
const adapter = this.adapters.get(adapterId);
|
|
16018
|
+
if (adapter) {
|
|
16019
|
+
try {
|
|
16020
|
+
await adapter.sendMessage(session.id, {
|
|
16021
|
+
type: "system_message",
|
|
16022
|
+
text: "Session detached from this adapter."
|
|
16023
|
+
});
|
|
16024
|
+
} catch {
|
|
16025
|
+
}
|
|
16026
|
+
}
|
|
16027
|
+
const key = this.bridgeKey(adapterId, session.id);
|
|
16028
|
+
const bridge = this.bridges.get(key);
|
|
16029
|
+
if (bridge) {
|
|
16030
|
+
bridge.disconnect();
|
|
16031
|
+
this.bridges.delete(key);
|
|
16032
|
+
}
|
|
16033
|
+
session.attachedAdapters = session.attachedAdapters.filter((a) => a !== adapterId);
|
|
16034
|
+
session.threadIds.delete(adapterId);
|
|
16035
|
+
await this.sessionManager.patchRecord(session.id, {
|
|
16036
|
+
attachedAdapters: session.attachedAdapters,
|
|
16037
|
+
platforms: this.buildPlatformsFromSession(session)
|
|
16038
|
+
});
|
|
16039
|
+
}
|
|
16040
|
+
buildPlatformsFromSession(session) {
|
|
16041
|
+
const platforms = {};
|
|
16042
|
+
for (const [adapterId, threadId] of session.threadIds) {
|
|
16043
|
+
if (adapterId === "telegram") {
|
|
16044
|
+
platforms.telegram = { topicId: Number(threadId) || threadId };
|
|
16045
|
+
} else {
|
|
16046
|
+
platforms[adapterId] = { threadId };
|
|
16047
|
+
}
|
|
16048
|
+
}
|
|
16049
|
+
return platforms;
|
|
16050
|
+
}
|
|
15613
16051
|
// --- Event Wiring ---
|
|
16052
|
+
/** Composite bridge key: "adapterId:sessionId" */
|
|
16053
|
+
bridgeKey(adapterId, sessionId) {
|
|
16054
|
+
return `${adapterId}:${sessionId}`;
|
|
16055
|
+
}
|
|
16056
|
+
/** Get all bridge keys for a session (regardless of adapter) */
|
|
16057
|
+
getSessionBridgeKeys(sessionId) {
|
|
16058
|
+
const keys = [];
|
|
16059
|
+
for (const key of this.bridges.keys()) {
|
|
16060
|
+
if (key.endsWith(`:${sessionId}`)) keys.push(key);
|
|
16061
|
+
}
|
|
16062
|
+
return keys;
|
|
16063
|
+
}
|
|
15614
16064
|
/** Connect a session bridge for the given session (used by AssistantManager) */
|
|
15615
16065
|
connectSessionBridge(session) {
|
|
15616
16066
|
const adapter = this.adapters.get(session.channelId);
|
|
15617
16067
|
if (!adapter) return;
|
|
15618
|
-
const bridge = this.createBridge(session, adapter);
|
|
16068
|
+
const bridge = this.createBridge(session, adapter, session.channelId);
|
|
15619
16069
|
bridge.connect();
|
|
15620
16070
|
}
|
|
15621
16071
|
/** Create a SessionBridge for the given session and adapter.
|
|
15622
|
-
* Disconnects any existing bridge for the same session first. */
|
|
15623
|
-
createBridge(session, adapter) {
|
|
15624
|
-
const
|
|
16072
|
+
* Disconnects any existing bridge for the same adapter+session first. */
|
|
16073
|
+
createBridge(session, adapter, adapterId) {
|
|
16074
|
+
const id = adapterId ?? adapter.name;
|
|
16075
|
+
const key = this.bridgeKey(id, session.id);
|
|
16076
|
+
const existing = this.bridges.get(key);
|
|
15625
16077
|
if (existing) {
|
|
15626
16078
|
existing.disconnect();
|
|
15627
16079
|
}
|
|
@@ -15632,8 +16084,8 @@ ${text3}`;
|
|
|
15632
16084
|
eventBus: this.eventBus,
|
|
15633
16085
|
fileService: this.fileService,
|
|
15634
16086
|
middlewareChain: this.lifecycleManager?.middlewareChain
|
|
15635
|
-
});
|
|
15636
|
-
this.bridges.set(
|
|
16087
|
+
}, id);
|
|
16088
|
+
this.bridges.set(key, bridge);
|
|
15637
16089
|
return bridge;
|
|
15638
16090
|
}
|
|
15639
16091
|
};
|
|
@@ -17668,12 +18120,14 @@ export {
|
|
|
17668
18120
|
createApiServerService,
|
|
17669
18121
|
createChildLogger,
|
|
17670
18122
|
createSessionLogger,
|
|
18123
|
+
createTurnContext,
|
|
17671
18124
|
expandHome3 as expandHome,
|
|
17672
18125
|
extractContentText,
|
|
17673
18126
|
formatTokens,
|
|
17674
18127
|
formatToolSummary,
|
|
17675
18128
|
formatToolTitle,
|
|
17676
18129
|
getConfigValue,
|
|
18130
|
+
getEffectiveTarget,
|
|
17677
18131
|
getFieldDef,
|
|
17678
18132
|
getPidPath,
|
|
17679
18133
|
getSafeFields,
|
|
@@ -17683,6 +18137,7 @@ export {
|
|
|
17683
18137
|
isAutoStartInstalled,
|
|
17684
18138
|
isAutoStartSupported,
|
|
17685
18139
|
isHotReloadable,
|
|
18140
|
+
isSystemEvent,
|
|
17686
18141
|
log,
|
|
17687
18142
|
nodeToWebReadable,
|
|
17688
18143
|
nodeToWebWritable,
|