@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/cli.js
CHANGED
|
@@ -6611,16 +6611,20 @@ var init_tunnel_service = __esm({
|
|
|
6611
6611
|
return entry.publicUrl || `http://localhost:${apiPort}`;
|
|
6612
6612
|
} catch (err) {
|
|
6613
6613
|
if (this.config.provider === "openacp") {
|
|
6614
|
-
|
|
6614
|
+
const reason = err.message ?? String(err);
|
|
6615
|
+
log11.error({ err: reason }, "[tunnel] OpenACP tunnel failed \u2014 falling back to Cloudflare quick tunnel");
|
|
6615
6616
|
try {
|
|
6616
6617
|
const fallbackEntry = await this.registry.add(apiPort, {
|
|
6617
6618
|
type: "system",
|
|
6618
6619
|
provider: "cloudflare",
|
|
6619
6620
|
label: "system"
|
|
6620
6621
|
});
|
|
6621
|
-
|
|
6622
|
-
|
|
6622
|
+
const fallbackUrl = fallbackEntry.publicUrl || `http://localhost:${apiPort}`;
|
|
6623
|
+
log11.warn({ url: fallbackUrl, reason }, "[tunnel] Cloudflare fallback tunnel active");
|
|
6624
|
+
this.startError = `OpenACP tunnel unavailable (${reason}) \u2014 using Cloudflare quick tunnel`;
|
|
6625
|
+
return fallbackUrl;
|
|
6623
6626
|
} catch (fallbackErr) {
|
|
6627
|
+
log11.error({ err: fallbackErr.message }, "[tunnel] Cloudflare fallback also failed \u2014 no public URL");
|
|
6624
6628
|
this.startError = fallbackErr.message;
|
|
6625
6629
|
return `http://localhost:${apiPort}`;
|
|
6626
6630
|
}
|
|
@@ -8068,7 +8072,10 @@ var init_sessions = __esm({
|
|
|
8068
8072
|
});
|
|
8069
8073
|
PromptBodySchema = z.object({
|
|
8070
8074
|
// 100 KB limit — prevents memory exhaustion / DoS via enormous payloads
|
|
8071
|
-
prompt: z.string().min(1).max(1e5)
|
|
8075
|
+
prompt: z.string().min(1).max(1e5),
|
|
8076
|
+
// Multi-adapter routing fields
|
|
8077
|
+
sourceAdapterId: z.string().optional(),
|
|
8078
|
+
responseAdapterId: z.string().nullable().optional()
|
|
8072
8079
|
});
|
|
8073
8080
|
PermissionResponseBodySchema = z.object({
|
|
8074
8081
|
permissionId: z.string().min(1).max(200),
|
|
@@ -8105,22 +8112,23 @@ __export(sessions_exports, {
|
|
|
8105
8112
|
});
|
|
8106
8113
|
async function sessionRoutes(app, deps) {
|
|
8107
8114
|
app.get("/", { preHandler: requireScopes("sessions:read") }, async () => {
|
|
8108
|
-
const
|
|
8115
|
+
const summaries = deps.core.sessionManager.listAllSessions();
|
|
8109
8116
|
return {
|
|
8110
|
-
sessions:
|
|
8117
|
+
sessions: summaries.map((s) => ({
|
|
8111
8118
|
id: s.id,
|
|
8112
|
-
agent: s.
|
|
8119
|
+
agent: s.agent,
|
|
8113
8120
|
status: s.status,
|
|
8114
|
-
name: s.name
|
|
8115
|
-
workspace: s.
|
|
8116
|
-
|
|
8117
|
-
|
|
8121
|
+
name: s.name,
|
|
8122
|
+
workspace: s.workspace,
|
|
8123
|
+
channelId: s.channelId,
|
|
8124
|
+
createdAt: s.createdAt,
|
|
8125
|
+
lastActiveAt: s.lastActiveAt,
|
|
8126
|
+
dangerousMode: s.dangerousMode,
|
|
8118
8127
|
queueDepth: s.queueDepth,
|
|
8119
8128
|
promptRunning: s.promptRunning,
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
capabilities: s.agentCapabilities ?? null
|
|
8129
|
+
configOptions: s.configOptions,
|
|
8130
|
+
capabilities: s.capabilities,
|
|
8131
|
+
isLive: s.isLive
|
|
8124
8132
|
}))
|
|
8125
8133
|
};
|
|
8126
8134
|
});
|
|
@@ -8232,7 +8240,7 @@ async function sessionRoutes(app, deps) {
|
|
|
8232
8240
|
async (request, reply) => {
|
|
8233
8241
|
const { sessionId: rawId } = SessionIdParamSchema.parse(request.params);
|
|
8234
8242
|
const sessionId = decodeURIComponent(rawId);
|
|
8235
|
-
const session = deps.core.
|
|
8243
|
+
const session = await deps.core.getOrResumeSessionById(sessionId);
|
|
8236
8244
|
if (!session) {
|
|
8237
8245
|
throw new NotFoundError(
|
|
8238
8246
|
"SESSION_NOT_FOUND",
|
|
@@ -8243,7 +8251,10 @@ async function sessionRoutes(app, deps) {
|
|
|
8243
8251
|
return reply.status(400).send({ error: `Session is ${session.status}` });
|
|
8244
8252
|
}
|
|
8245
8253
|
const body = PromptBodySchema.parse(request.body);
|
|
8246
|
-
await session.enqueuePrompt(body.prompt
|
|
8254
|
+
await session.enqueuePrompt(body.prompt, void 0, {
|
|
8255
|
+
sourceAdapterId: body.sourceAdapterId ?? "api",
|
|
8256
|
+
responseAdapterId: body.responseAdapterId
|
|
8257
|
+
});
|
|
8247
8258
|
return {
|
|
8248
8259
|
ok: true,
|
|
8249
8260
|
sessionId,
|
|
@@ -8290,6 +8301,9 @@ async function sessionRoutes(app, deps) {
|
|
|
8290
8301
|
const body = UpdateSessionBodySchema.parse(request.body);
|
|
8291
8302
|
const changes = {};
|
|
8292
8303
|
if (body.agentName !== void 0) {
|
|
8304
|
+
if (session.promptRunning) {
|
|
8305
|
+
await session.abortPrompt();
|
|
8306
|
+
}
|
|
8293
8307
|
const result = await deps.core.switchSessionAgent(sessionId, body.agentName);
|
|
8294
8308
|
changes.agentName = body.agentName;
|
|
8295
8309
|
changes.resumed = result.resumed;
|
|
@@ -8425,6 +8439,36 @@ async function sessionRoutes(app, deps) {
|
|
|
8425
8439
|
}
|
|
8426
8440
|
}
|
|
8427
8441
|
);
|
|
8442
|
+
app.post(
|
|
8443
|
+
"/:sessionId/attach",
|
|
8444
|
+
{ preHandler: requireScopes("sessions:write") },
|
|
8445
|
+
async (request, reply) => {
|
|
8446
|
+
const { sessionId } = request.params;
|
|
8447
|
+
const { adapterId } = request.body ?? {};
|
|
8448
|
+
if (!adapterId) return reply.code(400).send({ error: "adapterId is required" });
|
|
8449
|
+
try {
|
|
8450
|
+
const result = await deps.core.attachAdapter(sessionId, adapterId);
|
|
8451
|
+
return { ok: true, threadId: result.threadId };
|
|
8452
|
+
} catch (err) {
|
|
8453
|
+
return reply.code(400).send({ error: err.message });
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
);
|
|
8457
|
+
app.post(
|
|
8458
|
+
"/:sessionId/detach",
|
|
8459
|
+
{ preHandler: requireScopes("sessions:write") },
|
|
8460
|
+
async (request, reply) => {
|
|
8461
|
+
const { sessionId } = request.params;
|
|
8462
|
+
const { adapterId } = request.body ?? {};
|
|
8463
|
+
if (!adapterId) return reply.code(400).send({ error: "adapterId is required" });
|
|
8464
|
+
try {
|
|
8465
|
+
await deps.core.detachAdapter(sessionId, adapterId);
|
|
8466
|
+
return { ok: true };
|
|
8467
|
+
} catch (err) {
|
|
8468
|
+
return reply.code(400).send({ error: err.message });
|
|
8469
|
+
}
|
|
8470
|
+
}
|
|
8471
|
+
);
|
|
8428
8472
|
app.get(
|
|
8429
8473
|
"/:sessionId/history",
|
|
8430
8474
|
{ preHandler: requireScopes("sessions:read") },
|
|
@@ -10603,7 +10647,7 @@ async function sseRoutes(app, deps) {
|
|
|
10603
10647
|
return reply.status(400).send({ error: `Session is ${session.status}` });
|
|
10604
10648
|
}
|
|
10605
10649
|
const body = PromptBodySchema.parse(request.body);
|
|
10606
|
-
await session.enqueuePrompt(body.prompt);
|
|
10650
|
+
await session.enqueuePrompt(body.prompt, void 0, { sourceAdapterId: "sse" });
|
|
10607
10651
|
return { ok: true, sessionId, queueDepth: session.queueDepth };
|
|
10608
10652
|
}
|
|
10609
10653
|
);
|
|
@@ -11411,7 +11455,7 @@ var BYPASS_KEYWORDS;
|
|
|
11411
11455
|
var init_bypass_detection = __esm({
|
|
11412
11456
|
"src/core/utils/bypass-detection.ts"() {
|
|
11413
11457
|
"use strict";
|
|
11414
|
-
BYPASS_KEYWORDS = ["bypass", "dangerous", "auto_accept"];
|
|
11458
|
+
BYPASS_KEYWORDS = ["bypass", "dangerous", "auto_accept", "yolo"];
|
|
11415
11459
|
}
|
|
11416
11460
|
});
|
|
11417
11461
|
|
|
@@ -18855,6 +18899,7 @@ var init_agent_instance = __esm({
|
|
|
18855
18899
|
{ sessionId: this.sessionId, exitCode: code, signal },
|
|
18856
18900
|
"Agent process exited"
|
|
18857
18901
|
);
|
|
18902
|
+
if (signal === "SIGINT" || signal === "SIGTERM") return;
|
|
18858
18903
|
if (code !== 0 && code !== null || signal) {
|
|
18859
18904
|
const stderr = this.stderrCapture.getLastLines();
|
|
18860
18905
|
this.emit("agent_event", {
|
|
@@ -18884,12 +18929,18 @@ ${stderr}`
|
|
|
18884
18929
|
cwd: workingDirectory,
|
|
18885
18930
|
mcpServers: resolvedMcp
|
|
18886
18931
|
});
|
|
18932
|
+
log29.info(response, "newSession response");
|
|
18887
18933
|
instance.sessionId = response.sessionId;
|
|
18888
18934
|
instance.initialSessionResponse = response;
|
|
18889
18935
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
18890
18936
|
instance.setupCrashDetection();
|
|
18891
18937
|
log29.info(
|
|
18892
|
-
{
|
|
18938
|
+
{
|
|
18939
|
+
sessionId: response.sessionId,
|
|
18940
|
+
durationMs: Date.now() - spawnStart,
|
|
18941
|
+
configOptions: response.configOptions ?? [],
|
|
18942
|
+
agentCapabilities: instance.agentCapabilities ?? null
|
|
18943
|
+
},
|
|
18893
18944
|
"Agent spawn complete"
|
|
18894
18945
|
);
|
|
18895
18946
|
return instance;
|
|
@@ -18901,24 +18952,47 @@ ${stderr}`
|
|
|
18901
18952
|
agentDef,
|
|
18902
18953
|
workingDirectory
|
|
18903
18954
|
);
|
|
18955
|
+
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
18904
18956
|
try {
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
18913
|
-
|
|
18914
|
-
|
|
18915
|
-
|
|
18957
|
+
if (instance.agentCapabilities?.loadSession) {
|
|
18958
|
+
const response = await instance.connection.loadSession({
|
|
18959
|
+
sessionId: agentSessionId,
|
|
18960
|
+
cwd: workingDirectory,
|
|
18961
|
+
mcpServers: resolvedMcp
|
|
18962
|
+
});
|
|
18963
|
+
instance.sessionId = agentSessionId;
|
|
18964
|
+
instance.initialSessionResponse = response;
|
|
18965
|
+
instance.debugTracer = createDebugTracer(agentSessionId, workingDirectory);
|
|
18966
|
+
log29.info(
|
|
18967
|
+
{
|
|
18968
|
+
sessionId: agentSessionId,
|
|
18969
|
+
durationMs: Date.now() - spawnStart,
|
|
18970
|
+
agentCapabilities: instance.agentCapabilities ?? null
|
|
18971
|
+
},
|
|
18972
|
+
"Agent load complete"
|
|
18973
|
+
);
|
|
18974
|
+
} else {
|
|
18975
|
+
const response = await instance.connection.unstable_resumeSession({
|
|
18976
|
+
sessionId: agentSessionId,
|
|
18977
|
+
cwd: workingDirectory
|
|
18978
|
+
});
|
|
18979
|
+
instance.sessionId = response.sessionId;
|
|
18980
|
+
instance.initialSessionResponse = response;
|
|
18981
|
+
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
18982
|
+
log29.info(
|
|
18983
|
+
{
|
|
18984
|
+
sessionId: response.sessionId,
|
|
18985
|
+
durationMs: Date.now() - spawnStart,
|
|
18986
|
+
agentCapabilities: instance.agentCapabilities ?? null
|
|
18987
|
+
},
|
|
18988
|
+
"Agent resume complete"
|
|
18989
|
+
);
|
|
18990
|
+
}
|
|
18916
18991
|
} catch (err) {
|
|
18917
18992
|
log29.warn(
|
|
18918
18993
|
{ err, agentSessionId },
|
|
18919
18994
|
"Resume failed, falling back to new session"
|
|
18920
18995
|
);
|
|
18921
|
-
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
18922
18996
|
const response = await instance.connection.newSession({
|
|
18923
18997
|
cwd: workingDirectory,
|
|
18924
18998
|
mcpServers: resolvedMcp
|
|
@@ -19149,11 +19223,31 @@ ${stderr}`
|
|
|
19149
19223
|
}
|
|
19150
19224
|
// ── New ACP methods ──────────────────────────────────────────────────
|
|
19151
19225
|
async setConfigOption(configId, value) {
|
|
19152
|
-
|
|
19153
|
-
|
|
19154
|
-
|
|
19155
|
-
|
|
19156
|
-
|
|
19226
|
+
try {
|
|
19227
|
+
return await this.connection.setSessionConfigOption({
|
|
19228
|
+
sessionId: this.sessionId,
|
|
19229
|
+
configId,
|
|
19230
|
+
...value
|
|
19231
|
+
});
|
|
19232
|
+
} catch (err) {
|
|
19233
|
+
if (typeof err === "object" && err !== null && err.code === -32601) {
|
|
19234
|
+
if (configId === "mode" && value.type === "select") {
|
|
19235
|
+
await this.connection.setSessionMode({
|
|
19236
|
+
sessionId: this.sessionId,
|
|
19237
|
+
modeId: value.value
|
|
19238
|
+
});
|
|
19239
|
+
return { configOptions: [] };
|
|
19240
|
+
}
|
|
19241
|
+
if (configId === "model" && value.type === "select") {
|
|
19242
|
+
await this.connection.unstable_setSessionModel({
|
|
19243
|
+
sessionId: this.sessionId,
|
|
19244
|
+
modelId: value.value
|
|
19245
|
+
});
|
|
19246
|
+
return { configOptions: [] };
|
|
19247
|
+
}
|
|
19248
|
+
}
|
|
19249
|
+
throw err;
|
|
19250
|
+
}
|
|
19157
19251
|
}
|
|
19158
19252
|
async listSessions(cwd, cursor) {
|
|
19159
19253
|
return await this.connection.listSessions({
|
|
@@ -19302,21 +19396,21 @@ var init_prompt_queue = __esm({
|
|
|
19302
19396
|
queue = [];
|
|
19303
19397
|
processing = false;
|
|
19304
19398
|
abortController = null;
|
|
19305
|
-
async enqueue(text6, attachments) {
|
|
19399
|
+
async enqueue(text6, attachments, routing) {
|
|
19306
19400
|
if (this.processing) {
|
|
19307
19401
|
return new Promise((resolve8) => {
|
|
19308
|
-
this.queue.push({ text: text6, attachments, resolve: resolve8 });
|
|
19402
|
+
this.queue.push({ text: text6, attachments, routing, resolve: resolve8 });
|
|
19309
19403
|
});
|
|
19310
19404
|
}
|
|
19311
|
-
await this.process(text6, attachments);
|
|
19405
|
+
await this.process(text6, attachments, routing);
|
|
19312
19406
|
}
|
|
19313
|
-
async process(text6, attachments) {
|
|
19407
|
+
async process(text6, attachments, routing) {
|
|
19314
19408
|
this.processing = true;
|
|
19315
19409
|
this.abortController = new AbortController();
|
|
19316
19410
|
const { signal } = this.abortController;
|
|
19317
19411
|
try {
|
|
19318
19412
|
await Promise.race([
|
|
19319
|
-
this.processor(text6, attachments),
|
|
19413
|
+
this.processor(text6, attachments, routing),
|
|
19320
19414
|
new Promise((_, reject) => {
|
|
19321
19415
|
signal.addEventListener("abort", () => reject(new Error("Prompt aborted")), { once: true });
|
|
19322
19416
|
})
|
|
@@ -19334,7 +19428,7 @@ var init_prompt_queue = __esm({
|
|
|
19334
19428
|
drainNext() {
|
|
19335
19429
|
const next = this.queue.shift();
|
|
19336
19430
|
if (next) {
|
|
19337
|
-
this.process(next.text, next.attachments).then(next.resolve);
|
|
19431
|
+
this.process(next.text, next.attachments, next.routing).then(next.resolve);
|
|
19338
19432
|
}
|
|
19339
19433
|
}
|
|
19340
19434
|
clear() {
|
|
@@ -19429,8 +19523,39 @@ var init_permission_gate = __esm({
|
|
|
19429
19523
|
}
|
|
19430
19524
|
});
|
|
19431
19525
|
|
|
19432
|
-
// src/core/sessions/
|
|
19526
|
+
// src/core/sessions/turn-context.ts
|
|
19433
19527
|
import { nanoid as nanoid3 } from "nanoid";
|
|
19528
|
+
function createTurnContext(sourceAdapterId, responseAdapterId) {
|
|
19529
|
+
return {
|
|
19530
|
+
turnId: nanoid3(8),
|
|
19531
|
+
sourceAdapterId,
|
|
19532
|
+
responseAdapterId
|
|
19533
|
+
};
|
|
19534
|
+
}
|
|
19535
|
+
function getEffectiveTarget(ctx) {
|
|
19536
|
+
if (ctx.responseAdapterId === null) return null;
|
|
19537
|
+
return ctx.responseAdapterId ?? ctx.sourceAdapterId;
|
|
19538
|
+
}
|
|
19539
|
+
function isSystemEvent(event) {
|
|
19540
|
+
return SYSTEM_EVENT_TYPES.has(event.type);
|
|
19541
|
+
}
|
|
19542
|
+
var SYSTEM_EVENT_TYPES;
|
|
19543
|
+
var init_turn_context = __esm({
|
|
19544
|
+
"src/core/sessions/turn-context.ts"() {
|
|
19545
|
+
"use strict";
|
|
19546
|
+
SYSTEM_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
19547
|
+
"session_end",
|
|
19548
|
+
"system_message",
|
|
19549
|
+
"session_info_update",
|
|
19550
|
+
"config_option_update",
|
|
19551
|
+
"commands_update",
|
|
19552
|
+
"tts_strip"
|
|
19553
|
+
]);
|
|
19554
|
+
}
|
|
19555
|
+
});
|
|
19556
|
+
|
|
19557
|
+
// src/core/sessions/session.ts
|
|
19558
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
19434
19559
|
import * as fs41 from "fs";
|
|
19435
19560
|
var moduleLog, TTS_PROMPT_INSTRUCTION, TTS_BLOCK_REGEX, TTS_MAX_LENGTH, TTS_TIMEOUT_MS, VALID_TRANSITIONS, Session;
|
|
19436
19561
|
var init_session2 = __esm({
|
|
@@ -19440,6 +19565,7 @@ var init_session2 = __esm({
|
|
|
19440
19565
|
init_prompt_queue();
|
|
19441
19566
|
init_permission_gate();
|
|
19442
19567
|
init_log();
|
|
19568
|
+
init_turn_context();
|
|
19443
19569
|
moduleLog = createChildLogger({ module: "session" });
|
|
19444
19570
|
TTS_PROMPT_INSTRUCTION = `
|
|
19445
19571
|
|
|
@@ -19457,7 +19583,15 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
19457
19583
|
Session = class extends TypedEmitter {
|
|
19458
19584
|
id;
|
|
19459
19585
|
channelId;
|
|
19460
|
-
threadId
|
|
19586
|
+
/** @deprecated Use threadIds map directly. Getter returns primary adapter's threadId. */
|
|
19587
|
+
get threadId() {
|
|
19588
|
+
return this.threadIds.get(this.channelId) ?? "";
|
|
19589
|
+
}
|
|
19590
|
+
set threadId(value) {
|
|
19591
|
+
if (value) {
|
|
19592
|
+
this.threadIds.set(this.channelId, value);
|
|
19593
|
+
}
|
|
19594
|
+
}
|
|
19461
19595
|
agentName;
|
|
19462
19596
|
workingDirectory;
|
|
19463
19597
|
agentInstance;
|
|
@@ -19478,14 +19612,21 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
19478
19612
|
middlewareChain;
|
|
19479
19613
|
/** Latest commands emitted by the agent — buffered before bridge connects so they're not lost */
|
|
19480
19614
|
latestCommands = null;
|
|
19615
|
+
/** Adapters currently attached to this session (including primary) */
|
|
19616
|
+
attachedAdapters = [];
|
|
19617
|
+
/** Per-adapter thread IDs: adapterId → threadId */
|
|
19618
|
+
threadIds = /* @__PURE__ */ new Map();
|
|
19619
|
+
/** Active turn context — sealed on prompt dequeue, cleared on turn end */
|
|
19620
|
+
activeTurnContext = null;
|
|
19481
19621
|
permissionGate = new PermissionGate();
|
|
19482
19622
|
queue;
|
|
19483
19623
|
speechService;
|
|
19484
19624
|
pendingContext = null;
|
|
19485
19625
|
constructor(opts) {
|
|
19486
19626
|
super();
|
|
19487
|
-
this.id = opts.id ||
|
|
19627
|
+
this.id = opts.id || nanoid4(12);
|
|
19488
19628
|
this.channelId = opts.channelId;
|
|
19629
|
+
this.attachedAdapters = [opts.channelId];
|
|
19489
19630
|
this.agentName = opts.agentName;
|
|
19490
19631
|
this.firstAgent = opts.agentName;
|
|
19491
19632
|
this.workingDirectory = opts.workingDirectory;
|
|
@@ -19495,18 +19636,28 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
19495
19636
|
this.log = createSessionLogger(this.id, moduleLog);
|
|
19496
19637
|
this.log.info({ agentName: this.agentName }, "Session created");
|
|
19497
19638
|
this.queue = new PromptQueue(
|
|
19498
|
-
(text6, attachments) => this.processPrompt(text6, attachments),
|
|
19639
|
+
(text6, attachments, routing) => this.processPrompt(text6, attachments, routing),
|
|
19499
19640
|
(err) => {
|
|
19500
19641
|
this.log.error({ err }, "Prompt execution failed");
|
|
19501
19642
|
const message = err instanceof Error ? err.message : String(err);
|
|
19643
|
+
this.fail(message);
|
|
19502
19644
|
this.emit("agent_event", { type: "error", message: `Prompt execution failed: ${message}` });
|
|
19503
19645
|
}
|
|
19504
19646
|
);
|
|
19505
|
-
this.
|
|
19647
|
+
this.wireCommandsBuffer();
|
|
19648
|
+
}
|
|
19649
|
+
/** Wire a listener on the current agentInstance to buffer commands_update events.
|
|
19650
|
+
* Must be called after every agentInstance replacement (constructor + switchAgent). */
|
|
19651
|
+
commandsBufferCleanup;
|
|
19652
|
+
wireCommandsBuffer() {
|
|
19653
|
+
this.commandsBufferCleanup?.();
|
|
19654
|
+
const handler = (event) => {
|
|
19506
19655
|
if (event.type === "commands_update") {
|
|
19507
19656
|
this.latestCommands = event.commands;
|
|
19508
19657
|
}
|
|
19509
|
-
}
|
|
19658
|
+
};
|
|
19659
|
+
this.agentInstance.on("agent_event", handler);
|
|
19660
|
+
this.commandsBufferCleanup = () => this.agentInstance.off("agent_event", handler);
|
|
19510
19661
|
}
|
|
19511
19662
|
// --- State Machine ---
|
|
19512
19663
|
get status() {
|
|
@@ -19560,7 +19711,7 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
19560
19711
|
this.log.info({ voiceMode: mode }, "TTS mode changed");
|
|
19561
19712
|
}
|
|
19562
19713
|
// --- Public API ---
|
|
19563
|
-
async enqueuePrompt(text6, attachments) {
|
|
19714
|
+
async enqueuePrompt(text6, attachments, routing) {
|
|
19564
19715
|
if (this.middlewareChain) {
|
|
19565
19716
|
const payload = { text: text6, attachments, sessionId: this.id };
|
|
19566
19717
|
const result = await this.middlewareChain.execute("agent:beforePrompt", payload, async (p2) => p2);
|
|
@@ -19568,10 +19719,14 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
19568
19719
|
text6 = result.text;
|
|
19569
19720
|
attachments = result.attachments;
|
|
19570
19721
|
}
|
|
19571
|
-
await this.queue.enqueue(text6, attachments);
|
|
19722
|
+
await this.queue.enqueue(text6, attachments, routing);
|
|
19572
19723
|
}
|
|
19573
|
-
async processPrompt(text6, attachments) {
|
|
19724
|
+
async processPrompt(text6, attachments, routing) {
|
|
19574
19725
|
if (this._status === "finished") return;
|
|
19726
|
+
this.activeTurnContext = createTurnContext(
|
|
19727
|
+
routing?.sourceAdapterId ?? this.channelId,
|
|
19728
|
+
routing?.responseAdapterId
|
|
19729
|
+
);
|
|
19575
19730
|
this.promptCount++;
|
|
19576
19731
|
this.emit("prompt_count_changed", this.promptCount);
|
|
19577
19732
|
if (this._status === "initializing" || this._status === "cancelled" || this._status === "error") {
|
|
@@ -19638,6 +19793,7 @@ ${text6}`;
|
|
|
19638
19793
|
this.log.warn({ err }, "TTS post-processing failed");
|
|
19639
19794
|
});
|
|
19640
19795
|
}
|
|
19796
|
+
this.activeTurnContext = null;
|
|
19641
19797
|
if (!this.name) {
|
|
19642
19798
|
await this.autoName();
|
|
19643
19799
|
}
|
|
@@ -19753,6 +19909,42 @@ ${result.text}` : result.text;
|
|
|
19753
19909
|
setAgentCapabilities(caps) {
|
|
19754
19910
|
this.agentCapabilities = caps;
|
|
19755
19911
|
}
|
|
19912
|
+
/**
|
|
19913
|
+
* Hydrate configOptions and agentCapabilities from a spawn response.
|
|
19914
|
+
* Handles both the native configOptions format and legacy modes/models fields.
|
|
19915
|
+
*/
|
|
19916
|
+
applySpawnResponse(resp, caps) {
|
|
19917
|
+
if (caps) this.agentCapabilities = caps;
|
|
19918
|
+
if (!resp) return;
|
|
19919
|
+
if (resp.configOptions) {
|
|
19920
|
+
this.configOptions = resp.configOptions;
|
|
19921
|
+
return;
|
|
19922
|
+
}
|
|
19923
|
+
const legacyOptions = [];
|
|
19924
|
+
if (resp.modes) {
|
|
19925
|
+
const m = resp.modes;
|
|
19926
|
+
legacyOptions.push({
|
|
19927
|
+
id: "mode",
|
|
19928
|
+
name: "Mode",
|
|
19929
|
+
category: "mode",
|
|
19930
|
+
type: "select",
|
|
19931
|
+
currentValue: m.currentModeId,
|
|
19932
|
+
options: m.availableModes.map((x) => ({ value: x.id, name: x.name, description: x.description }))
|
|
19933
|
+
});
|
|
19934
|
+
}
|
|
19935
|
+
if (resp.models) {
|
|
19936
|
+
const m = resp.models;
|
|
19937
|
+
legacyOptions.push({
|
|
19938
|
+
id: "model",
|
|
19939
|
+
name: "Model",
|
|
19940
|
+
category: "model",
|
|
19941
|
+
type: "select",
|
|
19942
|
+
currentValue: m.currentModelId,
|
|
19943
|
+
options: m.availableModels.map((x) => ({ value: x.modelId ?? x.id, name: x.name, description: x.description }))
|
|
19944
|
+
});
|
|
19945
|
+
}
|
|
19946
|
+
if (legacyOptions.length > 0) this.configOptions = legacyOptions;
|
|
19947
|
+
}
|
|
19756
19948
|
getConfigOption(id) {
|
|
19757
19949
|
return this.configOptions.find((o) => o.id === id);
|
|
19758
19950
|
}
|
|
@@ -19772,8 +19964,13 @@ ${result.text}` : result.text;
|
|
|
19772
19964
|
/** Send a config option change to the agent and update local state from the response. */
|
|
19773
19965
|
async setConfigOption(configId, value) {
|
|
19774
19966
|
const response = await this.agentInstance.setConfigOption(configId, value);
|
|
19775
|
-
if (response.configOptions) {
|
|
19967
|
+
if (response.configOptions && response.configOptions.length > 0) {
|
|
19776
19968
|
await this.updateConfigOptions(response.configOptions);
|
|
19969
|
+
} else if (value.type === "select") {
|
|
19970
|
+
const updated = this.configOptions.map(
|
|
19971
|
+
(o) => o.id === configId && o.type === "select" ? { ...o, currentValue: value.value } : o
|
|
19972
|
+
);
|
|
19973
|
+
await this.updateConfigOptions(updated);
|
|
19777
19974
|
}
|
|
19778
19975
|
}
|
|
19779
19976
|
async updateConfigOptions(options) {
|
|
@@ -19837,6 +20034,9 @@ ${result.text}` : result.text;
|
|
|
19837
20034
|
this.promptCount = 0;
|
|
19838
20035
|
this.agentCapabilities = void 0;
|
|
19839
20036
|
this.configOptions = [];
|
|
20037
|
+
this.latestCommands = null;
|
|
20038
|
+
this.applySpawnResponse(newAgent.initialSessionResponse, newAgent.agentCapabilities);
|
|
20039
|
+
this.wireCommandsBuffer();
|
|
19840
20040
|
this.log.info({ from: this.agentSwitchHistory.at(-1).agentName, to: agentName }, "Agent switched");
|
|
19841
20041
|
}
|
|
19842
20042
|
async destroy() {
|
|
@@ -19901,6 +20101,8 @@ var init_session_manager = __esm({
|
|
|
19901
20101
|
}
|
|
19902
20102
|
getSessionByThread(channelId, threadId) {
|
|
19903
20103
|
for (const session of this.sessions.values()) {
|
|
20104
|
+
const adapterThread = session.threadIds.get(channelId);
|
|
20105
|
+
if (adapterThread === threadId) return session;
|
|
19904
20106
|
if (session.channelId === channelId && session.threadId === threadId) {
|
|
19905
20107
|
return session;
|
|
19906
20108
|
}
|
|
@@ -19968,6 +20170,67 @@ var init_session_manager = __esm({
|
|
|
19968
20170
|
if (channelId) return all.filter((s) => s.channelId === channelId);
|
|
19969
20171
|
return all;
|
|
19970
20172
|
}
|
|
20173
|
+
listAllSessions(channelId) {
|
|
20174
|
+
if (this.store) {
|
|
20175
|
+
let records = this.store.list();
|
|
20176
|
+
if (channelId) records = records.filter((r) => r.channelId === channelId);
|
|
20177
|
+
return records.map((record) => {
|
|
20178
|
+
const live2 = this.sessions.get(record.sessionId);
|
|
20179
|
+
if (live2) {
|
|
20180
|
+
return {
|
|
20181
|
+
id: live2.id,
|
|
20182
|
+
agent: live2.agentName,
|
|
20183
|
+
status: live2.status,
|
|
20184
|
+
name: live2.name ?? null,
|
|
20185
|
+
workspace: live2.workingDirectory,
|
|
20186
|
+
channelId: live2.channelId,
|
|
20187
|
+
createdAt: live2.createdAt.toISOString(),
|
|
20188
|
+
lastActiveAt: record.lastActiveAt ?? null,
|
|
20189
|
+
dangerousMode: live2.clientOverrides.bypassPermissions ?? false,
|
|
20190
|
+
queueDepth: live2.queueDepth,
|
|
20191
|
+
promptRunning: live2.promptRunning,
|
|
20192
|
+
configOptions: live2.configOptions?.length ? live2.configOptions : void 0,
|
|
20193
|
+
capabilities: live2.agentCapabilities ?? null,
|
|
20194
|
+
isLive: true
|
|
20195
|
+
};
|
|
20196
|
+
}
|
|
20197
|
+
return {
|
|
20198
|
+
id: record.sessionId,
|
|
20199
|
+
agent: record.agentName,
|
|
20200
|
+
status: record.status,
|
|
20201
|
+
name: record.name ?? null,
|
|
20202
|
+
workspace: record.workingDir,
|
|
20203
|
+
channelId: record.channelId,
|
|
20204
|
+
createdAt: record.createdAt,
|
|
20205
|
+
lastActiveAt: record.lastActiveAt ?? null,
|
|
20206
|
+
dangerousMode: record.clientOverrides?.bypassPermissions ?? false,
|
|
20207
|
+
queueDepth: 0,
|
|
20208
|
+
promptRunning: false,
|
|
20209
|
+
configOptions: record.acpState?.configOptions,
|
|
20210
|
+
capabilities: record.acpState?.agentCapabilities ?? null,
|
|
20211
|
+
isLive: false
|
|
20212
|
+
};
|
|
20213
|
+
});
|
|
20214
|
+
}
|
|
20215
|
+
let live = Array.from(this.sessions.values());
|
|
20216
|
+
if (channelId) live = live.filter((s) => s.channelId === channelId);
|
|
20217
|
+
return live.map((s) => ({
|
|
20218
|
+
id: s.id,
|
|
20219
|
+
agent: s.agentName,
|
|
20220
|
+
status: s.status,
|
|
20221
|
+
name: s.name ?? null,
|
|
20222
|
+
workspace: s.workingDirectory,
|
|
20223
|
+
channelId: s.channelId,
|
|
20224
|
+
createdAt: s.createdAt.toISOString(),
|
|
20225
|
+
lastActiveAt: null,
|
|
20226
|
+
dangerousMode: s.clientOverrides.bypassPermissions ?? false,
|
|
20227
|
+
queueDepth: s.queueDepth,
|
|
20228
|
+
promptRunning: s.promptRunning,
|
|
20229
|
+
configOptions: s.configOptions?.length ? s.configOptions : void 0,
|
|
20230
|
+
capabilities: s.agentCapabilities ?? null,
|
|
20231
|
+
isLive: true
|
|
20232
|
+
}));
|
|
20233
|
+
}
|
|
19971
20234
|
listRecords(filter) {
|
|
19972
20235
|
if (!this.store) return [];
|
|
19973
20236
|
let records = this.store.list();
|
|
@@ -19990,7 +20253,14 @@ var init_session_manager = __esm({
|
|
|
19990
20253
|
for (const session of this.sessions.values()) {
|
|
19991
20254
|
const record = this.store.get(session.id);
|
|
19992
20255
|
if (record) {
|
|
19993
|
-
await this.store.save({
|
|
20256
|
+
await this.store.save({
|
|
20257
|
+
...record,
|
|
20258
|
+
status: "finished",
|
|
20259
|
+
acpState: session.toAcpStateSnapshot(),
|
|
20260
|
+
clientOverrides: session.clientOverrides,
|
|
20261
|
+
currentPromptCount: session.promptCount,
|
|
20262
|
+
agentSwitchHistory: session.agentSwitchHistory
|
|
20263
|
+
});
|
|
19994
20264
|
}
|
|
19995
20265
|
}
|
|
19996
20266
|
this.store.flush();
|
|
@@ -20000,6 +20270,8 @@ var init_session_manager = __esm({
|
|
|
20000
20270
|
/**
|
|
20001
20271
|
* Forcefully destroy all sessions (kill agent subprocesses).
|
|
20002
20272
|
* Use only when sessions must be fully torn down (e.g. archive).
|
|
20273
|
+
* Unlike shutdownAll(), this does NOT snapshot live session state (acpState, etc.)
|
|
20274
|
+
* because destroyed sessions are terminal and will not be resumed.
|
|
20003
20275
|
*/
|
|
20004
20276
|
async destroyAll() {
|
|
20005
20277
|
if (this.store) {
|
|
@@ -20034,15 +20306,18 @@ var init_session_bridge = __esm({
|
|
|
20034
20306
|
"use strict";
|
|
20035
20307
|
init_log();
|
|
20036
20308
|
init_bypass_detection();
|
|
20309
|
+
init_turn_context();
|
|
20037
20310
|
log30 = createChildLogger({ module: "session-bridge" });
|
|
20038
20311
|
SessionBridge = class {
|
|
20039
|
-
constructor(session, adapter, deps) {
|
|
20312
|
+
constructor(session, adapter, deps, adapterId) {
|
|
20040
20313
|
this.session = session;
|
|
20041
20314
|
this.adapter = adapter;
|
|
20042
20315
|
this.deps = deps;
|
|
20316
|
+
this.adapterId = adapterId ?? adapter.name;
|
|
20043
20317
|
}
|
|
20044
20318
|
connected = false;
|
|
20045
20319
|
cleanupFns = [];
|
|
20320
|
+
adapterId;
|
|
20046
20321
|
get tracer() {
|
|
20047
20322
|
return this.session.agentInstance.debugTracer ?? null;
|
|
20048
20323
|
}
|
|
@@ -20073,6 +20348,15 @@ var init_session_bridge = __esm({
|
|
|
20073
20348
|
log30.error({ err, sessionId }, "Error in sendMessage middleware");
|
|
20074
20349
|
}
|
|
20075
20350
|
}
|
|
20351
|
+
/** Determine if this bridge should forward the given event based on turn routing. */
|
|
20352
|
+
shouldForward(event) {
|
|
20353
|
+
if (isSystemEvent(event)) return true;
|
|
20354
|
+
const ctx = this.session.activeTurnContext;
|
|
20355
|
+
if (!ctx) return true;
|
|
20356
|
+
const target = getEffectiveTarget(ctx);
|
|
20357
|
+
if (target === null) return false;
|
|
20358
|
+
return this.adapterId === target;
|
|
20359
|
+
}
|
|
20076
20360
|
connect() {
|
|
20077
20361
|
if (this.connected) return;
|
|
20078
20362
|
this.connected = true;
|
|
@@ -20080,11 +20364,29 @@ var init_session_bridge = __esm({
|
|
|
20080
20364
|
this.session.emit("agent_event", event);
|
|
20081
20365
|
});
|
|
20082
20366
|
this.listen(this.session, "agent_event", (event) => {
|
|
20083
|
-
this.
|
|
20367
|
+
if (this.shouldForward(event)) {
|
|
20368
|
+
this.dispatchAgentEvent(event);
|
|
20369
|
+
} else {
|
|
20370
|
+
this.deps.eventBus?.emit("agent:event", { sessionId: this.session.id, event });
|
|
20371
|
+
}
|
|
20372
|
+
});
|
|
20373
|
+
if (!this.session.agentInstance.onPermissionRequest || this.session.agentInstance.onPermissionRequest.__bridgeId === void 0) {
|
|
20374
|
+
const handler = async (request) => {
|
|
20375
|
+
return this.resolvePermission(request);
|
|
20376
|
+
};
|
|
20377
|
+
handler.__bridgeId = this.adapterId;
|
|
20378
|
+
this.session.agentInstance.onPermissionRequest = handler;
|
|
20379
|
+
}
|
|
20380
|
+
this.listen(this.session, "permission_request", async (request) => {
|
|
20381
|
+
const current = this.session.agentInstance.onPermissionRequest;
|
|
20382
|
+
if (current?.__bridgeId === this.adapterId) return;
|
|
20383
|
+
if (!this.session.permissionGate.isPending) return;
|
|
20384
|
+
try {
|
|
20385
|
+
await this.adapter.sendPermissionRequest(this.session.id, request);
|
|
20386
|
+
} catch (err) {
|
|
20387
|
+
log30.error({ err, sessionId: this.session.id, adapterId: this.adapterId }, "Failed to send permission request to adapter");
|
|
20388
|
+
}
|
|
20084
20389
|
});
|
|
20085
|
-
this.session.agentInstance.onPermissionRequest = async (request) => {
|
|
20086
|
-
return this.resolvePermission(request);
|
|
20087
|
-
};
|
|
20088
20390
|
this.listen(this.session, "status_change", (from, to) => {
|
|
20089
20391
|
this.deps.sessionManager.patchRecord(this.session.id, {
|
|
20090
20392
|
status: to,
|
|
@@ -20116,13 +20418,19 @@ var init_session_bridge = __esm({
|
|
|
20116
20418
|
if (this.session.latestCommands !== null) {
|
|
20117
20419
|
this.session.emit("agent_event", { type: "commands_update", commands: this.session.latestCommands });
|
|
20118
20420
|
}
|
|
20421
|
+
if (this.session.configOptions.length > 0) {
|
|
20422
|
+
this.session.emit("agent_event", { type: "config_option_update", options: this.session.configOptions });
|
|
20423
|
+
}
|
|
20119
20424
|
}
|
|
20120
20425
|
disconnect() {
|
|
20121
20426
|
if (!this.connected) return;
|
|
20122
20427
|
this.connected = false;
|
|
20123
20428
|
this.cleanupFns.forEach((fn) => fn());
|
|
20124
20429
|
this.cleanupFns = [];
|
|
20125
|
-
this.session.agentInstance.onPermissionRequest
|
|
20430
|
+
const current = this.session.agentInstance.onPermissionRequest;
|
|
20431
|
+
if (current?.__bridgeId === this.adapterId) {
|
|
20432
|
+
this.session.agentInstance.onPermissionRequest = async () => "";
|
|
20433
|
+
}
|
|
20126
20434
|
}
|
|
20127
20435
|
/** Dispatch an agent event through middleware and to the adapter */
|
|
20128
20436
|
async dispatchAgentEvent(event) {
|
|
@@ -20253,8 +20561,10 @@ var init_session_bridge = __esm({
|
|
|
20253
20561
|
this.sendMessage(this.session.id, outgoing);
|
|
20254
20562
|
break;
|
|
20255
20563
|
case "config_option_update":
|
|
20256
|
-
this.session.updateConfigOptions(event.options)
|
|
20257
|
-
|
|
20564
|
+
this.session.updateConfigOptions(event.options).then(() => {
|
|
20565
|
+
this.persistAcpState();
|
|
20566
|
+
}).catch(() => {
|
|
20567
|
+
});
|
|
20258
20568
|
outgoing = this.deps.messageTransformer.transform(event);
|
|
20259
20569
|
this.sendMessage(this.session.id, outgoing);
|
|
20260
20570
|
break;
|
|
@@ -20298,19 +20608,27 @@ var init_session_bridge = __esm({
|
|
|
20298
20608
|
return result.autoResolve;
|
|
20299
20609
|
}
|
|
20300
20610
|
}
|
|
20301
|
-
this.session.emit("permission_request", permReq);
|
|
20302
20611
|
this.deps.eventBus?.emit("permission:request", {
|
|
20303
20612
|
sessionId: this.session.id,
|
|
20304
20613
|
permission: permReq
|
|
20305
20614
|
});
|
|
20306
20615
|
const autoDecision = this.checkAutoApprove(permReq);
|
|
20307
20616
|
if (autoDecision) {
|
|
20617
|
+
this.session.emit("permission_request", permReq);
|
|
20308
20618
|
this.emitAfterResolve(mw, permReq.id, autoDecision, "system", startTime);
|
|
20309
20619
|
return autoDecision;
|
|
20310
20620
|
}
|
|
20311
20621
|
const promise = this.session.permissionGate.setPending(permReq);
|
|
20622
|
+
this.session.emit("permission_request", permReq);
|
|
20312
20623
|
await this.adapter.sendPermissionRequest(this.session.id, permReq);
|
|
20313
20624
|
const optionId = await promise;
|
|
20625
|
+
this.deps.eventBus?.emit("permission:resolved", {
|
|
20626
|
+
sessionId: this.session.id,
|
|
20627
|
+
requestId: permReq.id,
|
|
20628
|
+
decision: optionId,
|
|
20629
|
+
optionId,
|
|
20630
|
+
resolvedBy: this.adapterId
|
|
20631
|
+
});
|
|
20314
20632
|
this.emitAfterResolve(mw, permReq.id, optionId, "user", startTime);
|
|
20315
20633
|
return optionId;
|
|
20316
20634
|
}
|
|
@@ -20810,6 +21128,9 @@ var init_session_store = __esm({
|
|
|
20810
21128
|
}
|
|
20811
21129
|
findByPlatform(channelId, predicate) {
|
|
20812
21130
|
for (const record of this.records.values()) {
|
|
21131
|
+
if (record.platforms?.[channelId]) {
|
|
21132
|
+
if (predicate(record.platforms[channelId])) return record;
|
|
21133
|
+
}
|
|
20813
21134
|
if (record.channelId === channelId && predicate(record.platform)) {
|
|
20814
21135
|
return record;
|
|
20815
21136
|
}
|
|
@@ -20876,7 +21197,7 @@ var init_session_store = __esm({
|
|
|
20876
21197
|
return;
|
|
20877
21198
|
}
|
|
20878
21199
|
for (const [id, record] of Object.entries(raw.sessions)) {
|
|
20879
|
-
this.records.set(id, record);
|
|
21200
|
+
this.records.set(id, this.migrateRecord(record));
|
|
20880
21201
|
}
|
|
20881
21202
|
log32.debug({ count: this.records.size }, "Loaded session records");
|
|
20882
21203
|
} catch (err) {
|
|
@@ -20887,6 +21208,19 @@ var init_session_store = __esm({
|
|
|
20887
21208
|
}
|
|
20888
21209
|
}
|
|
20889
21210
|
}
|
|
21211
|
+
/** Migrate old SessionRecord format to new multi-adapter format. */
|
|
21212
|
+
migrateRecord(record) {
|
|
21213
|
+
if (!record.platforms && record.platform && typeof record.platform === "object") {
|
|
21214
|
+
const platformData = record.platform;
|
|
21215
|
+
if (Object.keys(platformData).length > 0) {
|
|
21216
|
+
record.platforms = { [record.channelId]: platformData };
|
|
21217
|
+
}
|
|
21218
|
+
}
|
|
21219
|
+
if (!record.attachedAdapters) {
|
|
21220
|
+
record.attachedAdapters = [record.channelId];
|
|
21221
|
+
}
|
|
21222
|
+
return record;
|
|
21223
|
+
}
|
|
20890
21224
|
cleanup() {
|
|
20891
21225
|
const cutoff = Date.now() - this.ttlDays * 24 * 60 * 60 * 1e3;
|
|
20892
21226
|
let removed = 0;
|
|
@@ -21057,17 +21391,7 @@ var init_session_factory = __esm({
|
|
|
21057
21391
|
if (createParams.initialName) {
|
|
21058
21392
|
session.name = createParams.initialName;
|
|
21059
21393
|
}
|
|
21060
|
-
|
|
21061
|
-
if (resp) {
|
|
21062
|
-
if (resp.configOptions) {
|
|
21063
|
-
session.setInitialConfigOptions(resp.configOptions);
|
|
21064
|
-
}
|
|
21065
|
-
if (agentInstance.agentCapabilities) {
|
|
21066
|
-
session.setAgentCapabilities(agentInstance.agentCapabilities);
|
|
21067
|
-
}
|
|
21068
|
-
} else if (agentInstance.agentCapabilities) {
|
|
21069
|
-
session.setAgentCapabilities(agentInstance.agentCapabilities);
|
|
21070
|
-
}
|
|
21394
|
+
session.applySpawnResponse(agentInstance.initialSessionResponse, agentInstance.agentCapabilities);
|
|
21071
21395
|
this.sessionManager.registerSession(session);
|
|
21072
21396
|
this.eventBus.emit("session:created", {
|
|
21073
21397
|
sessionId: session.id,
|
|
@@ -21085,6 +21409,65 @@ var init_session_factory = __esm({
|
|
|
21085
21409
|
if (session) return session;
|
|
21086
21410
|
return this.lazyResume(channelId, threadId);
|
|
21087
21411
|
}
|
|
21412
|
+
async getOrResumeById(sessionId) {
|
|
21413
|
+
const live = this.sessionManager.getSession(sessionId);
|
|
21414
|
+
if (live) return live;
|
|
21415
|
+
if (!this.sessionStore || !this.createFullSession) return null;
|
|
21416
|
+
const record = this.sessionStore.get(sessionId);
|
|
21417
|
+
if (!record) return null;
|
|
21418
|
+
if (record.status === "error" || record.status === "cancelled") return null;
|
|
21419
|
+
const existing = this.resumeLocks.get(sessionId);
|
|
21420
|
+
if (existing) return existing;
|
|
21421
|
+
const resumePromise = (async () => {
|
|
21422
|
+
try {
|
|
21423
|
+
const p2 = record.platform;
|
|
21424
|
+
const existingThreadId = p2?.topicId ? String(p2.topicId) : p2?.threadId;
|
|
21425
|
+
const session = await this.createFullSession({
|
|
21426
|
+
channelId: record.channelId,
|
|
21427
|
+
agentName: record.agentName,
|
|
21428
|
+
workingDirectory: record.workingDir,
|
|
21429
|
+
resumeAgentSessionId: record.agentSessionId,
|
|
21430
|
+
existingSessionId: record.sessionId,
|
|
21431
|
+
initialName: record.name,
|
|
21432
|
+
threadId: existingThreadId
|
|
21433
|
+
});
|
|
21434
|
+
session.activate();
|
|
21435
|
+
if (record.clientOverrides) {
|
|
21436
|
+
session.clientOverrides = record.clientOverrides;
|
|
21437
|
+
} else if (record.dangerousMode) {
|
|
21438
|
+
session.clientOverrides = { bypassPermissions: true };
|
|
21439
|
+
}
|
|
21440
|
+
if (record.firstAgent) session.firstAgent = record.firstAgent;
|
|
21441
|
+
if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;
|
|
21442
|
+
if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;
|
|
21443
|
+
if (record.attachedAdapters) session.attachedAdapters = record.attachedAdapters;
|
|
21444
|
+
if (record.platforms) {
|
|
21445
|
+
for (const [adapterId, platformData] of Object.entries(record.platforms)) {
|
|
21446
|
+
const data = platformData;
|
|
21447
|
+
const tid = adapterId === "telegram" ? String(data.topicId ?? "") : String(data.threadId ?? "");
|
|
21448
|
+
if (tid) session.threadIds.set(adapterId, tid);
|
|
21449
|
+
}
|
|
21450
|
+
}
|
|
21451
|
+
if (record.acpState) {
|
|
21452
|
+
if (record.acpState.configOptions && session.configOptions.length === 0) {
|
|
21453
|
+
session.setInitialConfigOptions(record.acpState.configOptions);
|
|
21454
|
+
}
|
|
21455
|
+
if (record.acpState.agentCapabilities && !session.agentCapabilities) {
|
|
21456
|
+
session.setAgentCapabilities(record.acpState.agentCapabilities);
|
|
21457
|
+
}
|
|
21458
|
+
}
|
|
21459
|
+
log33.info({ sessionId }, "Lazy resume by ID successful");
|
|
21460
|
+
return session;
|
|
21461
|
+
} catch (err) {
|
|
21462
|
+
log33.error({ err, sessionId }, "Lazy resume by ID failed");
|
|
21463
|
+
return null;
|
|
21464
|
+
} finally {
|
|
21465
|
+
this.resumeLocks.delete(sessionId);
|
|
21466
|
+
}
|
|
21467
|
+
})();
|
|
21468
|
+
this.resumeLocks.set(sessionId, resumePromise);
|
|
21469
|
+
return resumePromise;
|
|
21470
|
+
}
|
|
21088
21471
|
async lazyResume(channelId, threadId) {
|
|
21089
21472
|
const store = this.sessionStore;
|
|
21090
21473
|
if (!store || !this.createFullSession) return null;
|
|
@@ -21093,7 +21476,7 @@ var init_session_factory = __esm({
|
|
|
21093
21476
|
if (existing) return existing;
|
|
21094
21477
|
const record = store.findByPlatform(
|
|
21095
21478
|
channelId,
|
|
21096
|
-
(p2) => String(p2.topicId) === threadId
|
|
21479
|
+
(p2) => String(p2.topicId) === threadId || String(p2.threadId ?? "") === threadId
|
|
21097
21480
|
);
|
|
21098
21481
|
if (!record) {
|
|
21099
21482
|
log33.debug({ threadId, channelId }, "No session record found for thread");
|
|
@@ -21128,11 +21511,21 @@ var init_session_factory = __esm({
|
|
|
21128
21511
|
if (record.firstAgent) session.firstAgent = record.firstAgent;
|
|
21129
21512
|
if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;
|
|
21130
21513
|
if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;
|
|
21514
|
+
if (record.attachedAdapters) {
|
|
21515
|
+
session.attachedAdapters = record.attachedAdapters;
|
|
21516
|
+
}
|
|
21517
|
+
if (record.platforms) {
|
|
21518
|
+
for (const [adapterId, platformData] of Object.entries(record.platforms)) {
|
|
21519
|
+
const data = platformData;
|
|
21520
|
+
const tid = adapterId === "telegram" ? String(data.topicId ?? "") : String(data.threadId ?? "");
|
|
21521
|
+
if (tid) session.threadIds.set(adapterId, tid);
|
|
21522
|
+
}
|
|
21523
|
+
}
|
|
21131
21524
|
if (record.acpState) {
|
|
21132
|
-
if (record.acpState.configOptions) {
|
|
21525
|
+
if (record.acpState.configOptions && session.configOptions.length === 0) {
|
|
21133
21526
|
session.setInitialConfigOptions(record.acpState.configOptions);
|
|
21134
21527
|
}
|
|
21135
|
-
if (record.acpState.agentCapabilities) {
|
|
21528
|
+
if (record.acpState.agentCapabilities && !session.agentCapabilities) {
|
|
21136
21529
|
session.setAgentCapabilities(record.acpState.agentCapabilities);
|
|
21137
21530
|
}
|
|
21138
21531
|
}
|
|
@@ -21315,8 +21708,15 @@ var init_agent_switch_handler = __esm({
|
|
|
21315
21708
|
toAgent,
|
|
21316
21709
|
status: "starting"
|
|
21317
21710
|
});
|
|
21318
|
-
const
|
|
21319
|
-
|
|
21711
|
+
const sessionBridgeKeys = this.deps.getSessionBridgeKeys(sessionId);
|
|
21712
|
+
const hadBridges = sessionBridgeKeys.length > 0;
|
|
21713
|
+
for (const key of sessionBridgeKeys) {
|
|
21714
|
+
const bridge = bridges.get(key);
|
|
21715
|
+
if (bridge) {
|
|
21716
|
+
bridges.delete(key);
|
|
21717
|
+
bridge.disconnect();
|
|
21718
|
+
}
|
|
21719
|
+
}
|
|
21320
21720
|
const switchAdapter = adapters.get(session.channelId);
|
|
21321
21721
|
if (switchAdapter?.sendSkillCommands) {
|
|
21322
21722
|
await switchAdapter.sendSkillCommands(session.id, []);
|
|
@@ -21393,9 +21793,11 @@ var init_agent_switch_handler = __esm({
|
|
|
21393
21793
|
session.agentInstance = oldInstance;
|
|
21394
21794
|
session.agentName = fromAgent;
|
|
21395
21795
|
session.agentSessionId = oldInstance.sessionId;
|
|
21396
|
-
const
|
|
21397
|
-
|
|
21398
|
-
|
|
21796
|
+
for (const adapterId of session.attachedAdapters) {
|
|
21797
|
+
const adapter = adapters.get(adapterId);
|
|
21798
|
+
if (adapter) {
|
|
21799
|
+
createBridge(session, adapter, adapterId).connect();
|
|
21800
|
+
}
|
|
21399
21801
|
}
|
|
21400
21802
|
log34.warn({ sessionId, fromAgent, toAgent, err }, "Agent switch failed, rolled back to previous agent");
|
|
21401
21803
|
} catch (rollbackErr) {
|
|
@@ -21404,10 +21806,14 @@ var init_agent_switch_handler = __esm({
|
|
|
21404
21806
|
}
|
|
21405
21807
|
throw err;
|
|
21406
21808
|
}
|
|
21407
|
-
if (
|
|
21408
|
-
const
|
|
21409
|
-
|
|
21410
|
-
|
|
21809
|
+
if (hadBridges) {
|
|
21810
|
+
for (const adapterId of session.attachedAdapters) {
|
|
21811
|
+
const adapter = adapters.get(adapterId);
|
|
21812
|
+
if (adapter) {
|
|
21813
|
+
createBridge(session, adapter, adapterId).connect();
|
|
21814
|
+
} else {
|
|
21815
|
+
log34.warn({ sessionId, adapterId }, "Adapter not available during switch reconnect, skipping bridge");
|
|
21816
|
+
}
|
|
21411
21817
|
}
|
|
21412
21818
|
}
|
|
21413
21819
|
await sessionManager.patchRecord(sessionId, {
|
|
@@ -22155,7 +22561,7 @@ function createPluginContext(opts) {
|
|
|
22155
22561
|
}
|
|
22156
22562
|
};
|
|
22157
22563
|
const baseLog = opts.log ?? noopLog;
|
|
22158
|
-
const
|
|
22564
|
+
const log45 = typeof baseLog.child === "function" ? baseLog.child({ plugin: pluginName }) : baseLog;
|
|
22159
22565
|
const storageImpl = new PluginStorageImpl(storagePath);
|
|
22160
22566
|
const storage = {
|
|
22161
22567
|
async get(key) {
|
|
@@ -22182,7 +22588,7 @@ function createPluginContext(opts) {
|
|
|
22182
22588
|
const ctx = {
|
|
22183
22589
|
pluginName,
|
|
22184
22590
|
pluginConfig,
|
|
22185
|
-
log:
|
|
22591
|
+
log: log45,
|
|
22186
22592
|
storage,
|
|
22187
22593
|
on(event, handler) {
|
|
22188
22594
|
requirePermission(permissions, "events:read", "on()");
|
|
@@ -22217,7 +22623,7 @@ function createPluginContext(opts) {
|
|
|
22217
22623
|
const registry = serviceRegistry.get("command-registry");
|
|
22218
22624
|
if (registry && typeof registry.register === "function") {
|
|
22219
22625
|
registry.register(def, pluginName);
|
|
22220
|
-
|
|
22626
|
+
log45.debug(`Command '/${def.name}' registered`);
|
|
22221
22627
|
}
|
|
22222
22628
|
},
|
|
22223
22629
|
async sendMessage(_sessionId, _content) {
|
|
@@ -23019,7 +23425,7 @@ var init_core = __esm({
|
|
|
23019
23425
|
sessionManager;
|
|
23020
23426
|
messageTransformer;
|
|
23021
23427
|
adapters = /* @__PURE__ */ new Map();
|
|
23022
|
-
/** sessionId → SessionBridge — tracks active bridges for disconnect/reconnect
|
|
23428
|
+
/** "adapterId:sessionId" → SessionBridge — tracks active bridges for disconnect/reconnect */
|
|
23023
23429
|
bridges = /* @__PURE__ */ new Map();
|
|
23024
23430
|
/** Set by main.ts — triggers graceful shutdown with restart exit code */
|
|
23025
23431
|
requestRestart = null;
|
|
@@ -23115,7 +23521,8 @@ var init_core = __esm({
|
|
|
23115
23521
|
eventBus: this.eventBus,
|
|
23116
23522
|
adapters: this.adapters,
|
|
23117
23523
|
bridges: this.bridges,
|
|
23118
|
-
createBridge: (session, adapter) => this.createBridge(session, adapter),
|
|
23524
|
+
createBridge: (session, adapter, adapterId) => this.createBridge(session, adapter, adapterId),
|
|
23525
|
+
getSessionBridgeKeys: (sessionId) => this.getSessionBridgeKeys(sessionId),
|
|
23119
23526
|
getMiddlewareChain: () => this.lifecycleManager?.middlewareChain,
|
|
23120
23527
|
getService: (name) => this.lifecycleManager.serviceRegistry.get(name)
|
|
23121
23528
|
});
|
|
@@ -23295,7 +23702,7 @@ User message:
|
|
|
23295
23702
|
${text6}`;
|
|
23296
23703
|
}
|
|
23297
23704
|
}
|
|
23298
|
-
await session.enqueuePrompt(text6, message.attachments);
|
|
23705
|
+
await session.enqueuePrompt(text6, message.attachments, message.routing);
|
|
23299
23706
|
}
|
|
23300
23707
|
// --- Unified Session Creation Pipeline ---
|
|
23301
23708
|
async createSession(params) {
|
|
@@ -23322,6 +23729,12 @@ ${text6}`;
|
|
|
23322
23729
|
platform2.threadId = session.threadId;
|
|
23323
23730
|
}
|
|
23324
23731
|
}
|
|
23732
|
+
const platforms = {
|
|
23733
|
+
...existingRecord?.platforms ?? {}
|
|
23734
|
+
};
|
|
23735
|
+
if (session.threadId) {
|
|
23736
|
+
platforms[params.channelId] = params.channelId === "telegram" ? { topicId: Number(session.threadId) || session.threadId } : { threadId: session.threadId };
|
|
23737
|
+
}
|
|
23325
23738
|
await this.sessionManager.patchRecord(session.id, {
|
|
23326
23739
|
sessionId: session.id,
|
|
23327
23740
|
agentSessionId: session.agentSessionId,
|
|
@@ -23333,6 +23746,7 @@ ${text6}`;
|
|
|
23333
23746
|
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23334
23747
|
name: session.name,
|
|
23335
23748
|
platform: platform2,
|
|
23749
|
+
platforms,
|
|
23336
23750
|
firstAgent: session.firstAgent,
|
|
23337
23751
|
currentPromptCount: session.promptCount,
|
|
23338
23752
|
agentSwitchHistory: session.agentSwitchHistory,
|
|
@@ -23340,7 +23754,7 @@ ${text6}`;
|
|
|
23340
23754
|
acpState: session.toAcpStateSnapshot()
|
|
23341
23755
|
}, { immediate: true });
|
|
23342
23756
|
if (adapter) {
|
|
23343
|
-
const bridge = this.createBridge(session, adapter);
|
|
23757
|
+
const bridge = this.createBridge(session, adapter, session.channelId);
|
|
23344
23758
|
bridge.connect();
|
|
23345
23759
|
adapter.flushPendingSkillCommands?.(session.id).catch((err) => {
|
|
23346
23760
|
log40.warn({ err, sessionId: session.id }, "Failed to flush pending skill commands");
|
|
@@ -23479,9 +23893,14 @@ ${text6}`;
|
|
|
23479
23893
|
} else {
|
|
23480
23894
|
adoptPlatform.threadId = session.threadId;
|
|
23481
23895
|
}
|
|
23896
|
+
const adoptPlatforms = {};
|
|
23897
|
+
if (session.threadId) {
|
|
23898
|
+
adoptPlatforms[adapterChannelId] = adapterChannelId === "telegram" ? { topicId: Number(session.threadId) || session.threadId } : { threadId: session.threadId };
|
|
23899
|
+
}
|
|
23482
23900
|
await this.sessionManager.patchRecord(session.id, {
|
|
23483
23901
|
originalAgentSessionId: agentSessionId,
|
|
23484
|
-
platform: adoptPlatform
|
|
23902
|
+
platform: adoptPlatform,
|
|
23903
|
+
platforms: adoptPlatforms
|
|
23485
23904
|
});
|
|
23486
23905
|
return {
|
|
23487
23906
|
ok: true,
|
|
@@ -23503,18 +23922,101 @@ ${text6}`;
|
|
|
23503
23922
|
async getOrResumeSession(channelId, threadId) {
|
|
23504
23923
|
return this.sessionFactory.getOrResume(channelId, threadId);
|
|
23505
23924
|
}
|
|
23925
|
+
async getOrResumeSessionById(sessionId) {
|
|
23926
|
+
return this.sessionFactory.getOrResumeById(sessionId);
|
|
23927
|
+
}
|
|
23928
|
+
async attachAdapter(sessionId, adapterId) {
|
|
23929
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
23930
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
23931
|
+
const adapter = this.adapters.get(adapterId);
|
|
23932
|
+
if (!adapter) throw new Error(`Adapter "${adapterId}" not found or not running`);
|
|
23933
|
+
if (session.attachedAdapters.includes(adapterId)) {
|
|
23934
|
+
const existingThread = session.threadIds.get(adapterId) ?? session.id;
|
|
23935
|
+
return { threadId: existingThread };
|
|
23936
|
+
}
|
|
23937
|
+
const threadId = await adapter.createSessionThread(
|
|
23938
|
+
session.id,
|
|
23939
|
+
session.name ?? `Session ${session.id.slice(0, 6)}`
|
|
23940
|
+
);
|
|
23941
|
+
session.threadIds.set(adapterId, threadId);
|
|
23942
|
+
session.attachedAdapters.push(adapterId);
|
|
23943
|
+
const bridge = this.createBridge(session, adapter, adapterId);
|
|
23944
|
+
bridge.connect();
|
|
23945
|
+
await this.sessionManager.patchRecord(session.id, {
|
|
23946
|
+
attachedAdapters: session.attachedAdapters,
|
|
23947
|
+
platforms: this.buildPlatformsFromSession(session)
|
|
23948
|
+
});
|
|
23949
|
+
return { threadId };
|
|
23950
|
+
}
|
|
23951
|
+
async detachAdapter(sessionId, adapterId) {
|
|
23952
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
23953
|
+
if (!session) throw new Error(`Session ${sessionId} not found`);
|
|
23954
|
+
if (adapterId === session.channelId) {
|
|
23955
|
+
throw new Error("Cannot detach primary adapter (channelId)");
|
|
23956
|
+
}
|
|
23957
|
+
if (!session.attachedAdapters.includes(adapterId)) {
|
|
23958
|
+
return;
|
|
23959
|
+
}
|
|
23960
|
+
const adapter = this.adapters.get(adapterId);
|
|
23961
|
+
if (adapter) {
|
|
23962
|
+
try {
|
|
23963
|
+
await adapter.sendMessage(session.id, {
|
|
23964
|
+
type: "system_message",
|
|
23965
|
+
text: "Session detached from this adapter."
|
|
23966
|
+
});
|
|
23967
|
+
} catch {
|
|
23968
|
+
}
|
|
23969
|
+
}
|
|
23970
|
+
const key = this.bridgeKey(adapterId, session.id);
|
|
23971
|
+
const bridge = this.bridges.get(key);
|
|
23972
|
+
if (bridge) {
|
|
23973
|
+
bridge.disconnect();
|
|
23974
|
+
this.bridges.delete(key);
|
|
23975
|
+
}
|
|
23976
|
+
session.attachedAdapters = session.attachedAdapters.filter((a) => a !== adapterId);
|
|
23977
|
+
session.threadIds.delete(adapterId);
|
|
23978
|
+
await this.sessionManager.patchRecord(session.id, {
|
|
23979
|
+
attachedAdapters: session.attachedAdapters,
|
|
23980
|
+
platforms: this.buildPlatformsFromSession(session)
|
|
23981
|
+
});
|
|
23982
|
+
}
|
|
23983
|
+
buildPlatformsFromSession(session) {
|
|
23984
|
+
const platforms = {};
|
|
23985
|
+
for (const [adapterId, threadId] of session.threadIds) {
|
|
23986
|
+
if (adapterId === "telegram") {
|
|
23987
|
+
platforms.telegram = { topicId: Number(threadId) || threadId };
|
|
23988
|
+
} else {
|
|
23989
|
+
platforms[adapterId] = { threadId };
|
|
23990
|
+
}
|
|
23991
|
+
}
|
|
23992
|
+
return platforms;
|
|
23993
|
+
}
|
|
23506
23994
|
// --- Event Wiring ---
|
|
23995
|
+
/** Composite bridge key: "adapterId:sessionId" */
|
|
23996
|
+
bridgeKey(adapterId, sessionId) {
|
|
23997
|
+
return `${adapterId}:${sessionId}`;
|
|
23998
|
+
}
|
|
23999
|
+
/** Get all bridge keys for a session (regardless of adapter) */
|
|
24000
|
+
getSessionBridgeKeys(sessionId) {
|
|
24001
|
+
const keys = [];
|
|
24002
|
+
for (const key of this.bridges.keys()) {
|
|
24003
|
+
if (key.endsWith(`:${sessionId}`)) keys.push(key);
|
|
24004
|
+
}
|
|
24005
|
+
return keys;
|
|
24006
|
+
}
|
|
23507
24007
|
/** Connect a session bridge for the given session (used by AssistantManager) */
|
|
23508
24008
|
connectSessionBridge(session) {
|
|
23509
24009
|
const adapter = this.adapters.get(session.channelId);
|
|
23510
24010
|
if (!adapter) return;
|
|
23511
|
-
const bridge = this.createBridge(session, adapter);
|
|
24011
|
+
const bridge = this.createBridge(session, adapter, session.channelId);
|
|
23512
24012
|
bridge.connect();
|
|
23513
24013
|
}
|
|
23514
24014
|
/** Create a SessionBridge for the given session and adapter.
|
|
23515
|
-
* Disconnects any existing bridge for the same session first. */
|
|
23516
|
-
createBridge(session, adapter) {
|
|
23517
|
-
const
|
|
24015
|
+
* Disconnects any existing bridge for the same adapter+session first. */
|
|
24016
|
+
createBridge(session, adapter, adapterId) {
|
|
24017
|
+
const id = adapterId ?? adapter.name;
|
|
24018
|
+
const key = this.bridgeKey(id, session.id);
|
|
24019
|
+
const existing = this.bridges.get(key);
|
|
23518
24020
|
if (existing) {
|
|
23519
24021
|
existing.disconnect();
|
|
23520
24022
|
}
|
|
@@ -23525,8 +24027,8 @@ ${text6}`;
|
|
|
23525
24027
|
eventBus: this.eventBus,
|
|
23526
24028
|
fileService: this.fileService,
|
|
23527
24029
|
middlewareChain: this.lifecycleManager?.middlewareChain
|
|
23528
|
-
});
|
|
23529
|
-
this.bridges.set(
|
|
24030
|
+
}, id);
|
|
24031
|
+
this.bridges.set(key, bridge);
|
|
23530
24032
|
return bridge;
|
|
23531
24033
|
}
|
|
23532
24034
|
};
|
|
@@ -24171,13 +24673,15 @@ function registerSwitchCommands(registry, _core) {
|
|
|
24171
24673
|
return { type: "error", message: "No active session in this topic." };
|
|
24172
24674
|
}
|
|
24173
24675
|
if (raw) {
|
|
24676
|
+
const droppedCount = session.queueDepth;
|
|
24174
24677
|
if (session.promptRunning) {
|
|
24175
24678
|
await session.abortPrompt();
|
|
24176
24679
|
}
|
|
24177
24680
|
try {
|
|
24178
24681
|
const { resumed } = await core.switchSessionAgent(session.id, raw);
|
|
24179
24682
|
const status = resumed ? "resumed" : "new session";
|
|
24180
|
-
|
|
24683
|
+
const droppedNote = droppedCount > 0 ? ` (${droppedCount} queued prompt${droppedCount > 1 ? "s" : ""} cleared)` : "";
|
|
24684
|
+
return { type: "text", text: `\u2705 Switched to ${raw} (${status})${droppedNote}` };
|
|
24181
24685
|
} catch (err) {
|
|
24182
24686
|
return { type: "error", message: `Failed to switch agent: ${err.message || err}` };
|
|
24183
24687
|
}
|
|
@@ -24270,27 +24774,13 @@ function registerCategoryCommand(registry, core, category, commandName) {
|
|
|
24270
24774
|
if (configOption.currentValue === raw) {
|
|
24271
24775
|
return { type: "text", text: `Already using **${match.name}**.` };
|
|
24272
24776
|
}
|
|
24273
|
-
if (session.middlewareChain) {
|
|
24274
|
-
const result = await session.middlewareChain.execute("config:beforeChange", {
|
|
24275
|
-
sessionId: session.id,
|
|
24276
|
-
configId: configOption.id,
|
|
24277
|
-
oldValue: configOption.currentValue,
|
|
24278
|
-
newValue: raw
|
|
24279
|
-
}, async (p2) => p2);
|
|
24280
|
-
if (!result) return { type: "error", message: `This change was blocked by a plugin.` };
|
|
24281
|
-
}
|
|
24282
24777
|
try {
|
|
24283
|
-
|
|
24284
|
-
configOption.id,
|
|
24285
|
-
{ type: "select", value: raw }
|
|
24286
|
-
);
|
|
24287
|
-
if (response.configOptions) {
|
|
24288
|
-
session.configOptions = response.configOptions;
|
|
24289
|
-
}
|
|
24778
|
+
await session.setConfigOption(configOption.id, { type: "select", value: raw });
|
|
24290
24779
|
core.eventBus.emit("session:configChanged", { sessionId: session.id });
|
|
24291
24780
|
return { type: "text", text: labels.successMsg(match.name, configOption.name) };
|
|
24292
24781
|
} catch (err) {
|
|
24293
|
-
|
|
24782
|
+
log41.error({ err, commandName, configId: configOption.id }, "setConfigOption failed");
|
|
24783
|
+
const msg = err instanceof Error ? err.message : typeof err === "object" && err !== null && typeof err.message === "string" ? err.message : String(err);
|
|
24294
24784
|
return { type: "error", message: `Could not change ${commandName}: ${msg}` };
|
|
24295
24785
|
}
|
|
24296
24786
|
}
|
|
@@ -24347,20 +24837,15 @@ function registerDangerousCommand(registry, core) {
|
|
|
24347
24837
|
if (bypassValue && modeConfig) {
|
|
24348
24838
|
try {
|
|
24349
24839
|
const targetValue = wantOn ? bypassValue : nonBypassDefault;
|
|
24350
|
-
|
|
24351
|
-
modeConfig.id,
|
|
24352
|
-
{ type: "select", value: targetValue }
|
|
24353
|
-
);
|
|
24354
|
-
if (response.configOptions) {
|
|
24355
|
-
session.configOptions = response.configOptions;
|
|
24356
|
-
}
|
|
24840
|
+
await session.setConfigOption(modeConfig.id, { type: "select", value: targetValue });
|
|
24357
24841
|
core.eventBus.emit("session:configChanged", { sessionId: session.id });
|
|
24358
24842
|
return {
|
|
24359
24843
|
type: "text",
|
|
24360
24844
|
text: wantOn ? "\u2620\uFE0F **Bypass Permissions enabled** \u2014 all permission requests will be auto-approved. The agent can run any action without asking." : "\u{1F510} **Bypass Permissions disabled** \u2014 you will be asked to approve risky actions."
|
|
24361
24845
|
};
|
|
24362
24846
|
} catch (err) {
|
|
24363
|
-
|
|
24847
|
+
log41.error({ err }, "setConfigOption failed (bypass toggle)");
|
|
24848
|
+
const msg = err instanceof Error ? err.message : typeof err === "object" && err !== null && typeof err.message === "string" ? err.message : String(err);
|
|
24364
24849
|
return { type: "error", message: `Could not toggle bypass: ${msg}` };
|
|
24365
24850
|
}
|
|
24366
24851
|
}
|
|
@@ -24383,12 +24868,14 @@ function registerConfigCommands(registry, _core) {
|
|
|
24383
24868
|
registerCategoryCommand(registry, core, "thought_level", "thought");
|
|
24384
24869
|
registerDangerousCommand(registry, core);
|
|
24385
24870
|
}
|
|
24386
|
-
var CATEGORY_LABELS;
|
|
24871
|
+
var log41, CATEGORY_LABELS;
|
|
24387
24872
|
var init_config6 = __esm({
|
|
24388
24873
|
"src/core/commands/config.ts"() {
|
|
24389
24874
|
"use strict";
|
|
24875
|
+
init_log();
|
|
24390
24876
|
init_bypass_detection();
|
|
24391
24877
|
init_bypass_detection();
|
|
24878
|
+
log41 = createChildLogger({ module: "commands/config" });
|
|
24392
24879
|
CATEGORY_LABELS = {
|
|
24393
24880
|
mode: {
|
|
24394
24881
|
menuTitle: (cur) => `Choose session mode (current: ${cur})`,
|
|
@@ -24781,7 +25268,7 @@ function installAutoStart(logDir2) {
|
|
|
24781
25268
|
fs46.mkdirSync(dir, { recursive: true });
|
|
24782
25269
|
fs46.writeFileSync(LAUNCHD_PLIST_PATH, plist);
|
|
24783
25270
|
execFileSync7("launchctl", ["load", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
|
|
24784
|
-
|
|
25271
|
+
log42.info("LaunchAgent installed");
|
|
24785
25272
|
return { success: true };
|
|
24786
25273
|
}
|
|
24787
25274
|
if (process.platform === "linux") {
|
|
@@ -24791,13 +25278,13 @@ function installAutoStart(logDir2) {
|
|
|
24791
25278
|
fs46.writeFileSync(SYSTEMD_SERVICE_PATH, unit);
|
|
24792
25279
|
execFileSync7("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
24793
25280
|
execFileSync7("systemctl", ["--user", "enable", "openacp"], { stdio: "pipe" });
|
|
24794
|
-
|
|
25281
|
+
log42.info("systemd user service installed");
|
|
24795
25282
|
return { success: true };
|
|
24796
25283
|
}
|
|
24797
25284
|
return { success: false, error: "Unsupported platform" };
|
|
24798
25285
|
} catch (e) {
|
|
24799
25286
|
const msg = e.message;
|
|
24800
|
-
|
|
25287
|
+
log42.error({ err: msg }, "Failed to install auto-start");
|
|
24801
25288
|
return { success: false, error: msg };
|
|
24802
25289
|
}
|
|
24803
25290
|
}
|
|
@@ -24813,7 +25300,7 @@ function uninstallAutoStart() {
|
|
|
24813
25300
|
} catch {
|
|
24814
25301
|
}
|
|
24815
25302
|
fs46.unlinkSync(LAUNCHD_PLIST_PATH);
|
|
24816
|
-
|
|
25303
|
+
log42.info("LaunchAgent removed");
|
|
24817
25304
|
}
|
|
24818
25305
|
return { success: true };
|
|
24819
25306
|
}
|
|
@@ -24825,14 +25312,14 @@ function uninstallAutoStart() {
|
|
|
24825
25312
|
}
|
|
24826
25313
|
fs46.unlinkSync(SYSTEMD_SERVICE_PATH);
|
|
24827
25314
|
execFileSync7("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
24828
|
-
|
|
25315
|
+
log42.info("systemd user service removed");
|
|
24829
25316
|
}
|
|
24830
25317
|
return { success: true };
|
|
24831
25318
|
}
|
|
24832
25319
|
return { success: false, error: "Unsupported platform" };
|
|
24833
25320
|
} catch (e) {
|
|
24834
25321
|
const msg = e.message;
|
|
24835
|
-
|
|
25322
|
+
log42.error({ err: msg }, "Failed to uninstall auto-start");
|
|
24836
25323
|
return { success: false, error: msg };
|
|
24837
25324
|
}
|
|
24838
25325
|
}
|
|
@@ -24845,12 +25332,12 @@ function isAutoStartInstalled() {
|
|
|
24845
25332
|
}
|
|
24846
25333
|
return false;
|
|
24847
25334
|
}
|
|
24848
|
-
var
|
|
25335
|
+
var log42, LAUNCHD_LABEL, LAUNCHD_PLIST_PATH, SYSTEMD_SERVICE_PATH;
|
|
24849
25336
|
var init_autostart = __esm({
|
|
24850
25337
|
"src/cli/autostart.ts"() {
|
|
24851
25338
|
"use strict";
|
|
24852
25339
|
init_log();
|
|
24853
|
-
|
|
25340
|
+
log42 = createChildLogger({ module: "autostart" });
|
|
24854
25341
|
LAUNCHD_LABEL = "com.openacp.daemon";
|
|
24855
25342
|
LAUNCHD_PLIST_PATH = path53.join(os24.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
24856
25343
|
SYSTEMD_SERVICE_PATH = path53.join(os24.homedir(), ".config", "systemd", "user", "openacp.service");
|
|
@@ -24953,7 +25440,7 @@ async function setupIntegrations() {
|
|
|
24953
25440
|
if (integration) {
|
|
24954
25441
|
for (const item of integration.items) {
|
|
24955
25442
|
const result = await item.install();
|
|
24956
|
-
for (const
|
|
25443
|
+
for (const log45 of result.logs) console.log(` ${log45}`);
|
|
24957
25444
|
}
|
|
24958
25445
|
}
|
|
24959
25446
|
console.log("Claude CLI integration installed.\n");
|
|
@@ -25911,14 +26398,14 @@ async function runPostUpgradeChecks(config) {
|
|
|
25911
26398
|
const { ensureCloudflared: ensureCloudflared2 } = await Promise.resolve().then(() => (init_install_cloudflared(), install_cloudflared_exports));
|
|
25912
26399
|
await ensureCloudflared2();
|
|
25913
26400
|
} catch (err) {
|
|
25914
|
-
|
|
26401
|
+
log44.warn(
|
|
25915
26402
|
{ err: err.message },
|
|
25916
26403
|
"Could not install cloudflared. Tunnel may not work."
|
|
25917
26404
|
);
|
|
25918
26405
|
}
|
|
25919
26406
|
} else {
|
|
25920
26407
|
if (!commandExists(config.tunnel.provider)) {
|
|
25921
|
-
|
|
26408
|
+
log44.warn(
|
|
25922
26409
|
`Tunnel provider "${config.tunnel.provider}" is not installed. Install it or switch to cloudflare (free, auto-installed).`
|
|
25923
26410
|
);
|
|
25924
26411
|
}
|
|
@@ -25930,7 +26417,7 @@ async function runPostUpgradeChecks(config) {
|
|
|
25930
26417
|
if (integration) {
|
|
25931
26418
|
const allInstalled = integration.items.every((item) => item.isInstalled());
|
|
25932
26419
|
if (!allInstalled) {
|
|
25933
|
-
|
|
26420
|
+
log44.info(
|
|
25934
26421
|
'Claude CLI integration not installed. Run "openacp integrate claude" for session transfer + tunnel skill.'
|
|
25935
26422
|
);
|
|
25936
26423
|
}
|
|
@@ -25940,7 +26427,7 @@ async function runPostUpgradeChecks(config) {
|
|
|
25940
26427
|
const { ensureJq: ensureJq2 } = await Promise.resolve().then(() => (init_install_jq(), install_jq_exports));
|
|
25941
26428
|
await ensureJq2();
|
|
25942
26429
|
} catch (err) {
|
|
25943
|
-
|
|
26430
|
+
log44.warn(
|
|
25944
26431
|
{ err: err.message },
|
|
25945
26432
|
"Could not install jq. Handoff hooks may not work."
|
|
25946
26433
|
);
|
|
@@ -25950,7 +26437,7 @@ async function runPostUpgradeChecks(config) {
|
|
|
25950
26437
|
} catch {
|
|
25951
26438
|
}
|
|
25952
26439
|
if (!commandExists("unzip")) {
|
|
25953
|
-
|
|
26440
|
+
log44.warn(
|
|
25954
26441
|
"unzip is not installed. Some agent installations (binary distribution) may fail. Install: brew install unzip (macOS) or apt install unzip (Linux)"
|
|
25955
26442
|
);
|
|
25956
26443
|
}
|
|
@@ -25963,20 +26450,20 @@ async function runPostUpgradeChecks(config) {
|
|
|
25963
26450
|
(a) => a.distribution === "uvx"
|
|
25964
26451
|
);
|
|
25965
26452
|
if (hasUvxAgent && !commandExists("uvx")) {
|
|
25966
|
-
|
|
26453
|
+
log44.warn(
|
|
25967
26454
|
"uvx is not installed but you have Python-based agents. Install: pip install uv"
|
|
25968
26455
|
);
|
|
25969
26456
|
}
|
|
25970
26457
|
} catch {
|
|
25971
26458
|
}
|
|
25972
26459
|
}
|
|
25973
|
-
var
|
|
26460
|
+
var log44;
|
|
25974
26461
|
var init_post_upgrade = __esm({
|
|
25975
26462
|
"src/cli/post-upgrade.ts"() {
|
|
25976
26463
|
"use strict";
|
|
25977
26464
|
init_log();
|
|
25978
26465
|
init_agent_dependencies();
|
|
25979
|
-
|
|
26466
|
+
log44 = createChildLogger({ module: "post-upgrade" });
|
|
25980
26467
|
}
|
|
25981
26468
|
});
|
|
25982
26469
|
|
|
@@ -26340,7 +26827,10 @@ async function startServer(opts) {
|
|
|
26340
26827
|
const tunnelErr = tunnelSvc.getStartError();
|
|
26341
26828
|
const url = tunnelSvc.getPublicUrl();
|
|
26342
26829
|
const isPublic = url && !url.startsWith("http://localhost") && !url.startsWith("http://127.0.0.1");
|
|
26343
|
-
if (tunnelErr) {
|
|
26830
|
+
if (tunnelErr && isPublic) {
|
|
26831
|
+
warn3(`Primary tunnel failed \u2014 using fallback: ${tunnelErr}`);
|
|
26832
|
+
tunnelUrl = url;
|
|
26833
|
+
} else if (tunnelErr) {
|
|
26344
26834
|
warn3(`Tunnel failed (${tunnelErr}) \u2014 retrying in background`);
|
|
26345
26835
|
} else if (isPublic) {
|
|
26346
26836
|
ok3("Tunnel ready");
|
|
@@ -29711,7 +30201,7 @@ a "Handoff" slash command to Claude Code.
|
|
|
29711
30201
|
if (uninstall) {
|
|
29712
30202
|
console.log(`Removing ${agent}/${item.id}...`);
|
|
29713
30203
|
const result = await item.uninstall();
|
|
29714
|
-
for (const
|
|
30204
|
+
for (const log45 of result.logs) console.log(` ${log45}`);
|
|
29715
30205
|
if (result.success) {
|
|
29716
30206
|
console.log(` ${item.name} removed.`);
|
|
29717
30207
|
} else {
|
|
@@ -29721,7 +30211,7 @@ a "Handoff" slash command to Claude Code.
|
|
|
29721
30211
|
} else {
|
|
29722
30212
|
console.log(`Installing ${agent}/${item.id}...`);
|
|
29723
30213
|
const result = await item.install();
|
|
29724
|
-
for (const
|
|
30214
|
+
for (const log45 of result.logs) console.log(` ${log45}`);
|
|
29725
30215
|
if (result.success) {
|
|
29726
30216
|
console.log(` ${item.name} installed.`);
|
|
29727
30217
|
} else {
|