@openacp/cli 2026.331.2 → 2026.331.4
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/cli.js +333 -138
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +23 -5
- package/dist/index.js +195 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3929,7 +3929,7 @@ function initLogger(config) {
|
|
|
3929
3929
|
target: "pino-pretty",
|
|
3930
3930
|
options: {
|
|
3931
3931
|
colorize: true,
|
|
3932
|
-
translateTime: "HH:
|
|
3932
|
+
translateTime: "SYS:HH:MM:ss",
|
|
3933
3933
|
ignore: "pid,hostname",
|
|
3934
3934
|
singleLine: true
|
|
3935
3935
|
},
|
|
@@ -4964,7 +4964,7 @@ var init_tunnel_registry = __esm({
|
|
|
4964
4964
|
this.providerOptions = opts.providerOptions ?? {};
|
|
4965
4965
|
this.registryPath = opts.registryPath ?? path16.join(os9.homedir(), ".openacp", "tunnels.json");
|
|
4966
4966
|
}
|
|
4967
|
-
async add(port, opts) {
|
|
4967
|
+
async add(port, opts, _autoRetry = true) {
|
|
4968
4968
|
if (this.entries.has(port)) {
|
|
4969
4969
|
const existing = this.entries.get(port);
|
|
4970
4970
|
if (existing.entry.status === "active" || existing.entry.status === "starting") {
|
|
@@ -5018,6 +5018,15 @@ var init_tunnel_registry = __esm({
|
|
|
5018
5018
|
entry.status = "failed";
|
|
5019
5019
|
log7.error({ port, err: err.message }, "Tunnel failed to start");
|
|
5020
5020
|
this.scheduleSave();
|
|
5021
|
+
const live = this.entries.get(port);
|
|
5022
|
+
if (_autoRetry && live && !this.shuttingDown && live.entry.retryCount < MAX_RETRIES) {
|
|
5023
|
+
const delay = BASE_RETRY_DELAY_MS * Math.pow(2, live.entry.retryCount);
|
|
5024
|
+
log7.warn(
|
|
5025
|
+
{ port, retry: live.entry.retryCount + 1, maxRetries: MAX_RETRIES, delayMs: delay },
|
|
5026
|
+
"Scheduling retry after initial start failure"
|
|
5027
|
+
);
|
|
5028
|
+
live.retryTimer = setTimeout(() => this.retry(port, opts), delay);
|
|
5029
|
+
}
|
|
5021
5030
|
throw err;
|
|
5022
5031
|
});
|
|
5023
5032
|
this.entries.set(port, { entry, process: provider, spawnPromise, retryTimer: null });
|
|
@@ -5034,7 +5043,7 @@ var init_tunnel_registry = __esm({
|
|
|
5034
5043
|
if (live.retryTimer) clearTimeout(live.retryTimer);
|
|
5035
5044
|
this.entries.delete(port);
|
|
5036
5045
|
try {
|
|
5037
|
-
const entry = await this.add(port, opts);
|
|
5046
|
+
const entry = await this.add(port, opts, false);
|
|
5038
5047
|
entry.retryCount = retryCount;
|
|
5039
5048
|
} catch (err) {
|
|
5040
5049
|
log7.error({ port, err: err.message, retry: retryCount }, "Tunnel retry failed");
|
|
@@ -9943,7 +9952,7 @@ async function handleArchive(ctx, core) {
|
|
|
9943
9952
|
return;
|
|
9944
9953
|
}
|
|
9945
9954
|
await ctx.reply(
|
|
9946
|
-
"\u26A0\uFE0F <b>Archive this session?</b>\n\nThis will:\n\u2022
|
|
9955
|
+
"\u26A0\uFE0F <b>Archive this session?</b>\n\nThis will:\n\u2022 Stop the agent session\n\u2022 Delete this topic permanently\n\n<i>This cannot be undone.</i>",
|
|
9947
9956
|
{
|
|
9948
9957
|
parse_mode: "HTML",
|
|
9949
9958
|
reply_markup: new InlineKeyboard3().text("\u{1F5D1} Yes, archive", `ar:yes:${identifier}`).text("\u274C Cancel", `ar:no:${identifier}`)
|
|
@@ -9964,18 +9973,7 @@ async function handleArchiveConfirm(ctx, core, chatId) {
|
|
|
9964
9973
|
return;
|
|
9965
9974
|
}
|
|
9966
9975
|
const result = await core.archiveSession(identifier);
|
|
9967
|
-
if (result.ok) {
|
|
9968
|
-
const adapter = core.adapters.get("telegram");
|
|
9969
|
-
if (adapter) {
|
|
9970
|
-
try {
|
|
9971
|
-
await adapter.sendMessage(identifier, {
|
|
9972
|
-
type: "text",
|
|
9973
|
-
text: "Chat history cleared. Session is still active \u2014 send a message to continue."
|
|
9974
|
-
});
|
|
9975
|
-
} catch {
|
|
9976
|
-
}
|
|
9977
|
-
}
|
|
9978
|
-
} else {
|
|
9976
|
+
if (!result.ok) {
|
|
9979
9977
|
try {
|
|
9980
9978
|
await ctx.editMessageText(`Failed to archive: <code>${escapeHtml4(result.error)}</code>`, { parse_mode: "HTML" });
|
|
9981
9979
|
} catch {
|
|
@@ -15237,27 +15235,28 @@ var init_adapter = __esm({
|
|
|
15237
15235
|
}
|
|
15238
15236
|
return prev(method, payload, signal);
|
|
15239
15237
|
});
|
|
15240
|
-
|
|
15241
|
-
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
15242
|
-
});
|
|
15238
|
+
this.registerCommandsWithRetry();
|
|
15243
15239
|
this.bot.use((ctx, next) => {
|
|
15244
15240
|
const chatId = ctx.chat?.id ?? ctx.callbackQuery?.message?.chat?.id;
|
|
15245
15241
|
if (chatId !== this.telegramConfig.chatId) return;
|
|
15246
15242
|
return next();
|
|
15247
15243
|
});
|
|
15248
|
-
const topics = await
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15244
|
+
const topics = await this.retryWithBackoff(
|
|
15245
|
+
() => ensureTopics(
|
|
15246
|
+
this.bot,
|
|
15247
|
+
this.telegramConfig.chatId,
|
|
15248
|
+
this.telegramConfig,
|
|
15249
|
+
async (updates) => {
|
|
15250
|
+
if (this.saveTopicIds) {
|
|
15251
|
+
await this.saveTopicIds(updates);
|
|
15252
|
+
} else {
|
|
15253
|
+
await this.core.configManager.save({
|
|
15254
|
+
channels: { telegram: updates }
|
|
15255
|
+
});
|
|
15256
|
+
}
|
|
15259
15257
|
}
|
|
15260
|
-
|
|
15258
|
+
),
|
|
15259
|
+
"ensureTopics"
|
|
15261
15260
|
);
|
|
15262
15261
|
this.notificationTopicId = topics.notificationTopicId;
|
|
15263
15262
|
this.assistantTopicId = topics.assistantTopicId;
|
|
@@ -15505,6 +15504,40 @@ var init_adapter = __esm({
|
|
|
15505
15504
|
});
|
|
15506
15505
|
}
|
|
15507
15506
|
}
|
|
15507
|
+
/**
|
|
15508
|
+
* Retry an async operation with exponential backoff.
|
|
15509
|
+
* Used for Telegram API calls that may fail due to transient network issues.
|
|
15510
|
+
*/
|
|
15511
|
+
async retryWithBackoff(fn, label, maxRetries = 5, baseDelayMs = 2e3) {
|
|
15512
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
15513
|
+
try {
|
|
15514
|
+
return await fn();
|
|
15515
|
+
} catch (err) {
|
|
15516
|
+
if (attempt === maxRetries) throw err;
|
|
15517
|
+
const delay = baseDelayMs * Math.pow(2, attempt - 1);
|
|
15518
|
+
log26.warn(
|
|
15519
|
+
{ err, attempt, maxRetries, delayMs: delay, operation: label },
|
|
15520
|
+
`${label} failed, retrying in ${delay}ms`
|
|
15521
|
+
);
|
|
15522
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
15523
|
+
}
|
|
15524
|
+
}
|
|
15525
|
+
throw new Error("unreachable");
|
|
15526
|
+
}
|
|
15527
|
+
/**
|
|
15528
|
+
* Register Telegram commands in the background with retries.
|
|
15529
|
+
* Non-critical — bot works fine without autocomplete commands.
|
|
15530
|
+
*/
|
|
15531
|
+
registerCommandsWithRetry() {
|
|
15532
|
+
this.retryWithBackoff(
|
|
15533
|
+
() => this.bot.api.setMyCommands(STATIC_COMMANDS, {
|
|
15534
|
+
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
15535
|
+
}),
|
|
15536
|
+
"setMyCommands"
|
|
15537
|
+
).catch((err) => {
|
|
15538
|
+
log26.warn({ err }, "Failed to register Telegram commands after retries (non-critical)");
|
|
15539
|
+
});
|
|
15540
|
+
}
|
|
15508
15541
|
async stop() {
|
|
15509
15542
|
for (const tracker of this.sessionTrackers.values()) {
|
|
15510
15543
|
tracker.destroy();
|
|
@@ -16221,10 +16254,6 @@ Task completed.
|
|
|
16221
16254
|
this.sessionTrackers.delete(session.id);
|
|
16222
16255
|
}
|
|
16223
16256
|
await deleteSessionTopic(this.bot, chatId, oldTopicId);
|
|
16224
|
-
const topicName = session.name ?? `Session ${session.id.slice(0, 6)}`;
|
|
16225
|
-
const newTopicId = await createSessionTopic(this.bot, chatId, topicName);
|
|
16226
|
-
session.archiving = false;
|
|
16227
|
-
return String(newTopicId);
|
|
16228
16257
|
}
|
|
16229
16258
|
};
|
|
16230
16259
|
}
|
|
@@ -18813,7 +18842,7 @@ async function setupIntegrations(config) {
|
|
|
18813
18842
|
if (integration) {
|
|
18814
18843
|
for (const item of integration.items) {
|
|
18815
18844
|
const result = await item.install();
|
|
18816
|
-
for (const
|
|
18845
|
+
for (const log40 of result.logs) console.log(` ${log40}`);
|
|
18817
18846
|
}
|
|
18818
18847
|
}
|
|
18819
18848
|
console.log("Claude CLI integration installed.\n");
|
|
@@ -19176,6 +19205,10 @@ async function runSetup(configManager, opts) {
|
|
|
19176
19205
|
const builtInOptions = [
|
|
19177
19206
|
{ label: "Telegram", value: "telegram" }
|
|
19178
19207
|
];
|
|
19208
|
+
const officialAdapters = [
|
|
19209
|
+
{ label: "Discord", value: "official:@openacp/discord-adapter" },
|
|
19210
|
+
{ label: "Slack", value: "official:@openacp/slack-adapter" }
|
|
19211
|
+
];
|
|
19179
19212
|
const communityOptions = communityAdapters.map((a) => ({
|
|
19180
19213
|
label: `${a.icon} ${a.displayName}${a.verified ? " (verified)" : ""}`,
|
|
19181
19214
|
value: `community:${a.name}`
|
|
@@ -19185,6 +19218,7 @@ async function runSetup(configManager, opts) {
|
|
|
19185
19218
|
message: "Which channels do you want to set up?",
|
|
19186
19219
|
options: [
|
|
19187
19220
|
...builtInOptions.map((o) => ({ value: o.value, label: o.label, hint: "built-in" })),
|
|
19221
|
+
...officialAdapters.map((o) => ({ value: o.value, label: o.label, hint: "official" })),
|
|
19188
19222
|
...communityOptions.length > 0 ? communityOptions.map((o) => ({ value: o.value, label: o.label, hint: "from plugin registry" })) : []
|
|
19189
19223
|
],
|
|
19190
19224
|
required: true,
|
|
@@ -19214,6 +19248,54 @@ async function runSetup(configManager, opts) {
|
|
|
19214
19248
|
description: telegramPlugin.description
|
|
19215
19249
|
});
|
|
19216
19250
|
}
|
|
19251
|
+
if (channelId.startsWith("official:")) {
|
|
19252
|
+
const npmPackage = channelId.slice("official:".length);
|
|
19253
|
+
const { execFileSync: execFileSync8 } = await import("child_process");
|
|
19254
|
+
const pluginsDir = path47.join(getGlobalRoot(), "plugins");
|
|
19255
|
+
const nodeModulesDir = path47.join(pluginsDir, "node_modules");
|
|
19256
|
+
const installedPath = path47.join(nodeModulesDir, npmPackage);
|
|
19257
|
+
if (!fs41.existsSync(installedPath)) {
|
|
19258
|
+
try {
|
|
19259
|
+
clack9.log.step(`Installing ${npmPackage}...`);
|
|
19260
|
+
execFileSync8("npm", ["install", npmPackage, "--prefix", pluginsDir, "--save"], {
|
|
19261
|
+
stdio: "inherit",
|
|
19262
|
+
timeout: 6e4
|
|
19263
|
+
});
|
|
19264
|
+
} catch {
|
|
19265
|
+
console.log(fail(`Failed to install ${npmPackage}.`));
|
|
19266
|
+
continue;
|
|
19267
|
+
}
|
|
19268
|
+
}
|
|
19269
|
+
try {
|
|
19270
|
+
const installedPkgPath = path47.join(nodeModulesDir, npmPackage, "package.json");
|
|
19271
|
+
const installedPkg = JSON.parse(fs41.readFileSync(installedPkgPath, "utf-8"));
|
|
19272
|
+
const pluginModule = await import(path47.join(nodeModulesDir, npmPackage, installedPkg.main ?? "dist/index.js"));
|
|
19273
|
+
const plugin = pluginModule.default;
|
|
19274
|
+
if (plugin?.install) {
|
|
19275
|
+
const installCtx = createInstallContext2({
|
|
19276
|
+
pluginName: plugin.name ?? npmPackage,
|
|
19277
|
+
settingsManager,
|
|
19278
|
+
basePath: settingsManager.getBasePath()
|
|
19279
|
+
});
|
|
19280
|
+
await plugin.install(installCtx);
|
|
19281
|
+
}
|
|
19282
|
+
pluginRegistry.register(plugin?.name ?? npmPackage, {
|
|
19283
|
+
version: installedPkg.version,
|
|
19284
|
+
source: "npm",
|
|
19285
|
+
enabled: true,
|
|
19286
|
+
settingsPath: settingsManager.getSettingsPath(plugin?.name ?? npmPackage),
|
|
19287
|
+
description: plugin?.description ?? installedPkg.description
|
|
19288
|
+
});
|
|
19289
|
+
} catch (err) {
|
|
19290
|
+
console.log(fail(`Failed to load ${npmPackage}: ${err.message}`));
|
|
19291
|
+
pluginRegistry.register(npmPackage, {
|
|
19292
|
+
version: "unknown",
|
|
19293
|
+
source: "npm",
|
|
19294
|
+
enabled: false,
|
|
19295
|
+
settingsPath: settingsManager.getSettingsPath(npmPackage)
|
|
19296
|
+
});
|
|
19297
|
+
}
|
|
19298
|
+
}
|
|
19217
19299
|
if (channelId.startsWith("community:")) {
|
|
19218
19300
|
const npmPackage = channelId.slice("community:".length);
|
|
19219
19301
|
const { execFileSync: execFileSync8 } = await import("child_process");
|
|
@@ -19906,7 +19988,7 @@ function resolveAgentCommand(cmd) {
|
|
|
19906
19988
|
}
|
|
19907
19989
|
return { command: cmd, args: [] };
|
|
19908
19990
|
}
|
|
19909
|
-
var
|
|
19991
|
+
var log33, AgentInstance;
|
|
19910
19992
|
var init_agent_instance = __esm({
|
|
19911
19993
|
"src/core/agents/agent-instance.ts"() {
|
|
19912
19994
|
"use strict";
|
|
@@ -19918,7 +20000,7 @@ var init_agent_instance = __esm({
|
|
|
19918
20000
|
init_mcp_manager();
|
|
19919
20001
|
init_debug_tracer();
|
|
19920
20002
|
init_log();
|
|
19921
|
-
|
|
20003
|
+
log33 = createChildLogger({ module: "agent-instance" });
|
|
19922
20004
|
AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
19923
20005
|
connection;
|
|
19924
20006
|
child;
|
|
@@ -19940,7 +20022,7 @@ var init_agent_instance = __esm({
|
|
|
19940
20022
|
static async spawnSubprocess(agentDef, workingDirectory) {
|
|
19941
20023
|
const instance = new _AgentInstance(agentDef.name);
|
|
19942
20024
|
const resolved = resolveAgentCommand(agentDef.command);
|
|
19943
|
-
|
|
20025
|
+
log33.debug(
|
|
19944
20026
|
{
|
|
19945
20027
|
agentName: agentDef.name,
|
|
19946
20028
|
command: resolved.command,
|
|
@@ -20014,13 +20096,13 @@ var init_agent_instance = __esm({
|
|
|
20014
20096
|
}
|
|
20015
20097
|
});
|
|
20016
20098
|
if (initResponse.protocolVersion !== PROTOCOL_VERSION) {
|
|
20017
|
-
|
|
20099
|
+
log33.warn(
|
|
20018
20100
|
{ expected: PROTOCOL_VERSION, got: initResponse.protocolVersion },
|
|
20019
20101
|
"ACP protocol version mismatch \u2014 some features may not work correctly"
|
|
20020
20102
|
);
|
|
20021
20103
|
}
|
|
20022
20104
|
instance.promptCapabilities = initResponse.agentCapabilities?.promptCapabilities;
|
|
20023
|
-
|
|
20105
|
+
log33.info(
|
|
20024
20106
|
{ promptCapabilities: instance.promptCapabilities ?? {} },
|
|
20025
20107
|
"Agent prompt capabilities"
|
|
20026
20108
|
);
|
|
@@ -20029,7 +20111,7 @@ var init_agent_instance = __esm({
|
|
|
20029
20111
|
setupCrashDetection() {
|
|
20030
20112
|
this.child.on("exit", (code, signal) => {
|
|
20031
20113
|
if (this._destroying) return;
|
|
20032
|
-
|
|
20114
|
+
log33.info(
|
|
20033
20115
|
{ sessionId: this.sessionId, exitCode: code, signal },
|
|
20034
20116
|
"Agent process exited"
|
|
20035
20117
|
);
|
|
@@ -20044,11 +20126,11 @@ ${stderr}`
|
|
|
20044
20126
|
}
|
|
20045
20127
|
});
|
|
20046
20128
|
this.connection.closed.then(() => {
|
|
20047
|
-
|
|
20129
|
+
log33.debug({ sessionId: this.sessionId }, "ACP connection closed");
|
|
20048
20130
|
});
|
|
20049
20131
|
}
|
|
20050
20132
|
static async spawn(agentDef, workingDirectory, mcpServers) {
|
|
20051
|
-
|
|
20133
|
+
log33.debug(
|
|
20052
20134
|
{ agentName: agentDef.name, command: agentDef.command },
|
|
20053
20135
|
"Spawning agent"
|
|
20054
20136
|
);
|
|
@@ -20065,14 +20147,14 @@ ${stderr}`
|
|
|
20065
20147
|
instance.sessionId = response.sessionId;
|
|
20066
20148
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
20067
20149
|
instance.setupCrashDetection();
|
|
20068
|
-
|
|
20150
|
+
log33.info(
|
|
20069
20151
|
{ sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
|
|
20070
20152
|
"Agent spawn complete"
|
|
20071
20153
|
);
|
|
20072
20154
|
return instance;
|
|
20073
20155
|
}
|
|
20074
20156
|
static async resume(agentDef, workingDirectory, agentSessionId, mcpServers) {
|
|
20075
|
-
|
|
20157
|
+
log33.debug({ agentName: agentDef.name, agentSessionId }, "Resuming agent");
|
|
20076
20158
|
const spawnStart = Date.now();
|
|
20077
20159
|
const instance = await _AgentInstance.spawnSubprocess(
|
|
20078
20160
|
agentDef,
|
|
@@ -20085,12 +20167,12 @@ ${stderr}`
|
|
|
20085
20167
|
});
|
|
20086
20168
|
instance.sessionId = response.sessionId;
|
|
20087
20169
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
20088
|
-
|
|
20170
|
+
log33.info(
|
|
20089
20171
|
{ sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
|
|
20090
20172
|
"Agent resume complete"
|
|
20091
20173
|
);
|
|
20092
20174
|
} catch (err) {
|
|
20093
|
-
|
|
20175
|
+
log33.warn(
|
|
20094
20176
|
{ err, agentSessionId },
|
|
20095
20177
|
"Resume failed, falling back to new session"
|
|
20096
20178
|
);
|
|
@@ -20101,7 +20183,7 @@ ${stderr}`
|
|
|
20101
20183
|
});
|
|
20102
20184
|
instance.sessionId = response.sessionId;
|
|
20103
20185
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
20104
|
-
|
|
20186
|
+
log33.info(
|
|
20105
20187
|
{ sessionId: response.sessionId, durationMs: Date.now() - spawnStart },
|
|
20106
20188
|
"Agent fallback spawn complete"
|
|
20107
20189
|
);
|
|
@@ -20381,7 +20463,7 @@ ${stderr}`
|
|
|
20381
20463
|
contentBlocks.push({ type: "audio", data: data.toString("base64"), mimeType: att.mimeType });
|
|
20382
20464
|
} else {
|
|
20383
20465
|
if ((att.type === "image" || att.type === "audio") && !tooLarge) {
|
|
20384
|
-
|
|
20466
|
+
log33.debug(
|
|
20385
20467
|
{ type: att.type, capabilities: this.promptCapabilities ?? {} },
|
|
20386
20468
|
"Agent does not support %s content, falling back to file path",
|
|
20387
20469
|
att.type
|
|
@@ -21209,12 +21291,12 @@ var init_session_manager = __esm({
|
|
|
21209
21291
|
});
|
|
21210
21292
|
|
|
21211
21293
|
// src/core/sessions/session-bridge.ts
|
|
21212
|
-
var
|
|
21294
|
+
var log34, SessionBridge;
|
|
21213
21295
|
var init_session_bridge = __esm({
|
|
21214
21296
|
"src/core/sessions/session-bridge.ts"() {
|
|
21215
21297
|
"use strict";
|
|
21216
21298
|
init_log();
|
|
21217
|
-
|
|
21299
|
+
log34 = createChildLogger({ module: "session-bridge" });
|
|
21218
21300
|
SessionBridge = class {
|
|
21219
21301
|
constructor(session, adapter, deps) {
|
|
21220
21302
|
this.session = session;
|
|
@@ -21240,16 +21322,16 @@ var init_session_bridge = __esm({
|
|
|
21240
21322
|
if (!result) return;
|
|
21241
21323
|
this.tracer?.log("core", { step: "dispatch", sessionId, message: result.message });
|
|
21242
21324
|
this.adapter.sendMessage(sessionId, result.message).catch((err) => {
|
|
21243
|
-
|
|
21325
|
+
log34.error({ err, sessionId }, "Failed to send message to adapter");
|
|
21244
21326
|
});
|
|
21245
21327
|
} else {
|
|
21246
21328
|
this.tracer?.log("core", { step: "dispatch", sessionId, message });
|
|
21247
21329
|
this.adapter.sendMessage(sessionId, message).catch((err) => {
|
|
21248
|
-
|
|
21330
|
+
log34.error({ err, sessionId }, "Failed to send message to adapter");
|
|
21249
21331
|
});
|
|
21250
21332
|
}
|
|
21251
21333
|
} catch (err) {
|
|
21252
|
-
|
|
21334
|
+
log34.error({ err, sessionId }, "Error in sendMessage middleware");
|
|
21253
21335
|
}
|
|
21254
21336
|
}
|
|
21255
21337
|
connect() {
|
|
@@ -21304,20 +21386,20 @@ var init_session_bridge = __esm({
|
|
|
21304
21386
|
}, async (e) => e).catch(() => {
|
|
21305
21387
|
});
|
|
21306
21388
|
} catch (err) {
|
|
21307
|
-
|
|
21389
|
+
log34.error({ err, sessionId: this.session.id }, "Error handling agent event after middleware");
|
|
21308
21390
|
}
|
|
21309
21391
|
}).catch(() => {
|
|
21310
21392
|
try {
|
|
21311
21393
|
this.handleAgentEvent(event);
|
|
21312
21394
|
} catch (err) {
|
|
21313
|
-
|
|
21395
|
+
log34.error({ err, sessionId: this.session.id }, "Error handling agent event (middleware fallback)");
|
|
21314
21396
|
}
|
|
21315
21397
|
});
|
|
21316
21398
|
} else {
|
|
21317
21399
|
try {
|
|
21318
21400
|
this.handleAgentEvent(event);
|
|
21319
21401
|
} catch (err) {
|
|
21320
|
-
|
|
21402
|
+
log34.error({ err, sessionId: this.session.id }, "Error handling agent event");
|
|
21321
21403
|
}
|
|
21322
21404
|
}
|
|
21323
21405
|
};
|
|
@@ -21383,7 +21465,7 @@ var init_session_bridge = __esm({
|
|
|
21383
21465
|
text: "",
|
|
21384
21466
|
attachment: att
|
|
21385
21467
|
});
|
|
21386
|
-
}).catch((err) =>
|
|
21468
|
+
}).catch((err) => log34.error({ err }, "Failed to save agent image"));
|
|
21387
21469
|
}
|
|
21388
21470
|
break;
|
|
21389
21471
|
}
|
|
@@ -21400,12 +21482,12 @@ var init_session_bridge = __esm({
|
|
|
21400
21482
|
text: "",
|
|
21401
21483
|
attachment: att
|
|
21402
21484
|
});
|
|
21403
|
-
}).catch((err) =>
|
|
21485
|
+
}).catch((err) => log34.error({ err }, "Failed to save agent audio"));
|
|
21404
21486
|
}
|
|
21405
21487
|
break;
|
|
21406
21488
|
}
|
|
21407
21489
|
case "commands_update":
|
|
21408
|
-
|
|
21490
|
+
log34.debug({ commands: event.commands }, "Commands available");
|
|
21409
21491
|
this.adapter.sendSkillCommands?.(this.session.id, event.commands);
|
|
21410
21492
|
break;
|
|
21411
21493
|
case "system_message":
|
|
@@ -21485,7 +21567,7 @@ var init_session_bridge = __esm({
|
|
|
21485
21567
|
if (permReq.description.toLowerCase().includes("openacp")) {
|
|
21486
21568
|
const allowOption = permReq.options.find((o) => o.isAllow);
|
|
21487
21569
|
if (allowOption) {
|
|
21488
|
-
|
|
21570
|
+
log34.info(
|
|
21489
21571
|
{ sessionId: this.session.id, requestId: permReq.id },
|
|
21490
21572
|
"Auto-approving openacp command"
|
|
21491
21573
|
);
|
|
@@ -21505,7 +21587,7 @@ var init_session_bridge = __esm({
|
|
|
21505
21587
|
if (this.session.dangerousMode) {
|
|
21506
21588
|
const allowOption = permReq.options.find((o) => o.isAllow);
|
|
21507
21589
|
if (allowOption) {
|
|
21508
|
-
|
|
21590
|
+
log34.info(
|
|
21509
21591
|
{ sessionId: this.session.id, requestId: permReq.id, optionId: allowOption.id },
|
|
21510
21592
|
"Dangerous mode: auto-approving permission"
|
|
21511
21593
|
);
|
|
@@ -21553,13 +21635,17 @@ var init_session_bridge = __esm({
|
|
|
21553
21635
|
}
|
|
21554
21636
|
};
|
|
21555
21637
|
this.session.on("status_change", this.statusChangeHandler);
|
|
21556
|
-
this.namedHandler = (name) => {
|
|
21557
|
-
this.deps.sessionManager.
|
|
21638
|
+
this.namedHandler = async (name) => {
|
|
21639
|
+
const record = this.deps.sessionManager.getSessionRecord(this.session.id);
|
|
21640
|
+
const alreadyNamed = !!record?.name;
|
|
21641
|
+
await this.deps.sessionManager.patchRecord(this.session.id, { name });
|
|
21558
21642
|
this.deps.eventBus?.emit("session:updated", {
|
|
21559
21643
|
sessionId: this.session.id,
|
|
21560
21644
|
name
|
|
21561
21645
|
});
|
|
21562
|
-
|
|
21646
|
+
if (!alreadyNamed) {
|
|
21647
|
+
await this.adapter.renameSessionThread(this.session.id, name);
|
|
21648
|
+
}
|
|
21563
21649
|
};
|
|
21564
21650
|
this.session.on("named", this.namedHandler);
|
|
21565
21651
|
this.promptCountHandler = (count) => {
|
|
@@ -21717,13 +21803,13 @@ function computeLineDiff(oldStr, newStr) {
|
|
|
21717
21803
|
removed: Math.max(0, oldLines.length - prefixLen - suffixLen)
|
|
21718
21804
|
};
|
|
21719
21805
|
}
|
|
21720
|
-
var
|
|
21806
|
+
var log35, MessageTransformer;
|
|
21721
21807
|
var init_message_transformer = __esm({
|
|
21722
21808
|
"src/core/message-transformer.ts"() {
|
|
21723
21809
|
"use strict";
|
|
21724
21810
|
init_extract_file_info();
|
|
21725
21811
|
init_log();
|
|
21726
|
-
|
|
21812
|
+
log35 = createChildLogger({ module: "message-transformer" });
|
|
21727
21813
|
MessageTransformer = class {
|
|
21728
21814
|
tunnelService;
|
|
21729
21815
|
/** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */
|
|
@@ -21889,14 +21975,14 @@ var init_message_transformer = __esm({
|
|
|
21889
21975
|
}
|
|
21890
21976
|
}
|
|
21891
21977
|
if (!this.tunnelService || !sessionContext) {
|
|
21892
|
-
|
|
21978
|
+
log35.debug(
|
|
21893
21979
|
{ hasTunnel: !!this.tunnelService, hasCtx: !!sessionContext, kind },
|
|
21894
21980
|
"enrichWithViewerLinks: skipping (no tunnel or session context)"
|
|
21895
21981
|
);
|
|
21896
21982
|
return;
|
|
21897
21983
|
}
|
|
21898
21984
|
const name = "name" in event ? event.name || "" : "";
|
|
21899
|
-
|
|
21985
|
+
log35.debug(
|
|
21900
21986
|
{ name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },
|
|
21901
21987
|
"enrichWithViewerLinks: inspecting event"
|
|
21902
21988
|
);
|
|
@@ -21908,7 +21994,7 @@ var init_message_transformer = __esm({
|
|
|
21908
21994
|
event.meta
|
|
21909
21995
|
);
|
|
21910
21996
|
if (!fileInfo) {
|
|
21911
|
-
|
|
21997
|
+
log35.debug(
|
|
21912
21998
|
{ name, kind, hasContent: !!event.content, hasRawInput: !!event.rawInput, hasMeta: !!event.meta },
|
|
21913
21999
|
"enrichWithViewerLinks: extractFileInfo returned null"
|
|
21914
22000
|
);
|
|
@@ -21916,10 +22002,10 @@ var init_message_transformer = __esm({
|
|
|
21916
22002
|
}
|
|
21917
22003
|
const publicUrl = this.tunnelService.getPublicUrl();
|
|
21918
22004
|
if (publicUrl.startsWith("http://localhost") || publicUrl.startsWith("http://127.0.0.1")) {
|
|
21919
|
-
|
|
22005
|
+
log35.debug({ kind, filePath: fileInfo.filePath }, "enrichWithViewerLinks: skipping (no public tunnel URL)");
|
|
21920
22006
|
return;
|
|
21921
22007
|
}
|
|
21922
|
-
|
|
22008
|
+
log35.info(
|
|
21923
22009
|
{
|
|
21924
22010
|
name,
|
|
21925
22011
|
kind,
|
|
@@ -21965,12 +22051,12 @@ var init_message_transformer = __esm({
|
|
|
21965
22051
|
// src/core/sessions/session-store.ts
|
|
21966
22052
|
import fs45 from "fs";
|
|
21967
22053
|
import path51 from "path";
|
|
21968
|
-
var
|
|
22054
|
+
var log36, DEBOUNCE_MS2, JsonFileSessionStore;
|
|
21969
22055
|
var init_session_store = __esm({
|
|
21970
22056
|
"src/core/sessions/session-store.ts"() {
|
|
21971
22057
|
"use strict";
|
|
21972
22058
|
init_log();
|
|
21973
|
-
|
|
22059
|
+
log36 = createChildLogger({ module: "session-store" });
|
|
21974
22060
|
DEBOUNCE_MS2 = 2e3;
|
|
21975
22061
|
JsonFileSessionStore = class {
|
|
21976
22062
|
records = /* @__PURE__ */ new Map();
|
|
@@ -22058,7 +22144,7 @@ var init_session_store = __esm({
|
|
|
22058
22144
|
fs45.readFileSync(this.filePath, "utf-8")
|
|
22059
22145
|
);
|
|
22060
22146
|
if (raw.version !== 1) {
|
|
22061
|
-
|
|
22147
|
+
log36.warn(
|
|
22062
22148
|
{ version: raw.version },
|
|
22063
22149
|
"Unknown session store version, skipping load"
|
|
22064
22150
|
);
|
|
@@ -22067,9 +22153,9 @@ var init_session_store = __esm({
|
|
|
22067
22153
|
for (const [id, record] of Object.entries(raw.sessions)) {
|
|
22068
22154
|
this.records.set(id, record);
|
|
22069
22155
|
}
|
|
22070
|
-
|
|
22156
|
+
log36.debug({ count: this.records.size }, "Loaded session records");
|
|
22071
22157
|
} catch (err) {
|
|
22072
|
-
|
|
22158
|
+
log36.error({ err }, "Failed to load session store, backing up corrupt file");
|
|
22073
22159
|
try {
|
|
22074
22160
|
fs45.renameSync(this.filePath, `${this.filePath}.bak`);
|
|
22075
22161
|
} catch {
|
|
@@ -22089,7 +22175,7 @@ var init_session_store = __esm({
|
|
|
22089
22175
|
}
|
|
22090
22176
|
}
|
|
22091
22177
|
if (removed > 0) {
|
|
22092
|
-
|
|
22178
|
+
log36.info({ removed }, "Cleaned up expired session records");
|
|
22093
22179
|
this.scheduleDiskWrite();
|
|
22094
22180
|
}
|
|
22095
22181
|
}
|
|
@@ -22104,19 +22190,20 @@ var init_session_store = __esm({
|
|
|
22104
22190
|
});
|
|
22105
22191
|
|
|
22106
22192
|
// src/core/sessions/session-factory.ts
|
|
22107
|
-
var
|
|
22193
|
+
var log37, SessionFactory;
|
|
22108
22194
|
var init_session_factory = __esm({
|
|
22109
22195
|
"src/core/sessions/session-factory.ts"() {
|
|
22110
22196
|
"use strict";
|
|
22111
22197
|
init_session2();
|
|
22112
22198
|
init_log();
|
|
22113
|
-
|
|
22199
|
+
log37 = createChildLogger({ module: "session-factory" });
|
|
22114
22200
|
SessionFactory = class {
|
|
22115
|
-
constructor(agentManager, sessionManager, speechServiceAccessor, eventBus) {
|
|
22201
|
+
constructor(agentManager, sessionManager, speechServiceAccessor, eventBus, instanceRoot) {
|
|
22116
22202
|
this.agentManager = agentManager;
|
|
22117
22203
|
this.sessionManager = sessionManager;
|
|
22118
22204
|
this.speechServiceAccessor = speechServiceAccessor;
|
|
22119
22205
|
this.eventBus = eventBus;
|
|
22206
|
+
this.instanceRoot = instanceRoot;
|
|
22120
22207
|
}
|
|
22121
22208
|
middlewareChain;
|
|
22122
22209
|
get speechService() {
|
|
@@ -22143,14 +22230,73 @@ var init_session_factory = __esm({
|
|
|
22143
22230
|
channelId: result.channelId
|
|
22144
22231
|
};
|
|
22145
22232
|
}
|
|
22146
|
-
|
|
22147
|
-
|
|
22148
|
-
createParams.
|
|
22149
|
-
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
|
|
22153
|
-
|
|
22233
|
+
let agentInstance;
|
|
22234
|
+
try {
|
|
22235
|
+
agentInstance = createParams.resumeAgentSessionId ? await this.agentManager.resume(
|
|
22236
|
+
createParams.agentName,
|
|
22237
|
+
createParams.workingDirectory,
|
|
22238
|
+
createParams.resumeAgentSessionId
|
|
22239
|
+
) : await this.agentManager.spawn(
|
|
22240
|
+
createParams.agentName,
|
|
22241
|
+
createParams.workingDirectory
|
|
22242
|
+
);
|
|
22243
|
+
} catch (err) {
|
|
22244
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
22245
|
+
const guidanceLines = [
|
|
22246
|
+
`\u274C Failed to start agent "${createParams.agentName}": ${message}`,
|
|
22247
|
+
"",
|
|
22248
|
+
"Run the agent CLI once in a terminal for this OpenACP instance to complete login or setup."
|
|
22249
|
+
];
|
|
22250
|
+
if (this.instanceRoot) {
|
|
22251
|
+
guidanceLines.push(
|
|
22252
|
+
"",
|
|
22253
|
+
"Copy and run this command in your terminal:",
|
|
22254
|
+
` cd "${this.instanceRoot}" && openacp agents run ${createParams.agentName}`
|
|
22255
|
+
);
|
|
22256
|
+
} else {
|
|
22257
|
+
guidanceLines.push(
|
|
22258
|
+
"",
|
|
22259
|
+
"Copy and run this command in your terminal (same project where you started OpenACP):",
|
|
22260
|
+
` openacp agents run ${createParams.agentName}`
|
|
22261
|
+
);
|
|
22262
|
+
}
|
|
22263
|
+
guidanceLines.push(
|
|
22264
|
+
"",
|
|
22265
|
+
"After setup completes, retry creating the session here."
|
|
22266
|
+
);
|
|
22267
|
+
const guidance = {
|
|
22268
|
+
type: "system_message",
|
|
22269
|
+
message: guidanceLines.join("\n")
|
|
22270
|
+
};
|
|
22271
|
+
const failedSession = new Session({
|
|
22272
|
+
id: createParams.existingSessionId,
|
|
22273
|
+
channelId: createParams.channelId,
|
|
22274
|
+
agentName: createParams.agentName,
|
|
22275
|
+
workingDirectory: createParams.workingDirectory,
|
|
22276
|
+
// Dummy agent instance — will never be prompted
|
|
22277
|
+
agentInstance: {
|
|
22278
|
+
sessionId: "",
|
|
22279
|
+
prompt: async () => {
|
|
22280
|
+
},
|
|
22281
|
+
cancel: async () => {
|
|
22282
|
+
},
|
|
22283
|
+
destroy: async () => {
|
|
22284
|
+
},
|
|
22285
|
+
on: () => {
|
|
22286
|
+
},
|
|
22287
|
+
off: () => {
|
|
22288
|
+
}
|
|
22289
|
+
},
|
|
22290
|
+
speechService: this.speechService
|
|
22291
|
+
});
|
|
22292
|
+
this.sessionManager.registerSession(failedSession);
|
|
22293
|
+
failedSession.emit("agent_event", guidance);
|
|
22294
|
+
this.eventBus.emit("agent:event", {
|
|
22295
|
+
sessionId: failedSession.id,
|
|
22296
|
+
event: guidance
|
|
22297
|
+
});
|
|
22298
|
+
throw err;
|
|
22299
|
+
}
|
|
22154
22300
|
agentInstance.middlewareChain = this.middlewareChain;
|
|
22155
22301
|
const session = new Session({
|
|
22156
22302
|
id: createParams.existingSessionId,
|
|
@@ -22583,7 +22729,7 @@ function createPluginContext(opts) {
|
|
|
22583
22729
|
}
|
|
22584
22730
|
};
|
|
22585
22731
|
const baseLog = opts.log ?? noopLog;
|
|
22586
|
-
const
|
|
22732
|
+
const log40 = typeof baseLog.child === "function" ? baseLog.child({ plugin: pluginName }) : baseLog;
|
|
22587
22733
|
const storageImpl = new PluginStorageImpl(storagePath);
|
|
22588
22734
|
const storage = {
|
|
22589
22735
|
async get(key) {
|
|
@@ -22610,7 +22756,7 @@ function createPluginContext(opts) {
|
|
|
22610
22756
|
const ctx = {
|
|
22611
22757
|
pluginName,
|
|
22612
22758
|
pluginConfig,
|
|
22613
|
-
log:
|
|
22759
|
+
log: log40,
|
|
22614
22760
|
storage,
|
|
22615
22761
|
on(event, handler) {
|
|
22616
22762
|
requirePermission(permissions, "events:read", "on()");
|
|
@@ -22645,7 +22791,7 @@ function createPluginContext(opts) {
|
|
|
22645
22791
|
const registry = serviceRegistry.get("command-registry");
|
|
22646
22792
|
if (registry && typeof registry.register === "function") {
|
|
22647
22793
|
registry.register(def, pluginName);
|
|
22648
|
-
|
|
22794
|
+
log40.debug(`Command '/${def.name}' registered`);
|
|
22649
22795
|
}
|
|
22650
22796
|
},
|
|
22651
22797
|
async sendMessage(_sessionId, _content) {
|
|
@@ -22966,7 +23112,7 @@ var init_lifecycle_manager = __esm({
|
|
|
22966
23112
|
// src/core/core.ts
|
|
22967
23113
|
import path54 from "path";
|
|
22968
23114
|
import os26 from "os";
|
|
22969
|
-
var
|
|
23115
|
+
var log38, OpenACPCore;
|
|
22970
23116
|
var init_core = __esm({
|
|
22971
23117
|
"src/core/core.ts"() {
|
|
22972
23118
|
"use strict";
|
|
@@ -22984,7 +23130,7 @@ var init_core = __esm({
|
|
|
22984
23130
|
init_middleware_chain();
|
|
22985
23131
|
init_error_tracker();
|
|
22986
23132
|
init_log();
|
|
22987
|
-
|
|
23133
|
+
log38 = createChildLogger({ module: "core" });
|
|
22988
23134
|
OpenACPCore = class {
|
|
22989
23135
|
configManager;
|
|
22990
23136
|
agentCatalog;
|
|
@@ -23045,7 +23191,8 @@ var init_core = __esm({
|
|
|
23045
23191
|
this.agentManager,
|
|
23046
23192
|
this.sessionManager,
|
|
23047
23193
|
() => this.speechService,
|
|
23048
|
-
this.eventBus
|
|
23194
|
+
this.eventBus,
|
|
23195
|
+
ctx?.root
|
|
23049
23196
|
);
|
|
23050
23197
|
this.lifecycleManager = new LifecycleManager({
|
|
23051
23198
|
serviceRegistry: new ServiceRegistry(),
|
|
@@ -23066,7 +23213,7 @@ var init_core = __esm({
|
|
|
23066
23213
|
if (configPath === "logging.level" && typeof value === "string") {
|
|
23067
23214
|
const { setLogLevel: setLogLevel2 } = await Promise.resolve().then(() => (init_log(), log_exports));
|
|
23068
23215
|
setLogLevel2(value);
|
|
23069
|
-
|
|
23216
|
+
log38.info({ level: value }, "Log level changed at runtime");
|
|
23070
23217
|
}
|
|
23071
23218
|
if (configPath.startsWith("speech.")) {
|
|
23072
23219
|
const speechSvc = this.speechService;
|
|
@@ -23077,7 +23224,7 @@ var init_core = __esm({
|
|
|
23077
23224
|
tts: { provider: null, providers: {} }
|
|
23078
23225
|
};
|
|
23079
23226
|
speechSvc.refreshProviders(newSpeechConfig);
|
|
23080
|
-
|
|
23227
|
+
log38.info("Speech service config updated at runtime");
|
|
23081
23228
|
}
|
|
23082
23229
|
}
|
|
23083
23230
|
}
|
|
@@ -23095,10 +23242,21 @@ var init_core = __esm({
|
|
|
23095
23242
|
}
|
|
23096
23243
|
async start() {
|
|
23097
23244
|
this.agentCatalog.refreshRegistryIfStale().catch((err) => {
|
|
23098
|
-
|
|
23245
|
+
log38.warn({ err }, "Background registry refresh failed");
|
|
23099
23246
|
});
|
|
23100
|
-
|
|
23101
|
-
|
|
23247
|
+
const failures = [];
|
|
23248
|
+
for (const [name, adapter] of this.adapters.entries()) {
|
|
23249
|
+
try {
|
|
23250
|
+
await adapter.start();
|
|
23251
|
+
} catch (err) {
|
|
23252
|
+
log38.error({ err, adapter: name }, `Adapter "${name}" failed to start`);
|
|
23253
|
+
failures.push({ name, error: err });
|
|
23254
|
+
}
|
|
23255
|
+
}
|
|
23256
|
+
if (failures.length > 0 && failures.length === this.adapters.size) {
|
|
23257
|
+
throw new Error(
|
|
23258
|
+
`All adapters failed to start: ${failures.map((f) => f.name).join(", ")}`
|
|
23259
|
+
);
|
|
23102
23260
|
}
|
|
23103
23261
|
}
|
|
23104
23262
|
async stop() {
|
|
@@ -23129,20 +23287,9 @@ var init_core = __esm({
|
|
|
23129
23287
|
if (!adapter) return { ok: false, error: "Adapter not found for session" };
|
|
23130
23288
|
if (!adapter.archiveSessionTopic) return { ok: false, error: "Adapter does not support topic archiving" };
|
|
23131
23289
|
try {
|
|
23132
|
-
|
|
23133
|
-
|
|
23134
|
-
|
|
23135
|
-
const platform2 = {};
|
|
23136
|
-
if (session.channelId === "telegram") {
|
|
23137
|
-
platform2.topicId = Number(newThreadId);
|
|
23138
|
-
} else {
|
|
23139
|
-
platform2.threadId = newThreadId;
|
|
23140
|
-
}
|
|
23141
|
-
await this.sessionManager.patchRecord(sessionId, { platform: platform2 });
|
|
23142
|
-
} catch (patchErr) {
|
|
23143
|
-
log37.warn({ err: patchErr, sessionId }, "Failed to update session record after archive \u2014 session will work but may not survive restart");
|
|
23144
|
-
}
|
|
23145
|
-
return { ok: true, newThreadId };
|
|
23290
|
+
await adapter.archiveSessionTopic(session.id);
|
|
23291
|
+
await this.sessionManager.cancelSession(sessionId);
|
|
23292
|
+
return { ok: true };
|
|
23146
23293
|
} catch (err) {
|
|
23147
23294
|
session.archiving = false;
|
|
23148
23295
|
return { ok: false, error: err.message };
|
|
@@ -23150,7 +23297,7 @@ var init_core = __esm({
|
|
|
23150
23297
|
}
|
|
23151
23298
|
// --- Message Routing ---
|
|
23152
23299
|
async handleMessage(message) {
|
|
23153
|
-
|
|
23300
|
+
log38.debug(
|
|
23154
23301
|
{
|
|
23155
23302
|
channelId: message.channelId,
|
|
23156
23303
|
threadId: message.threadId,
|
|
@@ -23169,7 +23316,7 @@ var init_core = __esm({
|
|
|
23169
23316
|
}
|
|
23170
23317
|
const access2 = this.securityGuard.checkAccess(message);
|
|
23171
23318
|
if (!access2.allowed) {
|
|
23172
|
-
|
|
23319
|
+
log38.warn({ userId: message.userId, reason: access2.reason }, "Access denied");
|
|
23173
23320
|
if (access2.reason.includes("Session limit")) {
|
|
23174
23321
|
const adapter = this.adapters.get(message.channelId);
|
|
23175
23322
|
if (adapter) {
|
|
@@ -23189,7 +23336,7 @@ var init_core = __esm({
|
|
|
23189
23336
|
session = await this.lazyResume(message) ?? void 0;
|
|
23190
23337
|
}
|
|
23191
23338
|
if (!session) {
|
|
23192
|
-
|
|
23339
|
+
log38.warn(
|
|
23193
23340
|
{ channelId: message.channelId, threadId: message.threadId },
|
|
23194
23341
|
"No session found for thread (in-memory miss + lazy resume returned null)"
|
|
23195
23342
|
);
|
|
@@ -23249,7 +23396,7 @@ var init_core = __esm({
|
|
|
23249
23396
|
currentPromptCount: session.promptCount,
|
|
23250
23397
|
agentSwitchHistory: session.agentSwitchHistory
|
|
23251
23398
|
});
|
|
23252
|
-
|
|
23399
|
+
log38.info(
|
|
23253
23400
|
{ sessionId: session.id, agentName: params.agentName },
|
|
23254
23401
|
"Session created via pipeline"
|
|
23255
23402
|
);
|
|
@@ -23258,7 +23405,7 @@ var init_core = __esm({
|
|
|
23258
23405
|
async handleNewSession(channelId, agentName, workspacePath, options) {
|
|
23259
23406
|
const config = this.configManager.get();
|
|
23260
23407
|
const resolvedAgent = agentName || config.defaultAgent;
|
|
23261
|
-
|
|
23408
|
+
log38.info({ channelId, agentName: resolvedAgent }, "New session request");
|
|
23262
23409
|
const agentDef = this.agentCatalog.resolve(resolvedAgent);
|
|
23263
23410
|
const resolvedWorkspace = this.configManager.resolveWorkspace(
|
|
23264
23411
|
workspacePath || agentDef?.workingDirectory
|
|
@@ -23407,7 +23554,7 @@ var init_core = __esm({
|
|
|
23407
23554
|
params.contextOptions
|
|
23408
23555
|
);
|
|
23409
23556
|
} catch (err) {
|
|
23410
|
-
|
|
23557
|
+
log38.warn({ err }, "Context building failed, proceeding without context");
|
|
23411
23558
|
}
|
|
23412
23559
|
const session = await this.createSession({
|
|
23413
23560
|
channelId: params.channelId,
|
|
@@ -23449,6 +23596,21 @@ var init_core = __esm({
|
|
|
23449
23596
|
const caps = getAgentCapabilities(toAgent);
|
|
23450
23597
|
const canResume = !!(lastEntry && caps.supportsResume && lastEntry.promptCount === 0);
|
|
23451
23598
|
const resumed = canResume;
|
|
23599
|
+
const startEvent = {
|
|
23600
|
+
type: "system_message",
|
|
23601
|
+
message: `Switching from ${fromAgent} to ${toAgent}...`
|
|
23602
|
+
};
|
|
23603
|
+
session.emit("agent_event", startEvent);
|
|
23604
|
+
this.eventBus.emit("agent:event", {
|
|
23605
|
+
sessionId,
|
|
23606
|
+
event: startEvent
|
|
23607
|
+
});
|
|
23608
|
+
this.eventBus.emit("session:agentSwitch", {
|
|
23609
|
+
sessionId,
|
|
23610
|
+
fromAgent,
|
|
23611
|
+
toAgent,
|
|
23612
|
+
status: "starting"
|
|
23613
|
+
});
|
|
23452
23614
|
const bridge = this.bridges.get(sessionId);
|
|
23453
23615
|
if (bridge) bridge.disconnect();
|
|
23454
23616
|
const switchAdapter = this.adapters.get(session.channelId);
|
|
@@ -23483,7 +23645,40 @@ var init_core = __esm({
|
|
|
23483
23645
|
return instance;
|
|
23484
23646
|
}
|
|
23485
23647
|
});
|
|
23648
|
+
const successEvent = {
|
|
23649
|
+
type: "system_message",
|
|
23650
|
+
message: resumed ? `Switched to ${toAgent} (resumed previous session).` : `Switched to ${toAgent} (new session).`
|
|
23651
|
+
};
|
|
23652
|
+
session.emit("agent_event", successEvent);
|
|
23653
|
+
this.eventBus.emit("agent:event", {
|
|
23654
|
+
sessionId,
|
|
23655
|
+
event: successEvent
|
|
23656
|
+
});
|
|
23657
|
+
this.eventBus.emit("session:agentSwitch", {
|
|
23658
|
+
sessionId,
|
|
23659
|
+
fromAgent,
|
|
23660
|
+
toAgent,
|
|
23661
|
+
status: "succeeded",
|
|
23662
|
+
resumed
|
|
23663
|
+
});
|
|
23486
23664
|
} catch (err) {
|
|
23665
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
23666
|
+
const failedEvent = {
|
|
23667
|
+
type: "system_message",
|
|
23668
|
+
message: `Failed to switch to ${toAgent}: ${errorMessage}`
|
|
23669
|
+
};
|
|
23670
|
+
session.emit("agent_event", failedEvent);
|
|
23671
|
+
this.eventBus.emit("agent:event", {
|
|
23672
|
+
sessionId,
|
|
23673
|
+
event: failedEvent
|
|
23674
|
+
});
|
|
23675
|
+
this.eventBus.emit("session:agentSwitch", {
|
|
23676
|
+
sessionId,
|
|
23677
|
+
fromAgent,
|
|
23678
|
+
toAgent,
|
|
23679
|
+
status: "failed",
|
|
23680
|
+
error: errorMessage
|
|
23681
|
+
});
|
|
23487
23682
|
try {
|
|
23488
23683
|
let rollbackInstance;
|
|
23489
23684
|
try {
|
|
@@ -23501,10 +23696,10 @@ var init_core = __esm({
|
|
|
23501
23696
|
const rollbackBridge = this.createBridge(session, adapter);
|
|
23502
23697
|
rollbackBridge.connect();
|
|
23503
23698
|
}
|
|
23504
|
-
|
|
23699
|
+
log38.warn({ sessionId, fromAgent, toAgent, err }, "Agent switch failed, rolled back to previous agent");
|
|
23505
23700
|
} catch (rollbackErr) {
|
|
23506
23701
|
session.fail(`Switch failed and rollback failed: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`);
|
|
23507
|
-
|
|
23702
|
+
log38.error({ sessionId, fromAgent, toAgent, err, rollbackErr }, "Agent switch failed and rollback also failed");
|
|
23508
23703
|
}
|
|
23509
23704
|
throw err;
|
|
23510
23705
|
}
|
|
@@ -23552,14 +23747,14 @@ var init_core = __esm({
|
|
|
23552
23747
|
(p2) => String(p2.topicId) === message.threadId
|
|
23553
23748
|
);
|
|
23554
23749
|
if (!record) {
|
|
23555
|
-
|
|
23750
|
+
log38.debug(
|
|
23556
23751
|
{ threadId: message.threadId, channelId: message.channelId },
|
|
23557
23752
|
"No session record found for thread"
|
|
23558
23753
|
);
|
|
23559
23754
|
return null;
|
|
23560
23755
|
}
|
|
23561
23756
|
if (record.status === "error" || record.status === "cancelled") {
|
|
23562
|
-
|
|
23757
|
+
log38.debug(
|
|
23563
23758
|
{
|
|
23564
23759
|
threadId: message.threadId,
|
|
23565
23760
|
sessionId: record.sessionId,
|
|
@@ -23569,7 +23764,7 @@ var init_core = __esm({
|
|
|
23569
23764
|
);
|
|
23570
23765
|
return null;
|
|
23571
23766
|
}
|
|
23572
|
-
|
|
23767
|
+
log38.info(
|
|
23573
23768
|
{
|
|
23574
23769
|
threadId: message.threadId,
|
|
23575
23770
|
sessionId: record.sessionId,
|
|
@@ -23593,13 +23788,13 @@ var init_core = __esm({
|
|
|
23593
23788
|
if (record.firstAgent) session.firstAgent = record.firstAgent;
|
|
23594
23789
|
if (record.agentSwitchHistory) session.agentSwitchHistory = record.agentSwitchHistory;
|
|
23595
23790
|
if (record.currentPromptCount != null) session.promptCount = record.currentPromptCount;
|
|
23596
|
-
|
|
23791
|
+
log38.info(
|
|
23597
23792
|
{ sessionId: session.id, threadId: message.threadId },
|
|
23598
23793
|
"Lazy resume successful"
|
|
23599
23794
|
);
|
|
23600
23795
|
return session;
|
|
23601
23796
|
} catch (err) {
|
|
23602
|
-
|
|
23797
|
+
log38.error({ err, record }, "Lazy resume failed");
|
|
23603
23798
|
const adapter = this.adapters.get(message.channelId);
|
|
23604
23799
|
if (adapter) {
|
|
23605
23800
|
try {
|
|
@@ -24094,14 +24289,14 @@ async function runPostUpgradeChecks(config) {
|
|
|
24094
24289
|
const { ensureCloudflared: ensureCloudflared2 } = await Promise.resolve().then(() => (init_install_cloudflared(), install_cloudflared_exports));
|
|
24095
24290
|
await ensureCloudflared2();
|
|
24096
24291
|
} catch (err) {
|
|
24097
|
-
|
|
24292
|
+
log39.warn(
|
|
24098
24293
|
{ err: err.message },
|
|
24099
24294
|
"Could not install cloudflared. Tunnel may not work."
|
|
24100
24295
|
);
|
|
24101
24296
|
}
|
|
24102
24297
|
} else {
|
|
24103
24298
|
if (!commandExists(config.tunnel.provider)) {
|
|
24104
|
-
|
|
24299
|
+
log39.warn(
|
|
24105
24300
|
`Tunnel provider "${config.tunnel.provider}" is not installed. Install it or switch to cloudflare (free, auto-installed).`
|
|
24106
24301
|
);
|
|
24107
24302
|
}
|
|
@@ -24113,7 +24308,7 @@ async function runPostUpgradeChecks(config) {
|
|
|
24113
24308
|
if (integration) {
|
|
24114
24309
|
const allInstalled = integration.items.every((item) => item.isInstalled());
|
|
24115
24310
|
if (!allInstalled) {
|
|
24116
|
-
|
|
24311
|
+
log39.info(
|
|
24117
24312
|
'Claude CLI integration not installed. Run "openacp integrate claude" for session transfer + tunnel skill.'
|
|
24118
24313
|
);
|
|
24119
24314
|
}
|
|
@@ -24123,7 +24318,7 @@ async function runPostUpgradeChecks(config) {
|
|
|
24123
24318
|
const { ensureJq: ensureJq2 } = await Promise.resolve().then(() => (init_install_jq(), install_jq_exports));
|
|
24124
24319
|
await ensureJq2();
|
|
24125
24320
|
} catch (err) {
|
|
24126
|
-
|
|
24321
|
+
log39.warn(
|
|
24127
24322
|
{ err: err.message },
|
|
24128
24323
|
"Could not install jq. Handoff hooks may not work."
|
|
24129
24324
|
);
|
|
@@ -24133,7 +24328,7 @@ async function runPostUpgradeChecks(config) {
|
|
|
24133
24328
|
} catch {
|
|
24134
24329
|
}
|
|
24135
24330
|
if (!commandExists("unzip")) {
|
|
24136
|
-
|
|
24331
|
+
log39.warn(
|
|
24137
24332
|
"unzip is not installed. Some agent installations (binary distribution) may fail. Install: brew install unzip (macOS) or apt install unzip (Linux)"
|
|
24138
24333
|
);
|
|
24139
24334
|
}
|
|
@@ -24146,20 +24341,20 @@ async function runPostUpgradeChecks(config) {
|
|
|
24146
24341
|
(a) => a.distribution === "uvx"
|
|
24147
24342
|
);
|
|
24148
24343
|
if (hasUvxAgent && !commandExists("uvx")) {
|
|
24149
|
-
|
|
24344
|
+
log39.warn(
|
|
24150
24345
|
"uvx is not installed but you have Python-based agents. Install: pip install uv"
|
|
24151
24346
|
);
|
|
24152
24347
|
}
|
|
24153
24348
|
} catch {
|
|
24154
24349
|
}
|
|
24155
24350
|
}
|
|
24156
|
-
var
|
|
24351
|
+
var log39;
|
|
24157
24352
|
var init_post_upgrade = __esm({
|
|
24158
24353
|
"src/cli/post-upgrade.ts"() {
|
|
24159
24354
|
"use strict";
|
|
24160
24355
|
init_log();
|
|
24161
24356
|
init_agent_dependencies();
|
|
24162
|
-
|
|
24357
|
+
log39 = createChildLogger({ module: "post-upgrade" });
|
|
24163
24358
|
}
|
|
24164
24359
|
});
|
|
24165
24360
|
|
|
@@ -26323,7 +26518,7 @@ a "Handoff" slash command to Claude Code.
|
|
|
26323
26518
|
if (uninstall) {
|
|
26324
26519
|
console.log(`Removing ${agent}/${item.id}...`);
|
|
26325
26520
|
const result = await item.uninstall();
|
|
26326
|
-
for (const
|
|
26521
|
+
for (const log40 of result.logs) console.log(` ${log40}`);
|
|
26327
26522
|
if (result.success) {
|
|
26328
26523
|
console.log(` ${item.name} removed.`);
|
|
26329
26524
|
} else {
|
|
@@ -26333,7 +26528,7 @@ a "Handoff" slash command to Claude Code.
|
|
|
26333
26528
|
} else {
|
|
26334
26529
|
console.log(`Installing ${agent}/${item.id}...`);
|
|
26335
26530
|
const result = await item.install();
|
|
26336
|
-
for (const
|
|
26531
|
+
for (const log40 of result.logs) console.log(` ${log40}`);
|
|
26337
26532
|
if (result.success) {
|
|
26338
26533
|
console.log(` ${item.name} installed.`);
|
|
26339
26534
|
} else {
|